diff --git a/.env.master.example b/.env.master.example index 1c171541..75b53abf 100644 --- a/.env.master.example +++ b/.env.master.example @@ -113,7 +113,21 @@ PRIVATE_KEY= DEPLOYER_ADDRESS= RPC_URL_138= RPC_URL_138_PUBLIC= +# Infura (recommended): one dashboard project fills JSON-RPC for Ethereum + major L2s. +# Set INFURA_PROJECT_ID (or INFURA_API_KEY) and leave per-chain URLs empty to auto-fill in scripts/lib/load-project-env.sh, +# or set explicit URLs: https://mainnet.infura.io/v3/, https://polygon-mainnet.infura.io/v3/, … +INFURA_PROJECT_ID= +INFURA_API_KEY= ETHEREUM_MAINNET_RPC= +POLYGON_MAINNET_RPC= +ARBITRUM_MAINNET_RPC= +OPTIMISM_MAINNET_RPC= +BASE_MAINNET_RPC= +AVALANCHE_MAINNET_RPC= +# BSC / Gnosis / Celo (Infura-supported networks) +BSC_RPC_URL= +GNOSIS_MAINNET_RPC= +CELO_MAINNET_RPC= # Clear scripts/verify/check-external-dependencies.sh — use real service URLs when split; example interim health target: # DBIS_CORE_URL=https://dbis-api.d-bis.org/health DBIS_CORE_URL= diff --git a/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md b/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md index 6196b57a..810a1982 100644 --- a/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md +++ b/docs/03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md @@ -11,6 +11,8 @@ The final 2026-05-07 execution delivered exactly `5,000,000,000.000000 cWUSDC` t The current implementation proves the Engine X internal maintained rail, not public DEX market depth. A public `cWUSDC/USDC` peg still requires separate public liquidity and quote evidence. +**Public LP compliance boundary:** if the proof standard requires a public, Etherscan-indexable `cWUSDC/USDC` swap against official Mainnet `USDC`, Engine X evidence alone fails that standard. The proof must use an actual public LP surface, preserve before/after reserves plus swap transaction hashes, and avoid labeling virtual internal netting as public DEX volume. See `reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md` and `reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md`. + ## Proof Boundaries | Claim | Status | Evidence | @@ -22,6 +24,7 @@ The current implementation proves the Engine X internal maintained rail, not pub | On-chain proof anchor | Complete for 2026-05-07 execution | `VirtualProofAuditEvidence` emitted ISO/audit/peg hashes per recipient proof | | Legal/regulatory approval | Not claimed | Requires external legal, compliance, AML/KYC, sanctions, and auditor evidence | | Public DEX peg repair | Not claimed | Requires separate public `cWUSDC/USDC` liquidity, quote, and reserve evidence | +| Public indexed LP compliance | Blocked | Actual public LP is quote-starved; cross-protocol routes are allowed only when official-USDC unwind quotes repay all borrowed capital and fees | Official ISO references used by this package: @@ -35,11 +38,25 @@ Official ISO references used by this package: |---|---| | Virtual batch proof vault | `smom-dbis-138/contracts/flash/DBISEngineXVirtualBatchVault.sol` | | Virtual batch tests | `smom-dbis-138/test/flash/DBISEngineXVirtualBatchVault.t.sol` | +| Indexed public LP proof vault | `smom-dbis-138/contracts/flash/DBISEngineXIndexedLiquidityVault.sol` | +| Engine X ERC-3156 proof borrower | `smom-dbis-138/contracts/flash/DBISEngineXFlashProofBorrower.sol` | +| XAUt-backed USDC borrow vault | `smom-dbis-138/contracts/flash/DBISEngineXXautUsdcBorrowVault.sol` | +| XAUt-backed USDC borrow vault tests | `smom-dbis-138/test/flash/DBISEngineXXautUsdcBorrowVault.t.sol` | | Recipient deposit planner | `scripts/verify/plan-dbis-engine-x-recipient-deposits.py` | | Planner wrapper | `scripts/verify/plan-dbis-engine-x-recipient-deposits.sh` | | ISO/audit proof generator | `scripts/verify/generate-dbis-engine-x-iso20022-proofs.py` | | Proof package wrapper | `scripts/verify/build-dbis-engine-x-proof-package.sh` | | Canary preflight and canary hash generator | `scripts/verify/preflight-dbis-engine-x-virtual-batch-canary.py` | +| Engine X v2 deploy dry-run | `scripts/deployment/deploy-engine-x-v2-mainnet.sh` | +| XAUt-backed USDC borrow vault deploy dry-run | `scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh` | +| Indexed proof vault deploy dry-run | `scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh` | +| Public UniV3 indexed-liquidity migration dry-run | `scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh` | +| Legacy vault retirement dry-run | `scripts/deployment/retire-engine-x-legacy-vault.sh` | +| Engine X small loop proof dry-run | `scripts/deployment/run-engine-x-loop-proof.sh` | +| UniV3 public swap proof dry-run | `scripts/deployment/run-engine-x-univ3-public-swap-proof.sh` | +| Indexed proof recorder dry-run | `scripts/deployment/record-engine-x-indexed-liquidity-proof.sh` | +| Public indexed readiness report | `scripts/verify/check-engine-x-public-indexed-readiness.sh` | +| Audit manifest builder | `scripts/verify/build-engine-x-audit-manifest.sh` | | Main status report | `reports/status/dbis-engine-x-5b-cwusdc-ladder-recalc-20260506.md` | | Final execution report | `reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md` | @@ -136,6 +153,95 @@ Engine X proof execution must satisfy all of these conditions: This proves the maintained Engine X internal accounting rail. It does not prove public market price discovery or public DEX volume. +## Public LP And Flash-Loan Boundary + +Cross-protocol routing can be used for a public proof, but the roles must stay explicit: + +- UniV2 `cWUSDC/USDC` is the public indexed proof surface. +- UniV3 `cWUSDC/USDC` can also be used as the public indexed proof surface once created and funded with actual `cWUSDC` plus official Mainnet `USDC`. +- Aave USDC flash liquidity is same-block working capital, not permanent LP funding. +- The upgraded Engine X vault can provide ERC-3156 flash loans from its accounted USDC lender bucket only; it cannot lend pool USDC and it keeps flash fees in lender accounting. +- DODO or cW mesh pools may be unwind legs only when live min-out quotes repay Aave principal plus premium in official USDC. +- XAUt cannot currently expand Aave USDC debt capacity because live Aave XAUt LTV is `0`. + +With current balances, the only honest public LP path is a tiny wallet-funded UniV2 canary. Larger policy-floor or listing-quality depth still needs external official Mainnet USDC. + +## XAUt-Backed USDC Borrow Vault + +Engine X now has a dedicated repo-side borrowing component for the banking model where `XAUt` represents XAU collateral and `USDC` represents USD debt: + +```bash +pnpm engine-x:borrow-vault-deploy +``` + +The command is dry-run by default and deploys `DBISEngineXXautUsdcBorrowVault` only when `EXECUTE=1` is set. The vault: + +- accepts `XAUt` collateral deposits; +- lends only pre-funded official Mainnet `USDC`; +- enforces configurable LTV, liquidation threshold, minimum health factor, liquidation bonus, and global borrow cap; +- allows standard USDC repay, third-party USDC repay, and USDC repay with attached cWUSDC public-swap proof hashes; +- records cWUSDC-sourced repayment proof hashes without accepting cWUSDC as debt payment; +- exposes owner price/risk updates, pause/unpause, lender withdrawal, liquidation, and surplus-token rescue controls. + +The borrow vault does not solve quote-side capital by itself. It is a controlled accounting surface for XAU-backed USD borrowing once the vault is funded with real USDC. If the vault has no USDC lender bucket, no borrower can draw USDC regardless of XAUt collateral value. + +## Indexed Liquidity Upgrade Path + +The current Engine X virtual batch vault contains a tiny balanced proof pool: `85.763529 cWUSDC / 85.763529 USDC`, plus a `5 USDC` lender bucket. That balance can be migrated into an official Mainnet Uniswap v3 `cWUSDC/USDC` position for public, indexable 1:1 proof evidence: + +```bash +pnpm engine-x:indexed-lp-migration +``` + +The command is dry-run by default. It reads the live vault state, checks the official Uniswap v3 factory, simulates pool creation at exact 1:1 `sqrtPriceX96`, and prints the withdrawal, pool initialization, approval, and mint commands. It also detects whether the configured vault exposes the upgraded accounting-aware APIs. + +The repo-side next version of `DBISEngineXVirtualBatchVault` adds: + +- `withdrawPoolLiquidity(...)`, which decrements pool reserves and rejects withdrawals that break the maintained 1:1 pool invariant. +- `withdrawLenderUsdc(...)`, which decrements the lender bucket. +- ERC-3156 `maxFlashLoan`, `flashFee`, and `flashLoan`, limited to `lenderUsdcAvailable`. +- A safer generic `withdraw(...)`, now restricted to unaccounted surplus/rescue tokens. +- Emergency `pause()` / `unpause()` gates for proof execution, seeding, and flash loans. +- Flash borrower allowlist controls and optional max flash amount cap. + +The already-deployed 2026-05-07 vault does not retroactively gain those APIs. Redeploy or replace the vault before broadcasting an accounting-aware migration. + +The indexed LP upgrade creates public proof surface only. It does not meet the `2,500 / 2,500` policy floor or the preferred `10,000 / 10,000` target without additional official Mainnet USDC. + +**2026-05-07 live attempt:** the legacy vault balances were swept and used to create/fund the official UniV3 `cWUSDC/USDC` pool at `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3`. External swaps immediately moved the pool out of range; current active liquidity is `0` and deployer USDC is `0`. See `reports/status/engine-x-univ3-public-lp-migration-attempt-20260507.md`. + +## Engine X V2 Public Proof Sequence + +All commands are dry-run by default: + +```bash +pnpm engine-x:public-readiness +pnpm engine-x:v2-deploy +pnpm engine-x:borrow-vault-deploy +pnpm engine-x:indexed-lp-migration +pnpm engine-x:indexed-vault-deploy +pnpm engine-x:loop-proof +pnpm engine-x:univ3-swap-proof +pnpm engine-x:indexed-proof-record +pnpm engine-x:audit-manifest +``` + +Recommended order: + +1. Run `pnpm engine-x:public-readiness` and confirm the blockers. +2. Deploy the upgraded accounting-aware Engine X v2 vault with `pnpm engine-x:v2-deploy`. +3. Optionally deploy the XAUt-backed USDC borrow vault with `pnpm engine-x:borrow-vault-deploy`; fund it only with real official USDC. +4. Seed the upgraded vault with actual `cWUSDC/USDC` and a bounded USDC lender bucket. +5. Create/fund the official UniV3 `cWUSDC/USDC` public LP from the upgraded vault using `pnpm engine-x:indexed-lp-migration`. +6. Deploy `DBISEngineXIndexedLiquidityVault` with `pnpm engine-x:indexed-vault-deploy`. +7. For internal Engine X loop evidence, dry-run the exact `0.01 cWUSDC` loop proof with `pnpm engine-x:loop-proof`. +8. Run a tiny actual UniV3 public swap proof with `pnpm engine-x:univ3-swap-proof`. +9. Record the indexed proof with real swap/liquidity/proof hashes using `pnpm engine-x:indexed-proof-record`. +10. Build the ABI/proof audit manifest with `pnpm engine-x:audit-manifest`. +11. Retire the legacy vault only after v2 proof state is live and externally reviewed with `pnpm engine-x:legacy-retirement`. + +The indexed proof vault enforces the public proof gates: nonzero public swap hash, nonzero liquidity tx hash, tick drift limit, minimum liquidity, optional pool token-balance floors, optional max proof amount, pause, and operator allowlist. + ## Remaining External Gates - Attach external compliance evidence when required by jurisdiction or counterparty policy. diff --git a/docs/04-configuration/RPC_ENDPOINTS_MASTER.md b/docs/04-configuration/RPC_ENDPOINTS_MASTER.md index 601f1d79..d128074f 100644 --- a/docs/04-configuration/RPC_ENDPOINTS_MASTER.md +++ b/docs/04-configuration/RPC_ENDPOINTS_MASTER.md @@ -60,7 +60,7 @@ If `eth_maxPriorityFeePerGas` is missing, the first fix path is the public node | **RPC_URL_138** (Core) | `http://192.168.11.211:8545` | **Prefer IP:port for admin/deploy.** Fallback from off-LAN: `https://rpc-core.d-bis.org` | | **RPC_URL_138_PUBLIC** (Public) | `http://192.168.11.221:8545` or `https://rpc-http-pub.d-bis.org` | Single standard for Chain 138 public; VITE_RPC_URL_138 in frontend | | **RPC_URL_138_FIREBLOCKS** (Fireblocks) | `http://192.168.11.232:8545` or `https://rpc-fireblocks.d-bis.org` | Dedicated RPC for Fireblocks Web3 (VMID 2301); `WS_URL_138_FIREBLOCKS`: `wss://ws.rpc-fireblocks.d-bis.org` | -| **Ethereum Mainnet** | `https://eth.llamarpc.com` or Infura/Alchemy | `ETHEREUM_MAINNET_RPC` or `RPC_URL_MAINNET` in .env; CCIP relay uses both. Prefer Infura `https://mainnet.infura.io/v3/` to avoid 429. | +| **Ethereum Mainnet** | **Prefer Infura** `https://mainnet.infura.io/v3/` | Set `INFURA_PROJECT_ID` (or `INFURA_API_KEY`) in `.env` to auto-fill unset `ETHEREUM_MAINNET_RPC` / L2 RPCs via `scripts/lib/load-project-env.sh`, or set `ETHEREUM_MAINNET_RPC` / `RPC_URL_MAINNET` explicitly. Avoid unauthenticated public RPCs for high-volume `cast`/indexer jobs (429 / Unauthorized). | | **ALL Mainnet (651940)** | `https://mainnet-rpc.alltra.global` | alltra-lifi-settlement, token-lists | ### Obtaining RPC URLs (Infura, Etherscan API, public RPCs) diff --git a/docs/MASTER_INDEX.md b/docs/MASTER_INDEX.md index 1f938285..152b6ff6 100644 --- a/docs/MASTER_INDEX.md +++ b/docs/MASTER_INDEX.md @@ -37,7 +37,7 @@ | **Source to CEX production readiness** | [03-deployment/SOURCE_TO_CEX_PRODUCTION_READINESS.md](03-deployment/SOURCE_TO_CEX_PRODUCTION_READINESS.md) — repo-native readiness gate for immediate production | | **Immediate live production task list: source to CEX** | [03-deployment/IMMEDIATE_LIVE_PRODUCTION_TASK_LIST_SOURCE_TO_CEX.md](03-deployment/IMMEDIATE_LIVE_PRODUCTION_TASK_LIST_SOURCE_TO_CEX.md) — task list with remaining live blockers called out | | **ALL Mainnet protocol completion** | [03-deployment/ALL_MAINNET_PROTOCOL_COMPLETION_RUNBOOK.md](03-deployment/ALL_MAINNET_PROTOCOL_COMPLETION_RUNBOOK.md) — required inventory pools, funding/canary gates, auto-rebalancing path, and protocol enablement order for DODO, Uniswap, Balancer, Curve, Sushi, 1inch, and Aave | -| **DBIS Engine X recipient proof package** | [03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md](03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md) — executed 2026-05-07 5B-each `cWUSDC` recipient proof path, virtual-batch vault, ISO 20022-style evidence, audit envelope, and on-chain hash-anchor flow; final report: [../reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md](../reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md) | +| **DBIS Engine X recipient proof package** | [03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md](03-deployment/DBIS_ENGINE_X_RECIPIENT_PROOF_RUNBOOK.md) — executed 2026-05-07 5B-each `cWUSDC` recipient proof path, virtual-batch vault, ISO 20022-style evidence, audit envelope, and on-chain hash-anchor flow; final report: [../reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md](../reports/status/dbis-engine-x-final-recipient-proof-execution-20260507.md); public LP compliance gap/route plan: [../reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md](../reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md); indexed-liquidity upgrade plan: [../reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md](../reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md); public indexed readiness: [../reports/status/engine-x-public-indexed-readiness-latest.md](../reports/status/engine-x-public-indexed-readiness-latest.md) | | **External dependency blockers** | [03-deployment/EXTERNAL_DEPENDENCY_BLOCKERS.md](03-deployment/EXTERNAL_DEPENDENCY_BLOCKERS.md) — explicit list of items that cannot be closed by repo-only changes, with readiness checks and env knobs | | **Crypto.com OTC before vs after matrix** | [03-deployment/CRYPTO_COM_OTC_BEFORE_AFTER_OPERATOR_MATRIX.md](03-deployment/CRYPTO_COM_OTC_BEFORE_AFTER_OPERATOR_MATRIX.md) — strict operator comparison of the current ecosystem versus the state after a real Crypto.com OTC sink is connected | | **Provider-facing source to CEX package** | [03-deployment/PROVIDER_FACING_PACKAGE_SOURCE_TO_CEX.md](03-deployment/PROVIDER_FACING_PACKAGE_SOURCE_TO_CEX.md) — strict provider-facing package covering expectations, flow presentation, questions, and a first 30-day ramp plan | diff --git a/docs/runbooks/MAINNET_CWUSDC_USDC_REPEG_RUNBOOK.md b/docs/runbooks/MAINNET_CWUSDC_USDC_REPEG_RUNBOOK.md index 91f33ecf..4e935a19 100644 --- a/docs/runbooks/MAINNET_CWUSDC_USDC_REPEG_RUNBOOK.md +++ b/docs/runbooks/MAINNET_CWUSDC_USDC_REPEG_RUNBOOK.md @@ -10,7 +10,7 @@ - How much USDC is needed to fund one max-sized automated defense cycle - How far the public pair is below the current policy floor - Whether the current operator wallet can cover any of those gaps -- Exact commands to fund the manager, top up the defended PMM, and reseed the public pair once inventory exists +- Exact commands or guidance to fund the manager, top up the defended PMM, and repair the public pair once inventory exists ## Prereqs @@ -49,6 +49,8 @@ The wrapper writes: - `publicLane.policyFloorQuoteShortfallUnits` - `holderState` - `holderFundingChecks` +- `summary.publicIndexedLpComplianceStatus` +- `summary.publicPairRepairRequiresQuoteSideAction` - `blockers` - `operatorCommands` @@ -56,6 +58,8 @@ The wrapper writes: - The defended-pool `1:1` number is an inference from matched 6-decimal tokens and equal reserve targets. - DODO PMM price can still differ from reserve ratio because the PMM curve is not a constant-product pool. +- When the public pair is asymmetric, a plain `addLiquidity` call follows the bad reserve ratio; repair the quote side first, then add balanced liquidity. +- Aave flash liquidity can be used only as same-block working capital unless eligible collateral or credit delegation leaves a valid debt position. It cannot be counted as durable LP funding after the flash is repaid. - Re-run the preflight after every liquidity move. Treat the planner as a live calculator, not a one-time document. ## Operator sequence once funding exists @@ -66,8 +70,8 @@ Use `operatorCommands.fundManagerUsdc`. 2. Sell the planned USDC amount into the defended PMM. Use `operatorCommands.tradeDefendedPoolQuoteIn`. -3. Reseed the public pair to at least the current policy floor if public routing / discovery should be healthy again. -Use `operatorCommands.reseedPublicPair`. +3. Repair the public UniV2 pair if public routing / discovery should be healthy again. +Use `operatorCommands.publicPairRepairGuidance` when only the quote side is short or the reserve-implied price is distorted. Use `operatorCommands.reseedPublicPair` only when both sides need balanced seeding and the price gate is already acceptable. 4. Re-run both read-only checks. diff --git a/package.json b/package.json index fa8fbaec..bff279df 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,17 @@ "engine-x:recipient-plan": "bash scripts/verify/plan-dbis-engine-x-recipient-deposits.sh", "engine-x:iso20022-proofs": "bash scripts/verify/generate-dbis-engine-x-iso20022-proofs.sh", "engine-x:proof-package": "bash scripts/verify/build-dbis-engine-x-proof-package.sh", - "engine-x:canary-preflight": "bash scripts/verify/preflight-dbis-engine-x-virtual-batch-canary.sh" + "engine-x:canary-preflight": "bash scripts/verify/preflight-dbis-engine-x-virtual-batch-canary.sh", + "engine-x:v2-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-v2-mainnet.sh", + "engine-x:borrow-vault-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh", + "engine-x:indexed-vault-deploy": "EXECUTE=0 bash scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh", + "engine-x:indexed-lp-migration": "EXECUTE=0 bash scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh", + "engine-x:legacy-retirement": "EXECUTE=0 bash scripts/deployment/retire-engine-x-legacy-vault.sh", + "engine-x:loop-proof": "EXECUTE=0 bash scripts/deployment/run-engine-x-loop-proof.sh", + "engine-x:univ3-swap-proof": "EXECUTE=0 bash scripts/deployment/run-engine-x-univ3-public-swap-proof.sh", + "engine-x:indexed-proof-record": "EXECUTE=0 bash scripts/deployment/record-engine-x-indexed-liquidity-proof.sh", + "engine-x:public-readiness": "bash scripts/verify/check-engine-x-public-indexed-readiness.sh", + "engine-x:audit-manifest": "bash scripts/verify/build-engine-x-audit-manifest.sh" }, "keywords": [ "proxmox", diff --git a/reports/status/engine-x-loop-proof-20260507T184556Z.json b/reports/status/engine-x-loop-proof-20260507T184556Z.json new file mode 100644 index 00000000..47759a7f --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T184556Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": false, + "stamp": "20260507T184556Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 1, + "debtUsdcPerLoopRaw": "10029", + "debtUsdcPerLoop": "0.010029", + "exactOutputPerLoopRaw": "10000", + "exactOutputPerLoop": "0.01", + "proofId": "0x7c63f9a6a57ff835c5b06c2f08e51aedf9dfa202a21b1f494872834a7adb158a", + "isoHash": "0x1f585bf5c8e1998f1442cb92472dfe4338a87ae17305196b64c35568c2c7cece", + "auditHash": "0x2f076c1b92113a5e0357bd9b6c3b3020f4dd279962eee3d0af387c724cf3afb6", + "pegHash": "0xcfdb6a3053d368f4a652e8ef74eeb8aac9e431cad54346d3c59fb1078636c244", + "preview": { + "collateralXautRaw": "4", + "collateralXaut": "0.000004", + "cwusdcInPerLoopRaw": "10061", + "cwusdcInPerLoop": "0.010061", + "cwusdcOutPerLoopRaw": "10000", + "cwusdcOutPerLoop": "0.01", + "cwusdcLossPerLoopRaw": "61", + "cwusdcLossPerLoop": "0.000061", + "totalCwusdcInRaw": "10061", + "totalCwusdcIn": "0.010061", + "totalCwusdcOutRaw": "10000", + "totalCwusdcOut": "0.01", + "exactOutputTotalRaw": "10000", + "exactOutputTotal": "0.01", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "61", + "totalNeutralized": "0.000061" + }, + "balances": { + "cwusdcRaw": "56750385168300", + "cwusdc": "56750385.1683", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-20260507T184556Z.md b/reports/status/engine-x-loop-proof-20260507T184556Z.md new file mode 100644 index 00000000..2666f52f --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T184556Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `False` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `1` +- Exact cWUSDC output per loop: `0.01` +- Debt USDC per loop: `0.010029` +- cWUSDC in/out per loop: `0.010061 / 0.01` +- Neutralized per loop: `0.000061` +- Total exact output: `0.01` +- Proof ID: `0x7c63f9a6a57ff835c5b06c2f08e51aedf9dfa202a21b1f494872834a7adb158a` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-loop-proof-20260507T184617Z.json b/reports/status/engine-x-loop-proof-20260507T184617Z.json new file mode 100644 index 00000000..0c98b486 --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T184617Z.json @@ -0,0 +1,70 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": true, + "stamp": "20260507T184617Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 1, + "debtUsdcPerLoopRaw": "10029", + "debtUsdcPerLoop": "0.010029", + "exactOutputPerLoopRaw": "10000", + "exactOutputPerLoop": "0.01", + "proofId": "0x7c63f9a6a57ff835c5b06c2f08e51aedf9dfa202a21b1f494872834a7adb158a", + "isoHash": "0x1f585bf5c8e1998f1442cb92472dfe4338a87ae17305196b64c35568c2c7cece", + "auditHash": "0x2f076c1b92113a5e0357bd9b6c3b3020f4dd279962eee3d0af387c724cf3afb6", + "pegHash": "0xcfdb6a3053d368f4a652e8ef74eeb8aac9e431cad54346d3c59fb1078636c244", + "preview": { + "collateralXautRaw": "4", + "collateralXaut": "0.000004", + "cwusdcInPerLoopRaw": "10061", + "cwusdcInPerLoop": "0.010061", + "cwusdcOutPerLoopRaw": "10000", + "cwusdcOutPerLoop": "0.01", + "cwusdcLossPerLoopRaw": "61", + "cwusdcLossPerLoop": "0.000061", + "totalCwusdcInRaw": "10061", + "totalCwusdcIn": "0.010061", + "totalCwusdcOutRaw": "10000", + "totalCwusdcOut": "0.01", + "exactOutputTotalRaw": "10000", + "exactOutputTotal": "0.01", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "61", + "totalNeutralized": "0.000061" + }, + "balances": { + "cwusdcRaw": "56750385168300", + "cwusdc": "56750385.1683", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + }, + "transactions": { + "cwusdcApprove": "0x267338c09877188494dda5d9b7a129a541f1ef51c6300a1c5060c8e5401384e1", + "xautApprove": "0x45a2be645ea786117f58a1413aaf6284fa9e2e3fef8ac7e3ebf2182eb21166bb", + "runVirtualProofExactOutTo": "0xb6a69560a37e214060549dac02d1e0b31baa15ac822b4b4ed09105a4ecb713ad" + }, + "gas": { + "cwusdcApproveGasUsed": "45955", + "xautApproveGasUsed": "53548", + "runGasUsed": "142938", + "totalGasUsed": "242441", + "estimatedTotalCostEth": "0.000097009285243385" + }, + "postReadback": { + "proofUsed": true, + "cwusdcBalanceRaw": "56750385168300", + "xautBalanceRaw": "2974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "vaultTotalNeutralizedCwusdcRaw": "56750381619868", + "vaultTotalVirtualLoops": "1888406152", + "vaultTotalVirtualDebtUsdcRaw": "9442030750011029" + } +} diff --git a/reports/status/engine-x-loop-proof-20260507T184617Z.md b/reports/status/engine-x-loop-proof-20260507T184617Z.md new file mode 100644 index 00000000..e407ea92 --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T184617Z.md @@ -0,0 +1,23 @@ +# Engine X Loop Proof + +- Executed: `True` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `1` +- Exact cWUSDC output per loop: `0.01` +- Debt USDC per loop: `0.010029` +- cWUSDC in/out per loop: `0.010061 / 0.01` +- Neutralized per loop: `0.000061` +- Total exact output: `0.01` +- Proof ID: `0x7c63f9a6a57ff835c5b06c2f08e51aedf9dfa202a21b1f494872834a7adb158a` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` + +## Execution + +- cWUSDC approve tx: `0x267338c09877188494dda5d9b7a129a541f1ef51c6300a1c5060c8e5401384e1` +- XAUt approve tx: `0x45a2be645ea786117f58a1413aaf6284fa9e2e3fef8ac7e3ebf2182eb21166bb` +- Engine X loop tx: `0xb6a69560a37e214060549dac02d1e0b31baa15ac822b4b4ed09105a4ecb713ad` +- Total gas used: `242441` +- Approx ETH cost: `0.000097009285243385` +- Proof consumed on-chain: `true` + diff --git a/reports/status/engine-x-loop-proof-20260507T190452Z.json b/reports/status/engine-x-loop-proof-20260507T190452Z.json new file mode 100644 index 00000000..8adee11c --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T190452Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": true, + "stamp": "20260507T190452Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 5, + "debtUsdcPerLoopRaw": "10029", + "debtUsdcPerLoop": "0.010029", + "exactOutputPerLoopRaw": "10000", + "exactOutputPerLoop": "0.01", + "proofId": "0x12f9dc6680b05a6ed0d08db8f5f2e654a81ea797f6f5a3cc37363fd879ccd050", + "isoHash": "0x0e9c531dfa1b7f6037d1feac4db5819cb85416e2c223a9cd26c0a8291229798e", + "auditHash": "0x8d94861ca42d8640fb009dab13a322f1c4d5231f7399442338538ac2018a6505", + "pegHash": "0xccc48dd8fef23d8247bf8e9a435ce45267dc0eb2272e3a4e1a95ddcb19110d7a", + "preview": { + "collateralXautRaw": "4", + "collateralXaut": "0.000004", + "cwusdcInPerLoopRaw": "10061", + "cwusdcInPerLoop": "0.010061", + "cwusdcOutPerLoopRaw": "10000", + "cwusdcOutPerLoop": "0.01", + "cwusdcLossPerLoopRaw": "61", + "cwusdcLossPerLoop": "0.000061", + "totalCwusdcInRaw": "50305", + "totalCwusdcIn": "0.050305", + "totalCwusdcOutRaw": "50000", + "totalCwusdcOut": "0.05", + "exactOutputTotalRaw": "50000", + "exactOutputTotal": "0.05", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "305", + "totalNeutralized": "0.000305" + }, + "balances": { + "cwusdcRaw": "56772623723927", + "cwusdc": "56772623.723927", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-20260507T190452Z.md b/reports/status/engine-x-loop-proof-20260507T190452Z.md new file mode 100644 index 00000000..42105f52 --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T190452Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `True` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `5` +- Exact cWUSDC output per loop: `0.01` +- Debt USDC per loop: `0.010029` +- cWUSDC in/out per loop: `0.010061 / 0.01` +- Neutralized per loop: `0.000061` +- Total exact output: `0.05` +- Proof ID: `0x12f9dc6680b05a6ed0d08db8f5f2e654a81ea797f6f5a3cc37363fd879ccd050` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-loop-proof-20260507T190621Z.json b/reports/status/engine-x-loop-proof-20260507T190621Z.json new file mode 100644 index 00000000..0614b1d4 --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T190621Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": true, + "stamp": "20260507T190621Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 1, + "debtUsdcPerLoopRaw": "50121", + "debtUsdcPerLoop": "0.050121", + "exactOutputPerLoopRaw": "50000", + "exactOutputPerLoop": "0.05", + "proofId": "0xa0d3d62b8c59ad56b23f1455b450f90bc1bfc554113151d44825e2d17dbba0fd", + "isoHash": "0x1c32b8a507c8a9d807b908d6896b2092d33aef0981627c9e5c76ccacfc827e37", + "auditHash": "0x6ca4fa58e15f5439abd48feeb5f9d6739ee8e9b4a912497a3e8e64c40d7480c2", + "pegHash": "0xb61f7a450ba2ac2314f80cb504c9b14be593ffcbe82366364fda323d7241eb16", + "preview": { + "collateralXautRaw": "20", + "collateralXaut": "0.00002", + "cwusdcInPerLoopRaw": "50302", + "cwusdcInPerLoop": "0.050302", + "cwusdcOutPerLoopRaw": "50000", + "cwusdcOutPerLoop": "0.05", + "cwusdcLossPerLoopRaw": "302", + "cwusdcLossPerLoop": "0.000302", + "totalCwusdcInRaw": "50302", + "totalCwusdcIn": "0.050302", + "totalCwusdcOutRaw": "50000", + "totalCwusdcOut": "0.05", + "exactOutputTotalRaw": "50000", + "exactOutputTotal": "0.05", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "302", + "totalNeutralized": "0.000302" + }, + "balances": { + "cwusdcRaw": "56772623723927", + "cwusdc": "56772623.723927", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-20260507T190621Z.md b/reports/status/engine-x-loop-proof-20260507T190621Z.md new file mode 100644 index 00000000..6c205a70 --- /dev/null +++ b/reports/status/engine-x-loop-proof-20260507T190621Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `True` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `1` +- Exact cWUSDC output per loop: `0.05` +- Debt USDC per loop: `0.050121` +- cWUSDC in/out per loop: `0.050302 / 0.05` +- Neutralized per loop: `0.000302` +- Total exact output: `0.05` +- Proof ID: `0xa0d3d62b8c59ad56b23f1455b450f90bc1bfc554113151d44825e2d17dbba0fd` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-loop-proof-continuation-20260507.md b/reports/status/engine-x-loop-proof-continuation-20260507.md new file mode 100644 index 00000000..5fda7ac4 --- /dev/null +++ b/reports/status/engine-x-loop-proof-continuation-20260507.md @@ -0,0 +1,58 @@ +# Engine X Loop Proof Continuation - 2026-05-07 + +## Boundary + +These are Engine X internal loop proofs against the maintained virtual batch vault. They are not public `cWUSDC/USDC` LP peg evidence and do not repair the public UniV2 pool. + +## Vault and Signer + +| Field | Value | +|---|---| +| Vault | `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` | +| Signer / recipient | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| Chain | Ethereum Mainnet | + +## Executed Proofs + +| Proof | Loop shape | Exact output | cWUSDC input | Neutralized | Proof ID | Engine X tx | +|---|---:|---:|---:|---:|---|---| +| Five 0.01 loops | `5 x 0.01` | `0.05 cWUSDC` | `0.050305 cWUSDC` | `0.000305 cWUSDC` | `0x12f9dc6680b05a6ed0d08db8f5f2e654a81ea797f6f5a3cc37363fd879ccd050` | `0xcbf05070d901f3942f9fccabd23932ebf85626b5c3bb9bf58f9fac74b3a44b51` | +| Single 0.05 loop | `1 x 0.05` | `0.05 cWUSDC` | `0.050302 cWUSDC` | `0.000302 cWUSDC` | `0xa0d3d62b8c59ad56b23f1455b450f90bc1bfc554113151d44825e2d17dbba0fd` | `0x2216cda30fc2e32aa16c5926837c20ee4aad10bcc4a047ea4911ae226866bd13` | + +## Broadcast Hashes + +| Proof | Step | Tx hash | +|---|---|---| +| `5 x 0.01` | cWUSDC approve | `0x369736f2cbb86af9079f5a3d4d744e7724f1b057bff3ede19201c8948e365aef` | +| `5 x 0.01` | XAUt approve | `0xbc3467bc9136e875ca6bd6cc37f9e52ffdb68dd7be5907c9a124b346dd7ab3f4` | +| `5 x 0.01` | Engine X proof | `0xcbf05070d901f3942f9fccabd23932ebf85626b5c3bb9bf58f9fac74b3a44b51` | +| `1 x 0.05` | cWUSDC approve | `0xff61a1af904c284768c723f50cc4887c607b6c391bd6d37bfa7d708549da6b3e` | +| `1 x 0.05` | XAUt approve | `0xa38d3f00e20de49256fec8911f657a3afc85874bfa23ea3c5129860ce3a2100b` | +| `1 x 0.05` | Engine X proof | `0x2216cda30fc2e32aa16c5926837c20ee4aad10bcc4a047ea4911ae226866bd13` | + +## Gas + +| Proof | Approx ETH cost | +|---|---:| +| `5 x 0.01` | `0.000183577297848029 ETH` | +| `1 x 0.05` | `0.000185301811405636 ETH` | +| Total continuation | `~0.000368879109253665 ETH` | + +## Final Readback + +| Check | Value | +|---|---| +| `usedProofIds(0x12f9...)` | `true` | +| `usedProofIds(0xa0d3...)` | `true` | +| Final deployer ETH | `0.009682486338581646` | +| Final deployer USDC | `0.118235` | +| Final deployer cWUSDC | `56,772,623.723927` | +| Final deployer XAUt | `0.002974` | +| Final Engine X allowances | `0` for USDC, cWUSDC, and XAUt | + +## Generated Artifacts + +| Proof | JSON | Markdown | +|---|---|---| +| `5 x 0.01` | `reports/status/engine-x-loop-proof-20260507T190452Z.json` | `reports/status/engine-x-loop-proof-20260507T190452Z.md` | +| `1 x 0.05` | `reports/status/engine-x-loop-proof-20260507T190621Z.json` | `reports/status/engine-x-loop-proof-20260507T190621Z.md` | diff --git a/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.json b/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.json new file mode 100644 index 00000000..d58a376c --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": false, + "stamp": "dryrun-001-20260507T190422Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 1, + "debtUsdcPerLoopRaw": "10029", + "debtUsdcPerLoop": "0.010029", + "exactOutputPerLoopRaw": "10000", + "exactOutputPerLoop": "0.01", + "proofId": "0xdac1d96ae02afdb616d90e9e85a313af52930d0e8eeacccf85fa25a61d4b6c49", + "isoHash": "0x44355e38547f704597f3387514ba790e68bc5cd7db234cf6068060a4c5bd1301", + "auditHash": "0xf1e88efbfd6007ce7357528017e279905aec4c86a04febe049eeae650112fd63", + "pegHash": "0xf2936b13a4688a42d6d6e635b7697171bc08036d7acc109d001a5d68f78289b3", + "preview": { + "collateralXautRaw": "4", + "collateralXaut": "0.000004", + "cwusdcInPerLoopRaw": "10061", + "cwusdcInPerLoop": "0.010061", + "cwusdcOutPerLoopRaw": "10000", + "cwusdcOutPerLoop": "0.01", + "cwusdcLossPerLoopRaw": "61", + "cwusdcLossPerLoop": "0.000061", + "totalCwusdcInRaw": "10061", + "totalCwusdcIn": "0.010061", + "totalCwusdcOutRaw": "10000", + "totalCwusdcOut": "0.01", + "exactOutputTotalRaw": "10000", + "exactOutputTotal": "0.01", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "61", + "totalNeutralized": "0.000061" + }, + "balances": { + "cwusdcRaw": "56772623723927", + "cwusdc": "56772623.723927", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.md b/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.md new file mode 100644 index 00000000..97d09fe0 --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-001-20260507T190422Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `False` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `1` +- Exact cWUSDC output per loop: `0.01` +- Debt USDC per loop: `0.010029` +- cWUSDC in/out per loop: `0.010061 / 0.01` +- Neutralized per loop: `0.000061` +- Total exact output: `0.01` +- Proof ID: `0xdac1d96ae02afdb616d90e9e85a313af52930d0e8eeacccf85fa25a61d4b6c49` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.json b/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.json new file mode 100644 index 00000000..7b1b2624 --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": false, + "stamp": "dryrun-001x5-20260507T190422Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 5, + "debtUsdcPerLoopRaw": "10029", + "debtUsdcPerLoop": "0.010029", + "exactOutputPerLoopRaw": "10000", + "exactOutputPerLoop": "0.01", + "proofId": "0x12f9dc6680b05a6ed0d08db8f5f2e654a81ea797f6f5a3cc37363fd879ccd050", + "isoHash": "0x0e9c531dfa1b7f6037d1feac4db5819cb85416e2c223a9cd26c0a8291229798e", + "auditHash": "0x8d94861ca42d8640fb009dab13a322f1c4d5231f7399442338538ac2018a6505", + "pegHash": "0xccc48dd8fef23d8247bf8e9a435ce45267dc0eb2272e3a4e1a95ddcb19110d7a", + "preview": { + "collateralXautRaw": "4", + "collateralXaut": "0.000004", + "cwusdcInPerLoopRaw": "10061", + "cwusdcInPerLoop": "0.010061", + "cwusdcOutPerLoopRaw": "10000", + "cwusdcOutPerLoop": "0.01", + "cwusdcLossPerLoopRaw": "61", + "cwusdcLossPerLoop": "0.000061", + "totalCwusdcInRaw": "50305", + "totalCwusdcIn": "0.050305", + "totalCwusdcOutRaw": "50000", + "totalCwusdcOut": "0.05", + "exactOutputTotalRaw": "50000", + "exactOutputTotal": "0.05", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "305", + "totalNeutralized": "0.000305" + }, + "balances": { + "cwusdcRaw": "56772623723927", + "cwusdc": "56772623.723927", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.md b/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.md new file mode 100644 index 00000000..f7338db4 --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-001x5-20260507T190422Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `False` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `5` +- Exact cWUSDC output per loop: `0.01` +- Debt USDC per loop: `0.010029` +- cWUSDC in/out per loop: `0.010061 / 0.01` +- Neutralized per loop: `0.000061` +- Total exact output: `0.05` +- Proof ID: `0x12f9dc6680b05a6ed0d08db8f5f2e654a81ea797f6f5a3cc37363fd879ccd050` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.json b/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.json new file mode 100644 index 00000000..f9b0bdff --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.json @@ -0,0 +1,48 @@ +{ + "schema": "engine-x-loop-proof/v1", + "executed": false, + "stamp": "dryrun-005-20260507T190422Z", + "vault": "0x9a22a3e272A364D64240dE6bda796FcA421cA7E9", + "signer": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "recipient": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "roundingReceiver": "0x4A666F96fC8764181194447A7dFdb7d471b301C8", + "loops": 1, + "debtUsdcPerLoopRaw": "50121", + "debtUsdcPerLoop": "0.050121", + "exactOutputPerLoopRaw": "50000", + "exactOutputPerLoop": "0.05", + "proofId": "0xa0d3d62b8c59ad56b23f1455b450f90bc1bfc554113151d44825e2d17dbba0fd", + "isoHash": "0x1c32b8a507c8a9d807b908d6896b2092d33aef0981627c9e5c76ccacfc827e37", + "auditHash": "0x6ca4fa58e15f5439abd48feeb5f9d6739ee8e9b4a912497a3e8e64c40d7480c2", + "pegHash": "0xb61f7a450ba2ac2314f80cb504c9b14be593ffcbe82366364fda323d7241eb16", + "preview": { + "collateralXautRaw": "20", + "collateralXaut": "0.00002", + "cwusdcInPerLoopRaw": "50302", + "cwusdcInPerLoop": "0.050302", + "cwusdcOutPerLoopRaw": "50000", + "cwusdcOutPerLoop": "0.05", + "cwusdcLossPerLoopRaw": "302", + "cwusdcLossPerLoop": "0.000302", + "totalCwusdcInRaw": "50302", + "totalCwusdcIn": "0.050302", + "totalCwusdcOutRaw": "50000", + "totalCwusdcOut": "0.05", + "exactOutputTotalRaw": "50000", + "exactOutputTotal": "0.05", + "outputRoundingRaw": "0", + "outputRounding": "0", + "totalNeutralizedRaw": "302", + "totalNeutralized": "0.000302" + }, + "balances": { + "cwusdcRaw": "56772623723927", + "cwusdc": "56772623.723927", + "xautRaw": "2974", + "xaut": "0.002974", + "cwusdcAllowanceRaw": "0", + "xautAllowanceRaw": "0", + "cwusdcApproveNeeded": true, + "xautApproveNeeded": true + } +} diff --git a/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.md b/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.md new file mode 100644 index 00000000..3b2b3df9 --- /dev/null +++ b/reports/status/engine-x-loop-proof-dryrun-005-20260507T190422Z.md @@ -0,0 +1,13 @@ +# Engine X Loop Proof + +- Executed: `False` +- Vault: `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` +- Loops: `1` +- Exact cWUSDC output per loop: `0.05` +- Debt USDC per loop: `0.050121` +- cWUSDC in/out per loop: `0.050302 / 0.05` +- Neutralized per loop: `0.000302` +- Total exact output: `0.05` +- Proof ID: `0xa0d3d62b8c59ad56b23f1455b450f90bc1bfc554113151d44825e2d17dbba0fd` +- cWUSDC approval needed: `True` +- XAUt approval needed: `True` diff --git a/reports/status/engine-x-univ3-public-lp-migration-attempt-20260507.md b/reports/status/engine-x-univ3-public-lp-migration-attempt-20260507.md new file mode 100644 index 00000000..e504f42b --- /dev/null +++ b/reports/status/engine-x-univ3-public-lp-migration-attempt-20260507.md @@ -0,0 +1,57 @@ +# Engine X UniV3 Public LP Migration Attempt - 2026-05-07 + +## Summary + +The legacy Engine X virtual proof vault was retired and its token balances were swept back to the deployer. A new official Uniswap v3 `cWUSDC/USDC` pool was created and funded with the maximum available official Mainnet USDC from the deployer wallet. + +The public pool was then immediately traded by external actors. Current pool state is out of the intended active range and has no active liquidity, so it is not proof-ready for Engine X indexed-liquidity evidence. + +## Addresses + +| Field | Value | +|---|---| +| Legacy Engine X vault | `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` | +| Deployer | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| UniV3 cWUSDC/USDC pool | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | +| UniV3 NFT position ID | `1278424` | + +## Operator Transactions + +| Step | Tx hash | +|---|---| +| Withdraw legacy vault USDC | `0x233a2a79cf697dae2c1d608a3a584866a1e1643646f427e434a63677ddf27c9f` | +| Withdraw legacy vault cWUSDC | `0x7a8476a919c5d99394d0d1c14ef09c7d78b41beb9f1951ad13bf021053250f33` | +| Create and initialize UniV3 pool | `0xa2a35393de4b5e3765922ca737beff31b7435182fc22f7be7481ba83f120abf7` | +| Approve cWUSDC to position manager | `0xb3d720df02c02ead9f6b28239693f7253b2cdfe4ef552c95e61358a1763bbe52` | +| Approve USDC to position manager | `0x99319d08ed90e3dc67f39bfdaf3ff99a02584a08086a7dbbc261940a2b518c5f` | +| Mint UniV3 LP position | `0x056a8f2e97c30dd457cfcdfb428dcad301a7a47efe315717c226c12cba834e73` | + +## LP Mint + +| Field | Value | +|---|---:| +| cWUSDC deposited | `90.881764` | +| USDC deposited | `90.881764` | +| Tick range | `[-100, 100]` | +| Minted liquidity | `18,222,740,349` | + +## External Pool Activity After Mint + +| External tx | Observation | +|---|---| +| `0x5e12c485b6b67a3c9f0fd0a71d478789eb92fe82f0f901bbb56bde42bc7e4a52` | External swap against the new pool immediately after LP mint | +| `0x04a7d54fac3fd8d3a484d78aa46a7ddc43ed75f86e2c43cc04f905358152b397` | Follow-on external swap moved the pool just outside the minted range | + +## Current Readback + +| Check | Value | +|---|---:| +| Pool cWUSDC balance | `182.280270` | +| Pool USDC balance | `0.000001` | +| Current tick | `-101` | +| Active liquidity | `0` | +| Deployer USDC | `0` | + +## Conclusion + +This created an indexable public UniV3 pool and an NFT LP position, but it does not currently satisfy Engine X public indexed proof readiness. The pool needs additional official Mainnet USDC and protected/private execution before another public proof attempt. diff --git a/reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md b/reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md new file mode 100644 index 00000000..be5a8ef4 --- /dev/null +++ b/reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md @@ -0,0 +1,129 @@ +# Mainnet cWUSDC Cross-Protocol Public LP Proof Plan + +Date: `2026-05-07` + +## Finding + +The compliance-critical proof surface must use the actual public, Etherscan-indexable `cWUSDC/USDC` LP and actual official Mainnet USDC. Engine X internal accounting, cW mesh transfers, and virtual pool reserves do not satisfy that public LP swap standard by themselves. + +Current public UniV2 discovery LP: + +| Item | Value | +|---|---| +| Pair | `0xC28706F899266b36BC43cc072b3a921BDf2C48D9` | +| Base | `cWUSDC` | +| Quote | official Mainnet `USDC` | +| Latest read | `2026-05-07T15:37:56Z` | +| Reserves | `2,492,891.981995 cWUSDC / 0.000002 USDC` | +| Status | `blocked_missing_quote_side_public_lp_evidence` | + +Current deployer resources: + +| Asset | Balance | +|---|---:| +| ETH | `0.011028088441237817` | +| WETH | `0.000200000000000000` | +| USDC | `2.364477` | +| cWUSDC | `56,750,385.166450` | +| XAUt | `0.002974` | +| cWXAUT | `0` | + +## Cross-Protocol Interpretation + +Cross-protocol routing is allowed, but each protocol has a different proof role: + +| Protocol / surface | Valid role | Current limit | +|---|---|---| +| UniV2 `cWUSDC/USDC` | Public indexed swap and reserve proof | Quote side is almost empty | +| Aave USDC flash loan | Same-block working capital | Must be repaid in the same transaction unless eligible collateral/credit delegation exists | +| Aave XAUt collateral | Informational only for debt expansion | XAUt LTV is `0`; cannot originate USDC debt against XAUt today | +| DODO `cWUSDC/USDC` / `cWUSDT/USDC` | Possible unwind or secondary quote proof | Official-USDC quote reserves are too small for meaningful flash repayment | +| UniV2 `cWUSDT/cWUSDC` mesh | Deep internal cW route | Does not itself create official USDC | + +Aave current read: + +| Field | Value | +|---|---:| +| Aave USDC available liquidity | `147,083,645.099602 USDC` | +| Flash premium config | `5` bps | +| XAUt LTV | `0` | +| XAUt borrowing enabled | `false` | + +## What Can Be Done Now + +With the current wallet USDC, the smallest honest public proof is a tiny real UniV2 repair canary: + +```text +Swap about 2.236241794737 USDC into the actual cWUSDC/USDC UniV2 pair. +Expected output: about 2,492,889.745751 cWUSDC. +Post-swap reserves: about 2.236243794737 cWUSDC / 2.236243794737 USDC. +Then add the remaining about 0.128235205263 USDC plus matching cWUSDC as balanced liquidity. +Final wallet-only public LP depth: about 2.364479 / 2.364479. +``` + +The dry-run canary script keeps `0.01 USDC` back by default, so its default broadcast plan is slightly smaller: + +```text +Default balanced add after repair: 0.118235 USDC plus matching cWUSDC. +Default final public LP depth: about 2.354479 / 2.354479. +Override MAINNET_PUBLIC_LP_USDC_KEEP_RAW only after reviewing gas and reserve needs. +``` + +This would create real public LP swap evidence and actual USDC-backed reserves, but it is not enough for the `2,500 / 2,500` policy floor and not enough for the preferred `10,000 / 10,000` evidence target. + +Remaining USDC gap after current wallet balance: + +| Target | Additional official USDC needed after current wallet | +|---|---:| +| Policy floor `2,500 / 2,500` | `2,497.635521 USDC` | +| Preferred `10,000 / 10,000` | `9,997.635521 USDC` | +| Defended DODO parity | at least `290,993.364737 USDC` by current planner | + +## Aave Flash Loan Use + +Aave can be used as an execution component, but not as free permanent liquidity. + +Valid Aave proof shape: + +```text +Aave flash USDC +Use USDC in actual public UniV2 cWUSDC/USDC swap +Unwind acquired cWUSDC across approved routes into official USDC +Repay Aave principal + fee in the same transaction +Emit/save proof events and reserve readbacks +``` + +Current blocker: + +```text +official-USDC unwind capacity < Aave principal + fee +``` + +The cW mesh has deep `cWUSDC/cWUSDT` liquidity, but the official USDC exits are tiny. A flash loan sized for `2,500 USDC` would require at least `2,501.25 USDC` repayment at a `5` bps premium; current official-USDC exit liquidity is nowhere near that. + +A tiny Aave-assisted canary is possible only if the deployer repays the flash from its own USDC. That proves Aave + UniV2 routing, but it does not expand durable LP capacity and costs extra premium versus using the wallet USDC directly. + +## Recommended Execution Order + +1. Keep the Engine X recipient proof separate from public LP compliance language. +2. Dry-run the wallet-only UniV2 quote-side repair canary against the actual pair. +3. If the canary is acceptable, execute only the tiny public proof and capture: + - swap tx, + - add-liquidity tx, + - reserves before and after, + - token transfers, + - planner JSON, + - Etherscan links. +4. Do not run a meaningful Aave flash-loan proof until an official-USDC unwind route can repay principal plus premium from on-chain quotes. +5. For policy-floor or listing-quality compliance, fund official Mainnet USDC first, then run quote-side repair followed by balanced LP. + +## Safe Commands + +```bash +bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh +bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh +jq '.summary, .holderFundingChecks, .recommendedActions' reports/status/mainnet-cwusdc-usdc-repeg-plan-latest.json +EXECUTE=0 bash scripts/deployment/repair-mainnet-cwusdc-usdc-univ2-canary.sh +``` + +The canary script is dry-run by default. Do not set `EXECUTE=1` until the quote, minimum output, gas, and compliance intent are explicitly reviewed. diff --git a/reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md b/reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md new file mode 100644 index 00000000..cfd7e9c8 --- /dev/null +++ b/reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md @@ -0,0 +1,151 @@ +# Mainnet Engine X Indexed Liquidity Upgrade Plan + +Date: `2026-05-07` + +## Goal + +Upgrade the Engine X proof surface from an internal virtual `cWUSDC ~= USDC` accounting rail to public, Etherscan-indexable liquidity and real `cWUSDC/USDC` swap evidence. + +This report does not claim the current wallet can fund listing-quality depth. It defines the safe migration path for the existing small Engine X proof vault and the remaining capital gates. + +## Current Engine X Vault State + +| Item | Value | +|---|---| +| Engine X vault | `0x9a22a3e272A364D64240dE6bda796FcA421cA7E9` | +| Vault owner | `0x4A666F96fC8764181194447A7dFdb7d471b301C8` | +| Accounted pool reserves | `85.763529 cWUSDC / 85.763529 USDC` | +| Lender bucket | `5.000000 USDC` | +| Actual vault balances | `85.763529 cWUSDC / 90.763529 USDC` | +| Public UniV3 `cWUSDC/USDC` fee `100` pool | not yet deployed | +| Simulated UniV3 pool address | `0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3` | + +## Upgrade Model + +Engine X public proof mode should use these public artifacts: + +| Proof artifact | Required source | +|---|---| +| Public 1:1 liquidity | UniV3 `cWUSDC/USDC` position around tick `0` | +| Public peg readback | UniV3 pool `slot0`, tick, liquidity, token balances | +| Public swap proof | UniV3 or UniV2 transaction where actual official Mainnet `USDC` swaps against actual `cWUSDC` | +| Engine X proof linkage | Engine X proof ID and audit hash referencing the public LP pool, position token ID, and swap tx hashes | + +The live deployed `DBISEngineXVirtualBatchVault` at `0x9a22...A7E9` only has a generic `withdraw(...)`, which transfers tokens but does not reduce `poolCwusdcReserve`, `poolUsdcReserve`, or `lenderUsdcAvailable`. The repo-side next version now adds accounting-aware withdrawals and an ERC-3156 flash-lender interface, but the live vault must be redeployed or replaced before those APIs can be used on-chain. + +## Repo-Side Contract Upgrade + +Updated contract: + +```text +smom-dbis-138/contracts/flash/DBISEngineXVirtualBatchVault.sol +``` + +New accounting-aware operations: + +| Function | Purpose | +|---|---| +| `withdrawPoolLiquidity(address,uint256,uint256)` | Withdraws pool `cWUSDC/USDC` and decrements `poolCwusdcReserve` / `poolUsdcReserve`; rejects withdrawals that would break the maintained 1:1 pool invariant. | +| `withdrawLenderUsdc(address,uint256)` | Withdraws from the USDC lender bucket and decrements `lenderUsdcAvailable`. | +| `withdraw(address,address,uint256)` | Retained only for unaccounted surplus/rescue; reverts if the withdrawal would leave accounted pool/lender balances undercollateralized. | +| `maxFlashLoan(address)` / `flashFee(address,uint256)` / `flashLoan(...)` | ERC-3156 flash-loan surface for the accounted USDC lender bucket only. | +| `setFlashFeeBps(uint256)` | Owner-set flash fee, capped at `1,000` bps. Default is `5` bps. | +| `pause()` / `unpause()` | Emergency gates for seeding, proof execution, and flash loans. | +| `setFlashBorrowerAllowlistEnabled(bool)` / `setFlashBorrowerApproved(address,bool)` | Optional borrower allowlist for flash-loan callbacks. | +| `setMaxFlashLoanAmount(uint256)` | Optional cap below the full lender bucket. | + +Flash-loan accounting rule: + +```text +maxFlashLoan(USDC) = lenderUsdcAvailable +flash repayment = principal + fee +lenderUsdcAvailable after repayment = prior lenderUsdcAvailable + fee +poolUsdcReserve is never lent +``` + +This gives Engine X an internal same-transaction USDC working-capital primitive without pretending Aave flash liquidity is durable LP funding. + +Additional public-proof contracts: + +| Contract | Purpose | +|---|---| +| `DBISEngineXIndexedLiquidityVault` | Anchors Engine X proof IDs to public UniV3 pool state, swap tx hash, liquidity tx hash, and ISO/audit/peg hashes. | +| `DBISEngineXFlashProofBorrower` | Minimal ERC-3156 borrower for proving Engine X same-transaction USDC working capital from the v2 lender bucket. | + +## Dry-Run Operator Path + +The new default dry-run command is: + +```bash +pnpm engine-x:indexed-lp-migration +``` + +Equivalent direct command: + +```bash +EXECUTE=0 bash scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh +``` + +The script: + +1. Reads the Engine X vault owner, accounted reserves, lender bucket, and actual token balances. +2. Finds the official Uniswap v3 `cWUSDC/USDC` pool for fee `100`. +3. Simulates `createAndInitializePoolIfNecessary(...)` at exact 1:1 raw price. +4. Sizes the migration to the balanced pool amount only: `85.763529 cWUSDC + 85.763529 USDC`. +5. Checks whether the configured vault exposes the new accounting-aware APIs. +6. Prints the exact `withdrawPoolLiquidity`, `createAndInitializePoolIfNecessary`, `approve`, and `mint` commands only when the configured vault supports the upgraded API. +7. Leaves `EXECUTE=0` unless the operator explicitly opts into broadcast. + +Full dry-run lifecycle: + +```bash +pnpm engine-x:public-readiness +pnpm engine-x:v2-deploy +pnpm engine-x:indexed-lp-migration +pnpm engine-x:indexed-vault-deploy +pnpm engine-x:univ3-swap-proof +pnpm engine-x:indexed-proof-record +pnpm engine-x:audit-manifest +pnpm engine-x:legacy-retirement +``` + +Default UniV3 parameters: + +| Parameter | Value | +|---|---| +| Fee tier | `100` | +| Tick spacing | `1` | +| Tick lower / upper | `-100 / 100` | +| Initial `sqrtPriceX96` | `79228162514264337593543950336` | +| Migration amount | `85763529` raw per side | + +## Compliance Interpretation + +This upgrade creates a public, indexable proof surface, but only at tiny depth: + +- It can prove actual public LP creation and actual 1:1 USDC/cWUSDC liquidity. +- It can support tiny real swaps for on-chain proof hashes and Etherscan-visible transfer/event evidence. +- It does not satisfy the public policy floor of `2,500 / 2,500`. +- It does not satisfy the preferred evidence target of `10,000 / 10,000`. +- It does not solve the existing UniV2 quote-starvation by itself. + +## Remaining Gates + +| Gate | Status | +|---|---| +| Real official Mainnet USDC in the public proof transaction | available only at tiny depth | +| Public indexable LP | can be created with the new dry-run path | +| Public 1:1 swap evidence | possible after the V3 LP exists, sized below available depth | +| Policy-floor liquidity | blocked by official USDC shortfall | +| Listing-quality `10,000 / 10,000` depth | blocked by official USDC shortfall | +| Old live Engine X vault accounting | must be redeployed/replaced before accounting-aware migration | + +## Recommended Next Build + +The next contract-level upgrade should be a new Engine X indexed-liquidity adapter that: + +- references a public UniV3 pool as the peg source of truth, +- stores the fee tier, tick range, and position token ID, +- requires pre/post `slot0` and liquidity checks around proof execution, +- emits proof events containing public swap tx references and ISO/audit/peg hashes, +- refuses to label virtual internal netting as public DEX volume. diff --git a/scripts/deployment/check-deployer-lp-balances.py b/scripts/deployment/check-deployer-lp-balances.py index 5fb188a6..7cc88899 100755 --- a/scripts/deployment/check-deployer-lp-balances.py +++ b/scripts/deployment/check-deployer-lp-balances.py @@ -40,26 +40,26 @@ DEFAULT_ENV = ROOT / "smom-dbis-138" / ".env" ZERO = "0x0000000000000000000000000000000000000000" +# Defaults when .env has no RPC for a chain (prefer setting INFURA_PROJECT_ID + load-project-env, or per-chain *_RPC in .env). DEFAULT_RPC: dict[str, str] = { - "1": "https://eth.llamarpc.com", + "1": "https://ethereum.publicnode.com", "10": "https://mainnet.optimism.io", "25": "https://evm.cronos.org", "56": "https://bsc-dataseed.binance.org", "100": "https://rpc.gnosischain.com", "137": "https://polygon-rpc.com", "8453": "https://mainnet.base.org", - "42161": "https://arbitrum-one.publicnode.com", + "42161": "https://arb1.arbitrum.io/rpc", "42220": "https://forno.celo.org", - "43114": "https://avalanche-c-chain.publicnode.com", + "43114": "https://api.avax.network/ext/bc/C/rpc", "1111": "https://api.wemix.com", } # Extra public RPCs (retry when primary fails — connection resets, rate limits). RPC_FALLBACKS: dict[str, list[str]] = { "1": [ - "https://ethereum.publicnode.com", + "https://eth.llamarpc.com", "https://1rpc.io/eth", - "https://rpc.ankr.com/eth", ], "137": ["https://polygon-bor.publicnode.com", "https://1rpc.io/matic"], "42161": ["https://arbitrum.llamarpc.com"], @@ -85,6 +85,58 @@ RPC_KEYS: dict[str, list[str]] = { } +def _rpc_env_key_set() -> set[str]: + return {k for ks in RPC_KEYS.values() for k in ks} | { + "INFURA_PROJECT_ID", + "INFURA_API_KEY", + "RPC_URL_MAINNET", + } + + +def merge_rpc_os_into(env: dict[str, str]) -> None: + """Process env overrides .env for RPC-related keys (so `source load-project-env.sh` applies).""" + for k in _rpc_env_key_set(): + v = os.environ.get(k, "").strip() + if v: + env[k] = v + + +def apply_eth_mainnet_rpc_alias(env: dict[str, str]) -> None: + """Match load-project-env.sh: dotenv often sets ETH_MAINNET_RPC_URL only.""" + if not resolve(env, "ETHEREUM_MAINNET_RPC"): + alt = resolve(env, "ETH_MAINNET_RPC_URL", "") + if alt: + env["ETHEREUM_MAINNET_RPC"] = alt + + +def apply_infura_rpc_defaults(env: dict[str, str]) -> None: + """When INFURA_PROJECT_ID or INFURA_API_KEY is set, fill first unset RPC var per supported chain.""" + pid = (env.get("INFURA_PROJECT_ID") or env.get("INFURA_API_KEY") or "").strip() + if not pid: + return + + def chain_has_rpc(cid: str) -> bool: + return any(resolve(env, k) for k in RPC_KEYS.get(cid, [])) + + infura_by_chain: dict[str, str] = { + "1": f"https://mainnet.infura.io/v3/{pid}", + "10": f"https://optimism-mainnet.infura.io/v3/{pid}", + "56": f"https://bnb-mainnet.infura.io/v3/{pid}", + "100": f"https://gnosis-mainnet.infura.io/v3/{pid}", + "137": f"https://polygon-mainnet.infura.io/v3/{pid}", + "8453": f"https://base-mainnet.infura.io/v3/{pid}", + "42161": f"https://arbitrum-mainnet.infura.io/v3/{pid}", + "42220": f"https://celo-mainnet.infura.io/v3/{pid}", + "43114": f"https://avalanche-mainnet.infura.io/v3/{pid}", + } + for cid, url in infura_by_chain.items(): + if chain_has_rpc(cid): + continue + keys = RPC_KEYS.get(cid, []) + if keys: + env[keys[0]] = url + + def load_dotenv(path: Path) -> dict[str, str]: out: dict[str, str] = {} if not path.is_file(): @@ -388,6 +440,9 @@ def main() -> int: args = ap.parse_args() env = load_dotenv(args.env) + merge_rpc_os_into(env) + apply_eth_mainnet_rpc_alias(env) + apply_infura_rpc_defaults(env) dep = deployer_address(env, args.deployer) if not dep: print("No deployer: set PRIVATE_KEY or DEPLOYER_ADDRESS in .env or pass --deployer", file=sys.stderr) diff --git a/scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh b/scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh new file mode 100644 index 00000000..37b686a3 --- /dev/null +++ b/scripts/deployment/continue-mint-cwusdc-ei-matrix-wallets.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Resume mainnet cWUSDC EI matrix mints from ei-matrix-cwusdc-mint-last-idx.txt + 1. +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$SCRIPT_DIR/mint-cwusdc-ei-matrix-wallets.sh" --resume-next "$@" diff --git a/scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh b/scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh new file mode 100755 index 00000000..fc210312 --- /dev/null +++ b/scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}" +FEE="${ENGINE_X_UNIV3_FEE:-100}" +MAX_ABS_TICK="${ENGINE_X_INDEXED_MAX_ABS_TICK:-100}" +MIN_LIQUIDITY="${ENGINE_X_INDEXED_MIN_LIQUIDITY:-1}" +MAX_PROOF_SWAP_AMOUNT="${ENGINE_X_INDEXED_MAX_PROOF_SWAP_AMOUNT:-1000000}" +VERIFY="${VERIFY:-1}" +EXECUTE="${EXECUTE:-0}" + +OWNER="${ENGINE_X_INDEXED_OWNER:-${DEPLOYER_ADDRESS:-}}" +if [[ -n "${PRIVATE_KEY:-}" ]]; then + OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +fi +if [[ -z "${OWNER}" ]]; then + echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, or ENGINE_X_INDEXED_OWNER" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')" +TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')" +POOL="${ENGINE_X_UNIV3_POOL:-$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)}" + +if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then + cat </dev/null +"${CREATE_CMD_EXEC[@]}" +popd >/dev/null diff --git a/scripts/deployment/deploy-engine-x-v2-mainnet.sh b/scripts/deployment/deploy-engine-x-v2-mainnet.sh new file mode 100755 index 00000000..cd0c7933 --- /dev/null +++ b/scripts/deployment/deploy-engine-x-v2-mainnet.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +XAUT="${XAUT_MAINNET:-0x68749665FF8D2d112Fa859AA293F07A622782F38}" +XAUT_USD_PRICE6="${ENGINE_X_XAUT_USD_PRICE6:-3226640000}" +LTV_BPS="${ENGINE_X_LTV_BPS:-8000}" +MAX_ROUND_TRIP_LOSS_BPS="${ENGINE_X_MAX_ROUND_TRIP_LOSS_BPS:-100}" +POOL_CWUSDC_RAW="${ENGINE_X_POOL_CWUSDC_RAW:-85763529}" +POOL_USDC_RAW="${ENGINE_X_POOL_USDC_RAW:-85763529}" +LENDER_USDC_RAW="${ENGINE_X_LENDER_USDC_RAW:-5000000}" +FLASH_FEE_BPS="${ENGINE_X_FLASH_FEE_BPS:-5}" +MAX_FLASH_LOAN_RAW="${ENGINE_X_MAX_FLASH_LOAN_RAW:-0}" +SEED_AFTER_DEPLOY="${SEED_AFTER_DEPLOY:-0}" +VERIFY="${VERIFY:-1}" +EXECUTE="${EXECUTE:-0}" + +OWNER="${ENGINE_X_OWNER:-${DEPLOYER_ADDRESS:-}}" +if [[ -n "${PRIVATE_KEY:-}" ]]; then + OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +fi +SURPLUS_RECEIVER="${ENGINE_X_SURPLUS_RECEIVER:-${OWNER}}" + +if [[ -z "${OWNER}" || -z "${SURPLUS_RECEIVER}" ]]; then + echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, ENGINE_X_OWNER, and/or ENGINE_X_SURPLUS_RECEIVER" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +VERIFY_ARGS=() +if [[ "${VERIFY}" == "1" ]]; then + VERIFY_ARGS+=(--verify) +fi + +CREATE_CMD_EXEC=( + forge create + --broadcast + --rpc-url "${ETHEREUM_MAINNET_RPC}" + --private-key "${PRIVATE_KEY:-}" + "${VERIFY_ARGS[@]}" + contracts/flash/DBISEngineXVirtualBatchVault.sol:DBISEngineXVirtualBatchVault + --constructor-args + "${CWUSDC}" "${USDC}" "${XAUT}" "${OWNER}" "${SURPLUS_RECEIVER}" + "${XAUT_USD_PRICE6}" "${LTV_BPS}" "${MAX_ROUND_TRIP_LOSS_BPS}" +) + +cat < + +Optional seed/fund/risk controls: + cast send "\$DBIS_ENGINE_X_V2_VAULT" 'setFlashFeeBps(uint256)' "${FLASH_FEE_BPS}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" + cast send "\$DBIS_ENGINE_X_V2_VAULT" 'setMaxFlashLoanAmount(uint256)' "${MAX_FLASH_LOAN_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" + cast send "${CWUSDC}" 'approve(address,uint256)' "\$DBIS_ENGINE_X_V2_VAULT" "${POOL_CWUSDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" + cast send "${USDC}" 'approve(address,uint256)' "\$DBIS_ENGINE_X_V2_VAULT" "$((POOL_USDC_RAW + LENDER_USDC_RAW))" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" + cast send "\$DBIS_ENGINE_X_V2_VAULT" 'seedPool(uint256,uint256)' "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" + cast send "\$DBIS_ENGINE_X_V2_VAULT" 'fundLender(uint256)' "${LENDER_USDC_RAW}" --rpc-url "\$ETHEREUM_MAINNET_RPC" --private-key "\$PRIVATE_KEY" +EOF + exit 0 +fi + +pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null +DEPLOY_OUT="$("${CREATE_CMD_EXEC[@]}")" +popd >/dev/null +printf '%s\n' "${DEPLOY_OUT}" + +VAULT="$(printf '%s\n' "${DEPLOY_OUT}" | grep -oE 'Deployed to: 0x[a-fA-F0-9]{40}' | awk '{print $3}' | tail -1)" +if [[ -z "${VAULT}" ]]; then + echo "Could not parse deployed vault address" >&2 + exit 1 +fi + +cast send "${VAULT}" 'setFlashFeeBps(uint256)' "${FLASH_FEE_BPS}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" +cast send "${VAULT}" 'setMaxFlashLoanAmount(uint256)' "${MAX_FLASH_LOAN_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" + +if [[ "${SEED_AFTER_DEPLOY}" == "1" ]]; then + cast send "${CWUSDC}" 'approve(address,uint256)' "${VAULT}" "${POOL_CWUSDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" + cast send "${USDC}" 'approve(address,uint256)' "${VAULT}" "$((POOL_USDC_RAW + LENDER_USDC_RAW))" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" + cast send "${VAULT}" 'seedPool(uint256,uint256)' "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" + cast send "${VAULT}" 'fundLender(uint256)' "${LENDER_USDC_RAW}" --rpc-url "${ETHEREUM_MAINNET_RPC}" --private-key "${PRIVATE_KEY}" +fi + +echo "DBIS_ENGINE_X_V2_VAULT=${VAULT}" diff --git a/scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh b/scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh new file mode 100755 index 00000000..81682b70 --- /dev/null +++ b/scripts/deployment/deploy-engine-x-xaut-usdc-borrow-vault-mainnet.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +XAUT="${XAUT_MAINNET:-0x68749665FF8D2d112Fa859AA293F07A622782F38}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +XAUT_USD_PRICE6="${ENGINE_X_XAUT_USD_PRICE6:-3226640000}" +LTV_BPS="${ENGINE_X_BORROW_LTV_BPS:-7500}" +LIQUIDATION_THRESHOLD_BPS="${ENGINE_X_BORROW_LIQUIDATION_THRESHOLD_BPS:-8000}" +MIN_HEALTH_FACTOR_BPS="${ENGINE_X_BORROW_MIN_HEALTH_FACTOR_BPS:-11000}" +LIQUIDATION_BONUS_BPS="${ENGINE_X_BORROW_LIQUIDATION_BONUS_BPS:-500}" +MAX_BORROW_USDC_RAW="${ENGINE_X_BORROW_MAX_USDC_RAW:-0}" +PRICE_SOURCE_HASH="${ENGINE_X_XAUT_PRICE_SOURCE_HASH:-$(cast keccak "dbis-engine-x:xaut-usd-price6:${XAUT_USD_PRICE6}")}" +VERIFY="${VERIFY:-1}" +EXECUTE="${EXECUTE:-0}" + +OWNER="${ENGINE_X_BORROW_OWNER:-${DEPLOYER_ADDRESS:-}}" +if [[ -n "${PRIVATE_KEY:-}" ]]; then + OWNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +fi + +if [[ -z "${OWNER}" ]]; then + echo "Set PRIVATE_KEY, DEPLOYER_ADDRESS, or ENGINE_X_BORROW_OWNER" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +VERIFY_ARGS=() +if [[ "${VERIFY}" == "1" ]]; then + VERIFY_ARGS+=(--verify) +fi + +CREATE_CMD_EXEC=( + forge create + --broadcast + --rpc-url "${ETHEREUM_MAINNET_RPC}" + --private-key "${PRIVATE_KEY:-}" + "${VERIFY_ARGS[@]}" + contracts/flash/DBISEngineXXautUsdcBorrowVault.sol:DBISEngineXXautUsdcBorrowVault + --constructor-args + "${XAUT}" "${USDC}" "${CWUSDC}" "${OWNER}" + "${XAUT_USD_PRICE6}" "${LTV_BPS}" "${LIQUIDATION_THRESHOLD_BPS}" "${MIN_HEALTH_FACTOR_BPS}" + "${LIQUIDATION_BONUS_BPS}" "${MAX_BORROW_USDC_RAW}" "${PRICE_SOURCE_HASH}" +) + +cat </dev/null +DEPLOY_OUT="$("${CREATE_CMD_EXEC[@]}")" +popd >/dev/null +printf '%s\n' "${DEPLOY_OUT}" + +VAULT="$(printf '%s\n' "${DEPLOY_OUT}" | grep -oE 'Deployed to: 0x[a-fA-F0-9]{40}' | awk '{print $3}' | tail -1)" +if [[ -z "${VAULT}" ]]; then + echo "Could not parse deployed borrow vault address" >&2 + exit 1 +fi + +echo "DBIS_ENGINE_X_XAUT_USDC_BORROW_VAULT=${VAULT}" diff --git a/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh b/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh new file mode 100755 index 00000000..b08a8c0c --- /dev/null +++ b/scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +VAULT="${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}" +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}" +POSITION_MANAGER="${CHAIN_1_UNISWAP_V3_POSITION_MANAGER:-0xC36442b4a4522E871399CD717aBDD847Ab11FE88}" +FEE="${ENGINE_X_UNIV3_FEE:-100}" +TICK_LOWER="${ENGINE_X_UNIV3_TICK_LOWER:--100}" +TICK_UPPER="${ENGINE_X_UNIV3_TICK_UPPER:-100}" +SQRT_PRICE_X96="${ENGINE_X_UNIV3_SQRT_PRICE_X96:-79228162514264337593543950336}" +DEADLINE_SECONDS="${DEADLINE_SECONDS:-1800}" +EXECUTE="${EXECUTE:-0}" +INCLUDE_LENDER_USDC="${INCLUDE_LENDER_USDC:-0}" +MIGRATION_AMOUNT_RAW="${MIGRATION_AMOUNT_RAW:-}" + +OWNER="$(cast call "${VAULT}" 'owner()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +SIGNER="${DEPLOYER_ADDRESS:-${OWNER}}" +if [[ -n "${PRIVATE_KEY:-}" ]]; then + SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +fi + +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +if [[ "${EXECUTE}" == "1" && "${SIGNER,,}" != "${OWNER,,}" ]]; then + echo "EXECUTE=1 signer must be the Engine X vault owner" >&2 + echo " signer: ${SIGNER}" >&2 + echo " owner: ${OWNER}" >&2 + exit 1 +fi + +POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +VAULT_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +VAULT_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +FEE_TICK_SPACING="$(cast call "${FACTORY}" 'feeAmountTickSpacing(uint24)(int24)' "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +ACCOUNTING_AWARE_SUPPORTED="0" +if cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1; then + ACCOUNTING_AWARE_SUPPORTED="1" +fi + +if [[ "${FEE_TICK_SPACING}" == "0" ]]; then + echo "Uniswap v3 fee tier is not enabled on the configured factory: ${FEE}" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && "${ACCOUNTING_AWARE_SUPPORTED}" != "1" ]]; then + echo "EXECUTE=1 requires an upgraded accounting-aware Engine X vault" >&2 + echo " vault: ${VAULT}" >&2 + exit 1 +fi + +eval "$( + python3 - "${CWUSDC}" "${USDC}" "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" \ + "${LENDER_USDC_RAW}" "${VAULT_CWUSDC_RAW}" "${VAULT_USDC_RAW}" \ + "${INCLUDE_LENDER_USDC}" "${MIGRATION_AMOUNT_RAW}" "${FEE_TICK_SPACING}" \ + "${TICK_LOWER}" "${TICK_UPPER}" <<'PY' +from decimal import Decimal +import sys + +( + cwusdc, + usdc, + pool_cw, + pool_usdc, + lender_usdc, + vault_cw, + vault_usdc, + include_lender, + override_amount, + spacing, + tick_lower, + tick_upper, +) = sys.argv[1:] + +pool_cw = int(pool_cw) +pool_usdc = int(pool_usdc) +lender_usdc = int(lender_usdc) +vault_cw = int(vault_cw) +vault_usdc = int(vault_usdc) +spacing = int(spacing) +tick_lower = int(tick_lower) +tick_upper = int(tick_upper) + +if tick_lower >= tick_upper: + raise SystemExit("tick lower must be less than tick upper") +if tick_lower % spacing != 0 or tick_upper % spacing != 0: + raise SystemExit(f"ticks must be multiples of tick spacing {spacing}") + +available_usdc = vault_usdc if include_lender == "1" else max(vault_usdc - lender_usdc, 0) +amount = min(pool_cw, pool_usdc, vault_cw, available_usdc) +if override_amount: + amount = int(override_amount) +if amount <= 0: + raise SystemExit("no balanced vault liquidity is available to migrate") +if amount > vault_cw: + raise SystemExit("migration amount exceeds vault cWUSDC balance") +if amount > vault_usdc: + raise SystemExit("migration amount exceeds vault USDC balance") +if include_lender != "1" and amount > available_usdc: + raise SystemExit("migration amount would consume lender USDC; set INCLUDE_LENDER_USDC=1 only if intentional") + +addrs = sorted([cwusdc.lower(), usdc.lower()]) +token0, token1 = addrs +amount0 = amount if token0 == cwusdc.lower() else amount +amount1 = amount if token1 == usdc.lower() else amount + +def units(raw: int) -> str: + return f"{Decimal(raw) / Decimal(10**6):f}" + +def emit(name, value): + print(f"{name}='{value}'") + +emit("TOKEN0", token0) +emit("TOKEN1", token1) +emit("AMOUNT0_RAW", amount0) +emit("AMOUNT1_RAW", amount1) +emit("MIGRATE_RAW", amount) +emit("MIGRATE_UNITS", units(amount)) +emit("POOL_CWUSDC_UNITS", units(pool_cw)) +emit("POOL_USDC_UNITS", units(pool_usdc)) +emit("LENDER_USDC_UNITS", units(lender_usdc)) +emit("VAULT_CWUSDC_UNITS", units(vault_cw)) +emit("VAULT_USDC_UNITS", units(vault_usdc)) +emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_RAW", available_usdc) +emit("POOL_USDC_AVAILABLE_FOR_MIGRATION_UNITS", units(available_usdc)) +PY +)" + +POOL="$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +SIMULATED_POOL="" +if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then + SIMULATED_POOL="$(cast call "${POSITION_MANAGER}" 'createAndInitializePoolIfNecessary(address,address,uint24,uint160)(address)' \ + "${TOKEN0}" "${TOKEN1}" "${FEE}" "${SQRT_PRICE_X96}" --rpc-url "${ETHEREUM_MAINNET_RPC}" \ + | grep -oE '0x[a-fA-F0-9]{40}' | head -1 || true)" +fi + +POOL_SLOT0="" +POOL_LIQUIDITY="" +if [[ "${POOL}" != "0x0000000000000000000000000000000000000000" ]]; then + POOL_SLOT0="$(cast call "${POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}")" + POOL_LIQUIDITY="$(cast call "${POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +fi + +DEADLINE="$(( $(date +%s) + DEADLINE_SECONDS ))" + +cat < EXECUTE=0 bash scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh +EOF + exit 0 + fi + cat <&2; exit 1 ;; + esac +done + +LAST_IDX_FILE="${EI_MATRIX_CWUSDC_MINT_LAST_IDX_FILE:-${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt}" +if $RESUME_NEXT && $OFFSET_EXPLICIT; then + echo "Use only one of --offset or --resume-next." >&2 + exit 1 +fi +if $RESUME_NEXT; then + [[ -f "$LAST_IDX_FILE" ]] || { echo "Missing last-index file for --resume-next: $LAST_IDX_FILE" >&2; exit 1; } + _last="$(tr -d '[:space:]' < "$LAST_IDX_FILE" || echo "")" + [[ -n "$_last" ]] || { echo "Empty $LAST_IDX_FILE" >&2; exit 1; } + OFFSET=$((_last + 1)) + echo "Resume-next (mint): last completed idx=$_last → offset=$OFFSET" +fi + +if [[ -n "$MINT_RAW" && -n "$TOTAL_MINT_RAW" ]]; then + echo "Use only one of --mint-raw or --total-mint-raw." >&2 + exit 1 +fi +if [[ -z "$MINT_RAW" && -z "$TOTAL_MINT_RAW" ]]; then + echo "Set --mint-raw or --total-mint-raw." >&2 + exit 1 +fi + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}" +RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}" +BALANCE_RPC="${EI_MATRIX_BALANCE_RPC:-$RPC}" + +LOCK_FILE="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint.lock" +MANIFEST_DIR="${PROJECT_ROOT}/reports/status" +mkdir -p "$MANIFEST_DIR" +exec 200>"$LOCK_FILE" +if ! flock -n 200; then + echo "Another mint-cwusdc-ei-matrix-wallets.sh is already running (lock: $LOCK_FILE)." >&2 + exit 1 +fi + +GRID="$PROJECT_ROOT/config/pmm-soak-wallet-grid.json" +DEPLOYER_CANONICAL="0x4A666F96fC8764181194447A7dFdb7d471b301C8" + +[[ -f "$GRID" ]] || { echo "Missing $GRID" >&2; exit 1; } +command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; } +command -v jq &>/dev/null || { echo "jq required" >&2; exit 1; } +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; } + +FROM_ADDR=$(cast wallet address --private-key "$PRIVATE_KEY") +CHAIN_ID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || true) +[[ -n "$CHAIN_ID" ]] || CHAIN_ID="1" +if [[ "$CHAIN_ID" != "1" ]]; then + echo "[WARN] chain-id=$CHAIN_ID (expected 1)." >&2 +fi + +pending_nonce() { + local resp hex + resp=$(curl -sS -X POST "$RPC" -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"${FROM_ADDR}\",\"pending\"],\"id\":1}" 2>/dev/null) || return 1 + hex=$(echo "$resp" | jq -r '.result // empty') + [[ -n "$hex" ]] || return 1 + cast to-dec "$hex" +} + +token_decimals() { + cast call "$CWUSDC" 'decimals()(uint8)' --rpc-url "$BALANCE_RPC" 2>/dev/null | awk '{print $1}' +} + +generate_spread_amounts_raw() { + local count="$1" budget="$2" spread="$3" + python3 - "$count" "$budget" "$spread" <<'PY' +import random +import sys +n = int(sys.argv[1]) +budget = int(sys.argv[2]) +spread = float(sys.argv[3]) +if n <= 0: + sys.exit("count must be positive") +if budget < 0: + sys.exit("budget must be non-negative") +if spread < 0 or spread > 100: + sys.exit("spread-pct must be in [0, 100]") +base = 10000 +low_w = max(1, (100 * base - int(spread * base)) // 100) +high_w = (100 * base + int(spread * base)) // 100 +w = [random.randint(low_w, high_w) for _ in range(n)] +s = sum(w) +raw = [(budget * wi) // s for wi in w] +rem = budget - sum(raw) +for i in range(rem): + raw[i % n] += 1 +for x in raw: + print(x) +PY +} + +stream_addresses() { + if [[ -n "${LIMIT:-}" ]]; then + jq -r --argjson o "$OFFSET" --argjson l "$LIMIT" '.wallets[$o:$o+$l][] | .address' "$GRID" + else + jq -r --argjson o "$OFFSET" '.wallets[$o:][] | .address' "$GRID" + fi +} + +ERR_LOG="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-failures.log" +LAST_IDX="${PROJECT_ROOT}/reports/status/ei-matrix-cwusdc-mint-last-idx.txt" + +matrix_try_mint() { + local addr="$1" raw_amt="$2" idx="$3" + local dec human out tx attempt=1 + dec="${DECIMALS:-6}" + if [[ "$raw_amt" == "0" ]]; then + echo "[skip] idx=$idx $addr zero raw" + return 0 + fi + human=$(python3 -c "d=int('$dec'); a=int('$raw_amt'); print(f'{a / (10**d):.{min(d,8)}f}')" 2>/dev/null || echo "$raw_amt") + if $DRY_RUN; then + if ! $QUIET_DRY_RUN; then + echo "[dry-run] idx=$idx $addr raw=$raw_amt (~$human)" + fi + return 0 + fi + local cast_extra=() + $CAST_LEGACY && cast_extra+=(--legacy) + while [[ "$attempt" -le 2 ]]; do + if out=$(cast send "$CWUSDC" "mint(address,uint256)" "$addr" "$raw_amt" \ + --rpc-url "$RPC" --private-key "$PRIVATE_KEY" \ + --nonce "$NONCE" "${cast_extra[@]}" 2>&1); then + tx=$(echo "$out" | tail -n1) + echo "[ok] idx=$idx nonce=$NONCE $addr raw=$raw_amt (~$human) tx=$tx" + sent=$((sent + 1)) + NONCE=$((NONCE + 1)) + echo "$idx" > "$LAST_IDX" + return 0 + fi + if [[ "$attempt" -eq 1 ]] && echo "$out" | grep -qi 'nonce too low'; then + NONCE=$(pending_nonce) || true + echo "[retry] idx=$idx nonce refreshed to $NONCE (nonce too low)" >&2 + attempt=$((attempt + 1)) + continue + fi + echo "[fail] idx=$idx nonce=$NONCE $addr $out" | tee -a "$ERR_LOG" >&2 + failed=$((failed + 1)) + NONCE=$(pending_nonce) || true + return 0 + done +} + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "EI matrix cWUSDC mint (mainnet)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "RPC: $RPC" +echo "Token: $CWUSDC" +echo "Signer: $FROM_ADDR" +echo "Grid: $GRID" +echo "Dry-run: $DRY_RUN Quiet: $QUIET_DRY_RUN" +echo "Offset: $OFFSET Limit: ${LIMIT:-all}" +if [[ -n "$MINT_RAW" ]]; then + echo "Mode: fixed --mint-raw $MINT_RAW per wallet" +else + echo "Mode: --total-mint-raw $TOTAL_MINT_RAW spread: ±${SPREAD_PCT}% normalized" +fi +echo "" + +if [[ "${FROM_ADDR,,}" != "${DEPLOYER_CANONICAL,,}" ]]; then + echo "[WARN] Signer is not canonical deployer $DEPLOYER_CANONICAL — minter role may still be granted." + echo "" +fi + +DECIMALS=$(token_decimals || echo "6") + +ADDR_TMP=$(mktemp) +AMOUNTS_TMP=$(mktemp) +cleanup_tmp() { + [[ -f "$ADDR_TMP" ]] && rm -f "$ADDR_TMP" + [[ -f "$AMOUNTS_TMP" ]] && rm -f "$AMOUNTS_TMP" +} +trap cleanup_tmp EXIT + +stream_addresses > "$ADDR_TMP" +WALLET_COUNT=$(wc -l < "$ADDR_TMP" | tr -d '[:space:]') +if [[ -z "$WALLET_COUNT" || "$WALLET_COUNT" -eq 0 ]]; then + echo "No wallets in range (offset=$OFFSET limit=${LIMIT:-all})." >&2 + exit 1 +fi + +if [[ -n "$MINT_RAW" ]]; then + awk -v r="$MINT_RAW" '{print r}' "$ADDR_TMP" > "$AMOUNTS_TMP" + BUDGET_RAW=$((MINT_RAW * WALLET_COUNT)) +else + BUDGET_RAW="$TOTAL_MINT_RAW" + if [[ "$BUDGET_RAW" -le 0 ]]; then + echo "total-mint-raw must be positive." >&2 + exit 1 + fi + generate_spread_amounts_raw "$WALLET_COUNT" "$BUDGET_RAW" "$SPREAD_PCT" > "$AMOUNTS_TMP" +fi + +SUM_CHECK=$(awk '{s+=$1} END {print s}' "$AMOUNTS_TMP") +if [[ "$SUM_CHECK" != "$BUDGET_RAW" ]]; then + echo "INTERNAL: amount sum $SUM_CHECK != budget $BUDGET_RAW" >&2 + exit 1 +fi + +AMOUNTS_SHA256=$(sha256sum "$AMOUNTS_TMP" | awk '{print $1}') +TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +MANIFEST="$MANIFEST_DIR/ei-matrix-cwusdc-mint-manifest-${TS//:/-}.json" +cat >"$MANIFEST" </dev/null | awk '{print $1}' || echo "0") +ETH_HUMAN=$(python3 -c "print(f'{int(\"$ETH_WEI\") / 1e18:.6f}')" 2>/dev/null || echo "?") +echo "Signer ETH (gas): ${ETH_WEI} wei (~$ETH_HUMAN ETH)" + +if ! $DRY_RUN && [[ "${EI_MATRIX_SKIP_GAS_CHECK:-}" != "1" ]]; then + GAS_PRICE_WEI=$(cast gas-price --rpc-url "$RPC" 2>/dev/null | awk '{print $1}' | head -1) + [[ -n "$GAS_PRICE_WEI" ]] || GAS_PRICE_WEI=0 + MIN_WEI=$(python3 -c "c=int('$WALLET_COUNT'); g=int('$GAS_EST'); p=int('$GAS_PRICE_WEI'); b=int('$HEADROOM_BPS'); print(c*g*p*b//10000)") + if python3 -c "import sys; sys.exit(0 if int('$ETH_WEI') >= int('$MIN_WEI') else 1)"; then + echo "Gas preflight OK: est ${GAS_EST} gas/tx × $WALLET_COUNT × gasPrice $GAS_PRICE_WEI × (${HEADROOM_BPS}/10000) ≈ $MIN_WEI wei." + else + echo "Insufficient ETH for gas preflight. Need ≈ $MIN_WEI wei." >&2 + echo "Set EI_MATRIX_SKIP_GAS_CHECK=1 to override (operator risk)." >&2 + exit 1 + fi +fi +echo "" + +echo "Sample (first 3, last 3):" +_s_idx=$OFFSET +while IFS= read -r s_addr && IFS= read -r s_raw <&3; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) +done < <(head -3 "$ADDR_TMP") 3< <(head -3 "$AMOUNTS_TMP") +_s_idx=$((OFFSET + WALLET_COUNT - 3)) +while IFS= read -r s_addr && IFS= read -r s_raw <&3; do + h=$(python3 -c "d=int('$DECIMALS'); a=int('$s_raw'); print(f'{a / (10**d):.6f}')" 2>/dev/null || echo "$s_raw") + echo " idx=$_s_idx $s_addr raw=$s_raw (~$h cWUSDC)" + _s_idx=$((_s_idx + 1)) +done < <(tail -3 "$ADDR_TMP") 3< <(tail -3 "$AMOUNTS_TMP") +echo "" + +sent=0 +failed=0 +idx=$OFFSET +NONCE=$(pending_nonce) || { echo "Could not read pending nonce" >&2; exit 1; } +echo "Starting nonce (pending): $NONCE" +echo "" + +while IFS=$'\t' read -r addr raw_amt; do + matrix_try_mint "$addr" "$raw_amt" "$idx" + idx=$((idx + 1)) +done < <(paste -d $'\t' "$ADDR_TMP" "$AMOUNTS_TMP") + +if $DRY_RUN; then + echo "Dry-run complete. Indices covered: $OFFSET..$((idx - 1))." +else + echo "Done. Mint txs attempted: sent=$sent failed=$failed" +fi diff --git a/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh b/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh new file mode 100644 index 00000000..91544794 --- /dev/null +++ b/scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Single operator pipeline: preflight + mint cWUSDC to every EI matrix wallet on Ethereum mainnet. +# +# This does NOT bridge from Chain 138 — it mints on mainnet via MINTER_ROLE on CWUSDC_MAINNET. +# +# Usage: +# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --dry-run --mint-raw 1000000 +# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --total-mint-raw 1000000000000 --spread-pct 15 +# ./scripts/deployment/pipeline-ei-matrix-mint-cwusdc.sh --mint-raw 1000000 -- --limit 10 # extra args after -- +# +# Env: loads scripts/lib/load-project-env.sh (PRIVATE_KEY, ETHEREUM_MAINNET_RPC, CWUSDC_MAINNET). +# +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" + +PASS=() +while [[ $# -gt 0 ]]; do + if [[ "$1" == "--" ]]; then shift; PASS+=("$@"); break; fi + PASS+=("$1") + shift +done + +# shellcheck disable=SC1091 +source "$PROJECT_ROOT/scripts/lib/load-project-env.sh" + +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +PUBLIC_ETH_RPC="${ETHEREUM_MAINNET_PUBLIC_RPC:-https://ethereum-rpc.publicnode.com}" +RPC="${ETHEREUM_MAINNET_RPC:-${RPC_URL_1:-${ETH_MAINNET_RPC_URL:-$PUBLIC_ETH_RPC}}}" + +echo "══════════════════════════════════════════════════════════════" +echo "Pipeline: EI matrix mainnet cWUSDC mint" +echo "══════════════════════════════════════════════════════════════" +echo "Grid: config/pmm-soak-wallet-grid.json" +echo "Token: $CWUSDC" +echo "RPC: $RPC" +echo "" + +command -v cast &>/dev/null || { echo "cast required" >&2; exit 1; } +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY not set" >&2; exit 1; } + +FROM=$(cast wallet address --private-key "$PRIVATE_KEY") +CID=$(cast chain-id --rpc-url "$RPC" 2>/dev/null | tr -d '[:space:]' || echo "?") +echo "Signer: $FROM" +echo "Chain ID: $CID (expected 1)" +if [[ "$CID" != "1" ]]; then + echo "[WARN] Not Ethereum mainnet — aborting." >&2 + exit 1 +fi + +ROLE=$(cast keccak "MINTER_ROLE()") +# AccessControl MINTER_ROLE is keccak256 of the string "MINTER_ROLE" for OZ — use cast keccak +ROLE=$(cast keccak "MINTER_ROLE") +if HR=$(cast call "$CWUSDC" "hasRole(bytes32,address)(bool)" "$ROLE" "$FROM" --rpc-url "$RPC" 2>/dev/null); then + if [[ "${HR,,}" != *true* ]]; then + echo "[WARN] hasRole(MINTER_ROLE) returned false for signer — mints will likely revert." >&2 + else + echo "Preflight: MINTER_ROLE on cWUSDC for signer — OK" + fi +else + echo "[WARN] Could not call hasRole (ABI may differ) — continuing." >&2 +fi + +echo "" +echo "→ Running mint-cwusdc-ei-matrix-wallets.sh ${PASS[*]}" +echo "" +exec "$SCRIPT_DIR/mint-cwusdc-ei-matrix-wallets.sh" "${PASS[@]}" diff --git a/scripts/deployment/record-engine-x-indexed-liquidity-proof.sh b/scripts/deployment/record-engine-x-indexed-liquidity-proof.sh new file mode 100755 index 00000000..d3140d44 --- /dev/null +++ b/scripts/deployment/record-engine-x-indexed-liquidity-proof.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +if [[ -z "${ENGINE_X_INDEXED_LIQUIDITY_VAULT:-}" ]]; then + cat <<'EOF' +Engine X indexed proof recording blocked + ENGINE_X_INDEXED_LIQUIDITY_VAULT is not configured. + Deploy the indexed-liquidity proof vault first: + pnpm engine-x:indexed-vault-deploy +EOF + exit 0 +fi + +EXECUTE="${EXECUTE:-0}" +RECIPIENT="${RECIPIENT:-${DEPLOYER_ADDRESS:-}}" +EXACT_OUTPUT_RAW="${EXACT_OUTPUT_RAW:-10000}" +PUBLIC_SWAP_TX_HASH_PROVIDED="${PUBLIC_SWAP_TX_HASH+x}" +LIQUIDITY_TX_HASH_PROVIDED="${LIQUIDITY_TX_HASH+x}" +ISO_HASH_PROVIDED="${ISO_HASH+x}" +AUDIT_HASH_PROVIDED="${AUDIT_HASH+x}" +PEG_HASH_PROVIDED="${PEG_HASH+x}" +PROOF_ID="${PROOF_ID:-$(cast keccak "engine-x:indexed-proof:dry-run:${ENGINE_X_INDEXED_LIQUIDITY_VAULT}:${EXACT_OUTPUT_RAW}")}" +PUBLIC_SWAP_TX_HASH="${PUBLIC_SWAP_TX_HASH:-$(cast keccak "engine-x:placeholder:public-swap")}" +LIQUIDITY_TX_HASH="${LIQUIDITY_TX_HASH:-$(cast keccak "engine-x:placeholder:liquidity")}" +ISO_HASH="${ISO_HASH:-$(cast keccak "engine-x:placeholder:iso")}" +AUDIT_HASH="${AUDIT_HASH:-$(cast keccak "engine-x:placeholder:audit")}" +PEG_HASH="${PEG_HASH:-$(cast keccak "engine-x:placeholder:peg")}" + +if [[ -n "${PRIVATE_KEY:-}" ]]; then + SIGNER="$(cast wallet address --private-key "${PRIVATE_KEY}")" + RECIPIENT="${RECIPIENT:-${SIGNER}}" +else + SIGNER="${DEPLOYER_ADDRESS:-}" +fi +if [[ -z "${RECIPIENT}" || -z "${SIGNER}" ]]; then + echo "Set RECIPIENT and DEPLOYER_ADDRESS or PRIVATE_KEY" >&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +PLACEHOLDER_USED="0" +if [[ -z "${PUBLIC_SWAP_TX_HASH_PROVIDED}" || -z "${LIQUIDITY_TX_HASH_PROVIDED}" || -z "${ISO_HASH_PROVIDED}" || -z "${AUDIT_HASH_PROVIDED}" || -z "${PEG_HASH_PROVIDED}" ]]; then + PLACEHOLDER_USED="1" +fi + +if [[ "${EXECUTE}" == "1" && "${PLACEHOLDER_USED}" == "1" ]]; then + echo "EXECUTE=1 requires PUBLIC_SWAP_TX_HASH, LIQUIDITY_TX_HASH, ISO_HASH, AUDIT_HASH, and PEG_HASH" >&2 + exit 1 +fi + +STATE="$(cast call "${ENGINE_X_INDEXED_LIQUIDITY_VAULT}" 'currentPoolState()(uint160,int24,uint128,uint256,uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}")" + +cat <&2; exit 1; } +eval "${PLAN}" + +# UniV2 router is source of truth for swap output; CP closed-form above can diverge on extreme reserves. +ROUTER_AMOUNTS="$(cast call "${ROUTER}" "getAmountsOut(uint256,address[])(uint256[])" "${QUOTE_IN_RAW}" "[${USDC},${CWUSDC}]" --rpc-url "${ETHEREUM_MAINNET_RPC}")" +# Second hop amount is the cWUSDC out; require wide digits so we do not confuse brackets with reserves. +EXPECTED_BASE_OUT_RAW="$(python3 -c 'import re,sys; s=sys.argv[1]; nums=[int(x) for x in re.findall(r"\b\d{10,}\b", s)]; print(nums[-1] if nums else 0)' "${ROUTER_AMOUNTS}")" +MIN_BASE_OUT_RAW=$(( EXPECTED_BASE_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000 )) +if [[ "${EXPECTED_BASE_OUT_RAW}" -eq 0 ]]; then + echo "router getAmountsOut returned 0; aborting" >&2 + exit 1 +fi + +cat <&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +OWNER="$(cast call "${VAULT}" 'owner()(address)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" +POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)" +POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)" +LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' || echo 0)" +VAULT_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +VAULT_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" + +ACCOUNTING_AWARE_SUPPORTED="0" +if cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1; then + ACCOUNTING_AWARE_SUPPORTED="1" +fi + +if [[ "${EXECUTE}" == "1" && "${SIGNER,,}" != "${OWNER,,}" ]]; then + echo "EXECUTE=1 signer must be the vault owner" >&2 + echo " signer: ${SIGNER}" >&2 + echo " owner: ${OWNER}" >&2 + exit 1 +fi + +eval "$( + python3 - "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${LENDER_USDC_RAW}" "${VAULT_CWUSDC_RAW}" "${VAULT_USDC_RAW}" <<'PY' +from decimal import Decimal +import sys + +pool_cw, pool_usdc, lender_usdc, vault_cw, vault_usdc = map(int, sys.argv[1:]) + +def units(raw): + return f"{Decimal(raw) / Decimal(10**6):f}" + +def emit(k, v): + print(f"{k}='{v}'") + +emit("POOL_CWUSDC_UNITS", units(pool_cw)) +emit("POOL_USDC_UNITS", units(pool_usdc)) +emit("LENDER_USDC_UNITS", units(lender_usdc)) +emit("VAULT_CWUSDC_UNITS", units(vault_cw)) +emit("VAULT_USDC_UNITS", units(vault_usdc)) +emit("BALANCED_POOL_WITHDRAW_RAW", min(pool_cw, pool_usdc, vault_cw, max(vault_usdc - lender_usdc, 0))) +PY +)" + +cat <&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +RECIPIENT="${RECIPIENT:-${SIGNER}}" +ROUNDING_RECEIVER="${ROUNDING_RECEIVER:-${SIGNER}}" +PROOF_ID="${PROOF_ID:-$(cast keccak "dbis-engine-x:loop-proof:${SIGNER}:${RECIPIENT}:${EXACT_OUTPUT_PER_LOOP_RAW}:${LOOPS}:${STAMP}")}" +ISO_HASH="${ISO_HASH:-$(cast keccak "dbis-engine-x:loop-proof:iso:${PROOF_ID}")}" +AUDIT_HASH="${AUDIT_HASH:-$(cast keccak "dbis-engine-x:loop-proof:audit:${PROOF_ID}")}" +PEG_HASH="${PEG_HASH:-$(cast keccak "dbis-engine-x:loop-proof:peg:${PROOF_ID}")}" + +POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" + +if [[ -z "${DEBT_RAW}" ]]; then + DEBT_RAW="$( + python3 - "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${EXACT_OUTPUT_PER_LOOP_RAW}" "${LENDER_USDC_RAW}" <<'PY' +import sys + +reserve_cw = int(sys.argv[1]) +reserve_usdc = int(sys.argv[2]) +target = int(sys.argv[3]) +max_debt = int(sys.argv[4]) + +def amount_in(amount_out, reserve_in, reserve_out): + if amount_out <= 0 or reserve_in <= 0 or reserve_out <= amount_out: + raise ValueError("bad getAmountIn inputs") + return reserve_in * amount_out * 1000 // ((reserve_out - amount_out) * 997) + 1 + +def amount_out(amount_in, reserve_in, reserve_out): + if amount_in <= 0 or reserve_in <= 0 or reserve_out <= 0: + raise ValueError("bad getAmountOut inputs") + return amount_in * 997 * reserve_out // (reserve_in * 1000 + amount_in * 997) + +for debt in range(1, max_debt + 1): + cw_in = amount_in(debt, reserve_cw, reserve_usdc) + cw_reserve_after_in = reserve_cw + cw_in + usdc_reserve_after_out = reserve_usdc - debt + cw_out = amount_out(debt, usdc_reserve_after_out, cw_reserve_after_in) + if cw_out >= target: + print(debt) + raise SystemExit(0) +raise SystemExit("no debt size can reach exact output within lender bucket") +PY + )" +fi + +mapfile -t PREVIEW < <( + cast call "${VAULT}" \ + 'previewVirtualProof(uint256,uint256)(uint256,uint256,uint256,uint256,uint256,uint256,uint256)' \ + "${DEBT_RAW}" "${LOOPS}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}' +) + +COLLATERAL_XAUT_RAW="${PREVIEW[0]}" +CWUSDC_IN_PER_LOOP_RAW="${PREVIEW[1]}" +CWUSDC_OUT_PER_LOOP_RAW="${PREVIEW[2]}" +CWUSDC_LOSS_PER_LOOP_RAW="${PREVIEW[3]}" +TOTAL_CWUSDC_IN_RAW="${PREVIEW[4]}" +TOTAL_CWUSDC_OUT_RAW="${PREVIEW[5]}" +TOTAL_NEUTRALIZED_RAW="${PREVIEW[6]}" +EXACT_OUTPUT_TOTAL_RAW="$((EXACT_OUTPUT_PER_LOOP_RAW * LOOPS))" +OUTPUT_ROUNDING_RAW="$((TOTAL_CWUSDC_OUT_RAW - EXACT_OUTPUT_TOTAL_RAW))" + +if (( OUTPUT_ROUNDING_RAW < 0 )); then + echo "Preview output is lower than requested exact output" >&2 + exit 1 +fi + +CWUSDC_BAL_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +XAUT_BAL_RAW="$(cast call "${XAUT}" 'balanceOf(address)(uint256)' "${SIGNER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +CWUSDC_ALLOWANCE_RAW="$(cast call "${CWUSDC}" 'allowance(address,address)(uint256)' "${SIGNER}" "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +XAUT_ALLOWANCE_RAW="$(cast call "${XAUT}" 'allowance(address,address)(uint256)' "${SIGNER}" "${VAULT}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | awk '{print $1}')" +PROOF_USED="$(cast call "${VAULT}" 'usedProofIds(bytes32)(bool)' "${PROOF_ID}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | tr -d '[:space:]')" + +if [[ "${PROOF_USED}" == "true" ]]; then + echo "Proof ID is already used: ${PROOF_ID}" >&2 + exit 1 +fi +if (( CWUSDC_BAL_RAW < TOTAL_CWUSDC_IN_RAW )); then + echo "Insufficient cWUSDC balance" >&2 + exit 1 +fi +if (( XAUT_BAL_RAW < COLLATERAL_XAUT_RAW )); then + echo "Insufficient XAUt balance" >&2 + exit 1 +fi + +CWUSDC_APPROVE_NEEDED=0 +XAUT_APPROVE_NEEDED=0 +if (( CWUSDC_ALLOWANCE_RAW < TOTAL_CWUSDC_IN_RAW )); then + CWUSDC_APPROVE_NEEDED=1 +fi +if (( XAUT_ALLOWANCE_RAW < COLLATERAL_XAUT_RAW )); then + XAUT_APPROVE_NEEDED=1 +fi + +mkdir -p "$(dirname "${OUT_JSON}")" +python3 - "${OUT_JSON}" "${OUT_MD}" "${LATEST_JSON}" "${LATEST_MD}" \ + "${EXECUTE}" "${STAMP}" "${VAULT}" "${SIGNER}" "${RECIPIENT}" "${ROUNDING_RECEIVER}" \ + "${LOOPS}" "${EXACT_OUTPUT_PER_LOOP_RAW}" "${DEBT_RAW}" "${PROOF_ID}" "${ISO_HASH}" "${AUDIT_HASH}" "${PEG_HASH}" \ + "${COLLATERAL_XAUT_RAW}" "${CWUSDC_IN_PER_LOOP_RAW}" "${CWUSDC_OUT_PER_LOOP_RAW}" "${CWUSDC_LOSS_PER_LOOP_RAW}" \ + "${TOTAL_CWUSDC_IN_RAW}" "${TOTAL_CWUSDC_OUT_RAW}" "${EXACT_OUTPUT_TOTAL_RAW}" "${OUTPUT_ROUNDING_RAW}" "${TOTAL_NEUTRALIZED_RAW}" \ + "${CWUSDC_BAL_RAW}" "${XAUT_BAL_RAW}" "${CWUSDC_ALLOWANCE_RAW}" "${XAUT_ALLOWANCE_RAW}" "${CWUSDC_APPROVE_NEEDED}" "${XAUT_APPROVE_NEEDED}" <<'PY' +from decimal import Decimal +import json +from pathlib import Path +import sys + +( + out_json, out_md, latest_json, latest_md, execute, stamp, vault, signer, recipient, rounding_receiver, + loops, exact_per_loop, debt, proof_id, iso_hash, audit_hash, peg_hash, collateral, cw_in, cw_out, loss, + total_in, total_out, exact_total, rounding, neutralized, cw_bal, xaut_bal, cw_allow, xaut_allow, + cw_approve, xaut_approve, +) = sys.argv[1:] + +def units(raw): + return f"{Decimal(int(raw)) / Decimal(10**6):f}" + +payload = { + "schema": "engine-x-loop-proof/v1", + "executed": execute == "1", + "stamp": stamp, + "vault": vault, + "signer": signer, + "recipient": recipient, + "roundingReceiver": rounding_receiver, + "loops": int(loops), + "debtUsdcPerLoopRaw": debt, + "debtUsdcPerLoop": units(debt), + "exactOutputPerLoopRaw": exact_per_loop, + "exactOutputPerLoop": units(exact_per_loop), + "proofId": proof_id, + "isoHash": iso_hash, + "auditHash": audit_hash, + "pegHash": peg_hash, + "preview": { + "collateralXautRaw": collateral, + "collateralXaut": units(collateral), + "cwusdcInPerLoopRaw": cw_in, + "cwusdcInPerLoop": units(cw_in), + "cwusdcOutPerLoopRaw": cw_out, + "cwusdcOutPerLoop": units(cw_out), + "cwusdcLossPerLoopRaw": loss, + "cwusdcLossPerLoop": units(loss), + "totalCwusdcInRaw": total_in, + "totalCwusdcIn": units(total_in), + "totalCwusdcOutRaw": total_out, + "totalCwusdcOut": units(total_out), + "exactOutputTotalRaw": exact_total, + "exactOutputTotal": units(exact_total), + "outputRoundingRaw": rounding, + "outputRounding": units(rounding), + "totalNeutralizedRaw": neutralized, + "totalNeutralized": units(neutralized), + }, + "balances": { + "cwusdcRaw": cw_bal, + "cwusdc": units(cw_bal), + "xautRaw": xaut_bal, + "xaut": units(xaut_bal), + "cwusdcAllowanceRaw": cw_allow, + "xautAllowanceRaw": xaut_allow, + "cwusdcApproveNeeded": cw_approve == "1", + "xautApproveNeeded": xaut_approve == "1", + }, +} +Path(out_json).write_text(json.dumps(payload, indent=2) + "\n") +Path(latest_json).write_text(json.dumps(payload, indent=2) + "\n") +lines = [ + "# Engine X Loop Proof", + "", + f"- Executed: `{payload['executed']}`", + f"- Vault: `{vault}`", + f"- Loops: `{loops}`", + f"- Exact cWUSDC output per loop: `{payload['exactOutputPerLoop']}`", + f"- Debt USDC per loop: `{payload['debtUsdcPerLoop']}`", + f"- cWUSDC in/out per loop: `{payload['preview']['cwusdcInPerLoop']} / {payload['preview']['cwusdcOutPerLoop']}`", + f"- Neutralized per loop: `{payload['preview']['cwusdcLossPerLoop']}`", + f"- Total exact output: `{payload['preview']['exactOutputTotal']}`", + f"- Proof ID: `{proof_id}`", + f"- cWUSDC approval needed: `{payload['balances']['cwusdcApproveNeeded']}`", + f"- XAUt approval needed: `{payload['balances']['xautApproveNeeded']}`", +] +Path(out_md).write_text("\n".join(lines) + "\n") +Path(latest_md).write_text("\n".join(lines) + "\n") +PY + +cat <&2 + exit 1 +fi +if [[ "${EXECUTE}" == "1" && -z "${PRIVATE_KEY:-}" ]]; then + echo "PRIVATE_KEY is required when EXECUTE=1" >&2 + exit 1 +fi + +case "${DIRECTION}" in + USDC_TO_CWUSDC) + TOKEN_IN="${USDC}" + TOKEN_OUT="${CWUSDC}" + ;; + CWUSDC_TO_USDC) + TOKEN_IN="${CWUSDC}" + TOKEN_OUT="${USDC}" + ;; + *) + echo "Unsupported DIRECTION: ${DIRECTION}; use USDC_TO_CWUSDC or CWUSDC_TO_USDC" >&2 + exit 1 + ;; +esac + +TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')" +TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')" +POOL="$(cast call "${FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" | grep -oE '0x[a-fA-F0-9]{40}' | head -1)" + +if [[ "${POOL}" == "0x0000000000000000000000000000000000000000" ]]; then + cat </dev/null | awk '{print $1}' || true +)" +QUOTE_OUT_RAW="${QUOTE_OUT_RAW:-0}" +MIN_OUT_RAW="$((QUOTE_OUT_RAW * (10000 - SLIPPAGE_BPS) / 10000))" +DEADLINE="$(( $(date +%s) + DEADLINE_SECONDS ))" + +cat </dev/null || true +# 4d. Ethereum RPC alias (dotenv often sets ETH_MAINNET_RPC_URL only) +export ETHEREUM_MAINNET_RPC="${ETHEREUM_MAINNET_RPC:-${ETH_MAINNET_RPC_URL:-}}" +export RPC_URL_MAINNET="${RPC_URL_MAINNET:-${ETHEREUM_MAINNET_RPC:-}}" + +# 4e. Optional Infura: when INFURA_PROJECT_ID or INFURA_API_KEY is set, fill unset public-chain RPCs +# (Infura dashboard labels this value "API Key"; JSON-RPC path is always .../v3/.) +_lpr_inf="${INFURA_PROJECT_ID:-${INFURA_API_KEY:-}}" +if [[ -n "$_lpr_inf" ]]; then + [[ -z "${ETHEREUM_MAINNET_RPC:-}" ]] && export ETHEREUM_MAINNET_RPC="https://mainnet.infura.io/v3/${_lpr_inf}" + export RPC_URL_MAINNET="${RPC_URL_MAINNET:-${ETHEREUM_MAINNET_RPC:-}}" + [[ -z "${POLYGON_MAINNET_RPC:-}" ]] && [[ -z "${POLYGON_RPC_URL:-}" ]] && export POLYGON_MAINNET_RPC="https://polygon-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${ARBITRUM_MAINNET_RPC:-}" ]] && [[ -z "${ARBITRUM_RPC_URL:-}" ]] && export ARBITRUM_MAINNET_RPC="https://arbitrum-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${OPTIMISM_MAINNET_RPC:-}" ]] && [[ -z "${OPTIMISM_RPC_URL:-}" ]] && export OPTIMISM_MAINNET_RPC="https://optimism-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${BASE_MAINNET_RPC:-}" ]] && [[ -z "${BASE_RPC_URL:-}" ]] && export BASE_MAINNET_RPC="https://base-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${AVALANCHE_MAINNET_RPC:-}" ]] && [[ -z "${AVALANCHE_RPC_URL:-}" ]] && export AVALANCHE_MAINNET_RPC="https://avalanche-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${BSC_RPC_URL:-}" ]] && [[ -z "${BSC_MAINNET_RPC:-}" ]] && export BSC_RPC_URL="https://bnb-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${GNOSIS_MAINNET_RPC:-}" ]] && [[ -z "${GNOSIS_RPC_URL:-}" ]] && [[ -z "${GNOSIS_RPC:-}" ]] && export GNOSIS_MAINNET_RPC="https://gnosis-mainnet.infura.io/v3/${_lpr_inf}" + [[ -z "${CELO_MAINNET_RPC:-}" ]] && [[ -z "${CELO_RPC_URL:-}" ]] && [[ -z "${CELO_RPC:-}" ]] && export CELO_MAINNET_RPC="https://celo-mainnet.infura.io/v3/${_lpr_inf}" +fi +unset _lpr_inf + # 5. Contract addresses from master JSON (config/smart-contracts-master.json) when not set by .env [[ -f "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" ]] && source "${PROJECT_ROOT}/scripts/lib/load-contract-addresses.sh" 2>/dev/null || true diff --git a/scripts/verify/build-engine-x-audit-manifest.sh b/scripts/verify/build-engine-x-audit-manifest.sh new file mode 100755 index 00000000..22d63939 --- /dev/null +++ b/scripts/verify/build-engine-x-audit-manifest.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +OUT_DIR="${OUT_DIR:-reports/status}" +MANIFEST_JSON="${MANIFEST_JSON:-${OUT_DIR}/engine-x-audit-manifest-latest.json}" +MANIFEST_MD="${MANIFEST_MD:-${OUT_DIR}/engine-x-audit-manifest-latest.md}" +mkdir -p "${PROJECT_ROOT}/${OUT_DIR}" + +V2_ABI="${OUT_DIR}/engine-x-v2-vault-abi-latest.json" +INDEXED_ABI="${OUT_DIR}/engine-x-indexed-liquidity-vault-abi-latest.json" +BORROWER_ABI="${OUT_DIR}/engine-x-flash-proof-borrower-abi-latest.json" + +pushd "${PROJECT_ROOT}/smom-dbis-138" >/dev/null +forge inspect contracts/flash/DBISEngineXVirtualBatchVault.sol:DBISEngineXVirtualBatchVault abi >"${PROJECT_ROOT}/${V2_ABI}" +forge inspect contracts/flash/DBISEngineXIndexedLiquidityVault.sol:DBISEngineXIndexedLiquidityVault abi >"${PROJECT_ROOT}/${INDEXED_ABI}" +forge inspect contracts/flash/DBISEngineXFlashProofBorrower.sol:DBISEngineXFlashProofBorrower abi >"${PROJECT_ROOT}/${BORROWER_ABI}" +popd >/dev/null + +python3 - "${PROJECT_ROOT}" "${MANIFEST_JSON}" "${MANIFEST_MD}" "${V2_ABI}" "${INDEXED_ABI}" "${BORROWER_ABI}" <<'PY' +import hashlib +import json +from pathlib import Path +import sys +from datetime import datetime, timezone + +root = Path(sys.argv[1]) +manifest_json = root / sys.argv[2] +manifest_md = root / sys.argv[3] +abi_paths = [Path(p) for p in sys.argv[4:]] + +def sha256(path: Path) -> str: + return "0x" + hashlib.sha256((root / path).read_bytes()).hexdigest() + +contracts = [ + { + "name": "DBISEngineXVirtualBatchVault", + "source": "smom-dbis-138/contracts/flash/DBISEngineXVirtualBatchVault.sol", + "abi": str(abi_paths[0]), + "abiSha256": sha256(abi_paths[0]), + "constructorArgs": [ + "cWUSDC", + "USDC", + "XAUt", + "owner", + "surplusReceiver", + "xautUsdPrice6", + "ltvBps", + "maxRoundTripLossBps", + ], + }, + { + "name": "DBISEngineXIndexedLiquidityVault", + "source": "smom-dbis-138/contracts/flash/DBISEngineXIndexedLiquidityVault.sol", + "abi": str(abi_paths[1]), + "abiSha256": sha256(abi_paths[1]), + "constructorArgs": [ + "cWUSDC", + "USDC", + "uniV3Pool", + "owner", + "maxAbsTick", + "minLiquidity", + "maxProofSwapAmount", + ], + }, + { + "name": "DBISEngineXFlashProofBorrower", + "source": "smom-dbis-138/contracts/flash/DBISEngineXFlashProofBorrower.sol", + "abi": str(abi_paths[2]), + "abiSha256": sha256(abi_paths[2]), + "constructorArgs": ["lender", "USDC", "owner"], + }, +] + +payload = { + "schema": "engine-x-audit-manifest/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "contracts": contracts, + "operatorScripts": [ + "scripts/deployment/deploy-engine-x-v2-mainnet.sh", + "scripts/deployment/migrate-engine-x-vault-to-mainnet-cwusdc-usdc-univ3.sh", + "scripts/deployment/deploy-engine-x-indexed-liquidity-vault-mainnet.sh", + "scripts/deployment/run-engine-x-univ3-public-swap-proof.sh", + "scripts/deployment/record-engine-x-indexed-liquidity-proof.sh", + "scripts/deployment/retire-engine-x-legacy-vault.sh", + "scripts/verify/check-engine-x-public-indexed-readiness.sh", + ], + "reports": [ + "reports/status/mainnet-engine-x-indexed-liquidity-upgrade-plan-20260507.md", + "reports/status/mainnet-cwusdc-cross-protocol-public-lp-proof-plan-20260507.md", + "reports/status/engine-x-public-indexed-readiness-latest.json", + ], + "proofRequirements": [ + "verified deployed source", + "constructor args", + "public UniV3 liquidity tx hash", + "actual public cWUSDC/USDC swap tx hash", + "pre/post pool slot0 and liquidity", + "ISO 20022-style hash", + "audit envelope hash", + "peg proof hash", + ], +} + +manifest_json.write_text(json.dumps(payload, indent=2) + "\n") +lines = [ + "# Engine X Audit Manifest", + "", + f"- Generated: `{payload['generatedAt']}`", + "", + "## Contracts", +] +for c in contracts: + lines.append(f"- `{c['name']}`: `{c['source']}` ABI `{c['abi']}` hash `{c['abiSha256']}`") +lines.extend(["", "## Operator Scripts"]) +lines.extend(f"- `{s}`" for s in payload["operatorScripts"]) +lines.extend(["", "## Proof Requirements"]) +lines.extend(f"- {r}" for r in payload["proofRequirements"]) +manifest_md.write_text("\n".join(lines) + "\n") +print(manifest_json) +print(manifest_md) +PY diff --git a/scripts/verify/check-engine-x-public-indexed-readiness.sh b/scripts/verify/check-engine-x-public-indexed-readiness.sh new file mode 100755 index 00000000..99e2fa5d --- /dev/null +++ b/scripts/verify/check-engine-x-public-indexed-readiness.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +# shellcheck source=/home/intlc/projects/proxmox/scripts/lib/load-project-env.sh +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" + +: "${ETHEREUM_MAINNET_RPC:?ETHEREUM_MAINNET_RPC is required}" + +OUT_JSON="${OUT_JSON:-reports/status/engine-x-public-indexed-readiness-latest.json}" +OUT_MD="${OUT_MD:-reports/status/engine-x-public-indexed-readiness-latest.md}" +CWUSDC="${CWUSDC_MAINNET:-0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a}" +USDC="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" +V2_PAIR="${MAINNET_CWUSDC_USDC_UNIV2_PAIR:-0xC28706F899266b36BC43cc072b3a921BDf2C48D9}" +V3_FACTORY="${CHAIN_1_UNISWAP_V3_FACTORY:-0x1F98431c8aD98523631AE4a59f267346ea31F984}" +V3_FEE="${ENGINE_X_UNIV3_FEE:-100}" +VAULT="${DBIS_ENGINE_X_V2_VAULT:-${ENGINE_X_VAULT:-0x9a22a3e272A364D64240dE6bda796FcA421cA7E9}}" +INDEXED_VAULT="${ENGINE_X_INDEXED_LIQUIDITY_VAULT:-}" + +if [[ -n "${PRIVATE_KEY:-}" ]]; then + DEPLOYER="$(cast wallet address --private-key "${PRIVATE_KEY}")" +else + DEPLOYER="${DEPLOYER_ADDRESS:-}" +fi + +TOKEN0="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')" +TOKEN1="$(printf '%s\n%s\n' "${CWUSDC}" "${USDC}" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')" +V3_POOL="$(cast call "${V3_FACTORY}" 'getPool(address,address,uint24)(address)' "${TOKEN0}" "${TOKEN1}" "${V3_FEE}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | grep -oE '0x[a-fA-F0-9]{40}' | head -1 || true)" +V3_POOL="${V3_POOL:-0x0000000000000000000000000000000000000000}" + +ACCOUNTING_AWARE="0" +cast call "${VAULT}" 'maxFlashLoan(address)(uint256)' "${USDC}" --rpc-url "${ETHEREUM_MAINNET_RPC}" >/dev/null 2>&1 && ACCOUNTING_AWARE="1" + +POOL_CWUSDC_RAW="$(cast call "${VAULT}" 'poolCwusdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" +POOL_USDC_RAW="$(cast call "${VAULT}" 'poolUsdcReserve()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" +LENDER_USDC_RAW="$(cast call "${VAULT}" 'lenderUsdcAvailable()(uint256)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" +V2_RESERVES="$(cast call "${V2_PAIR}" 'getReserves()(uint112,uint112,uint32)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)" +V3_SLOT0="" +V3_LIQUIDITY="0" +V3_TOKEN0_BALANCE="0" +V3_TOKEN1_BALANCE="0" +if [[ "${V3_POOL}" != "0x0000000000000000000000000000000000000000" ]]; then + V3_SLOT0="$(cast call "${V3_POOL}" 'slot0()(uint160,int24,uint16,uint16,uint16,uint8,bool)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null || true)" + V3_LIQUIDITY="$(cast call "${V3_POOL}" 'liquidity()(uint128)' --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" + V3_TOKEN0_BALANCE="$(cast call "${TOKEN0}" 'balanceOf(address)(uint256)' "${V3_POOL}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" + V3_TOKEN1_BALANCE="$(cast call "${TOKEN1}" 'balanceOf(address)(uint256)' "${V3_POOL}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" +fi +WALLET_CWUSDC_RAW="0" +WALLET_USDC_RAW="0" +if [[ -n "${DEPLOYER}" ]]; then + WALLET_CWUSDC_RAW="$(cast call "${CWUSDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" + WALLET_USDC_RAW="$(cast call "${USDC}" 'balanceOf(address)(uint256)' "${DEPLOYER}" --rpc-url "${ETHEREUM_MAINNET_RPC}" 2>/dev/null | awk '{print $1}' || echo 0)" +fi + +mkdir -p "$(dirname "${OUT_JSON}")" +python3 - "${OUT_JSON}" "${OUT_MD}" \ + "${VAULT}" "${ACCOUNTING_AWARE}" "${POOL_CWUSDC_RAW}" "${POOL_USDC_RAW}" "${LENDER_USDC_RAW}" \ + "${INDEXED_VAULT}" "${V3_POOL}" "${V3_SLOT0}" "${V3_LIQUIDITY}" "${V3_TOKEN0_BALANCE}" "${V3_TOKEN1_BALANCE}" \ + "${V2_PAIR}" "${V2_RESERVES}" "${DEPLOYER:-}" "${WALLET_CWUSDC_RAW}" "${WALLET_USDC_RAW}" <<'PY' +from decimal import Decimal +import json +from pathlib import Path +import re +import sys +from datetime import datetime, timezone + +( + out_json, + out_md, + vault, + accounting_aware, + pool_cw, + pool_usdc, + lender_usdc, + indexed_vault, + v3_pool, + v3_slot0, + v3_liquidity, + v3_token0, + v3_token1, + v2_pair, + v2_reserves, + deployer, + wallet_cw, + wallet_usdc, +) = sys.argv[1:] + +def units(raw): + return str(Decimal(int(raw or 0)) / Decimal(10**6)) + +v2_nums = [int(x) for x in re.findall(r"\b\d+\b", v2_reserves or "")] +blockers = [] +if accounting_aware != "1": + blockers.append("configured Engine X vault is not accounting-aware") +if v3_pool.lower() == "0x0000000000000000000000000000000000000000": + blockers.append("UniV3 cWUSDC/USDC public pool is not deployed") +if int(v3_liquidity or 0) == 0: + blockers.append("UniV3 public pool has zero active liquidity") +if not indexed_vault: + blockers.append("Engine X indexed-liquidity proof vault is not configured") +if not deployer: + blockers.append("deployer address unavailable") + +payload = { + "schema": "engine-x-public-indexed-readiness/v1", + "generatedAt": datetime.now(timezone.utc).isoformat(), + "summary": { + "readyForPublicIndexedProof": not blockers, + "blockers": blockers, + }, + "engineXV2Vault": { + "address": vault, + "accountingAware": accounting_aware == "1", + "poolCwusdcRaw": pool_cw, + "poolCwusdc": units(pool_cw), + "poolUsdcRaw": pool_usdc, + "poolUsdc": units(pool_usdc), + "lenderUsdcRaw": lender_usdc, + "lenderUsdc": units(lender_usdc), + }, + "indexedProofVault": {"address": indexed_vault or None}, + "uniV3": { + "pool": v3_pool, + "slot0": v3_slot0 or None, + "liquidity": v3_liquidity, + "token0BalanceRaw": v3_token0, + "token1BalanceRaw": v3_token1, + "token0Balance": units(v3_token0), + "token1Balance": units(v3_token1), + }, + "uniV2": { + "pair": v2_pair, + "rawReserves": v2_nums[:2], + }, + "deployer": { + "address": deployer or None, + "cwusdcRaw": wallet_cw, + "cwusdc": units(wallet_cw), + "usdcRaw": wallet_usdc, + "usdc": units(wallet_usdc), + }, +} + +Path(out_json).write_text(json.dumps(payload, indent=2) + "\n") +lines = [ + "# Engine X Public Indexed Readiness", + "", + f"- Generated: `{payload['generatedAt']}`", + f"- Ready: `{payload['summary']['readyForPublicIndexedProof']}`", + f"- Engine X v2 vault: `{vault}`", + f"- Accounting-aware: `{payload['engineXV2Vault']['accountingAware']}`", + f"- UniV3 pool: `{v3_pool}`", + f"- UniV3 liquidity: `{v3_liquidity}`", + f"- Indexed proof vault: `{indexed_vault or 'not configured'}`", + f"- Deployer USDC: `{payload['deployer']['usdc']}`", + f"- Deployer cWUSDC: `{payload['deployer']['cwusdc']}`", + "", + "## Blockers", +] +if blockers: + lines.extend(f"- {b}" for b in blockers) +else: + lines.append("- none") +Path(out_md).write_text("\n".join(lines) + "\n") +print(out_json) +print(out_md) +PY diff --git a/scripts/verify/plan-mainnet-cwusdc-usdc-repeg.py b/scripts/verify/plan-mainnet-cwusdc-usdc-repeg.py index 9b099e51..f1bf1163 100644 --- a/scripts/verify/plan-mainnet-cwusdc-usdc-repeg.py +++ b/scripts/verify/plan-mainnet-cwusdc-usdc-repeg.py @@ -131,6 +131,23 @@ def funding_status(required_raw: int, available_raw: int, decimals: int = 6) -> } +def public_reseed_status( + base_shortfall_raw: int, + quote_shortfall_raw: int, + base_funding: dict, + quote_funding: dict, +) -> str: + if base_shortfall_raw == 0 and quote_shortfall_raw == 0: + return "ready" + if quote_shortfall_raw > 0 and not quote_funding["covered"]: + return "needs_usdc" + if base_shortfall_raw > 0 and not base_funding["covered"]: + return "needs_cwusdc" + if quote_shortfall_raw > 0: + return "needs_quote_side_repair" + return "needs_base_side_reseed" + + def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_override: str) -> dict: rpc_url = resolve_env_value("ETHEREUM_MAINNET_RPC", env_values) if not rpc_url: @@ -217,6 +234,11 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_ if any("defended quote query failed" in warning for warning in warnings): blockers.append("defended pool quote preview reverted; set MIN_BASE_OUT_RAW manually before any quote-in trade") + public_deviation_bps = Decimal(str(summary["publicPairDeviationBps"])) + public_price_distorted = public_deviation_bps >= Decimal(str(policy["thresholds"]["warnDeviationBps"])) + if public_price_distorted: + warnings.append("public pair is materially asymmetric; plain addLiquidity follows the current reserve ratio and will not repair price") + operator_commands = { "rerunPreflight": "bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh", "rerunPlan": "bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh", @@ -272,7 +294,42 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_ ] ) - recommended_actions = [ + if ( + public_base_shortfall_raw > 0 + or public_quote_shortfall_raw > 0 + or public_price_distorted + ): + operator_commands["publicPairRepairGuidance"] = command_block( + [ + "# Public cWUSDC/USDC is one-sided or price-distorted.", + "# Do not use addLiquidity by itself as a price repair when only one reserve side is short.", + "# Use official Mainnet USDC for a quote-side repair swap first, then add balanced", + "# liquidity only after the reserve-implied price is back inside policy gates.", + "bash scripts/verify/snapshot-mainnet-cwusdc-usdc-preflight.sh", + "bash scripts/verify/plan-mainnet-cwusdc-usdc-repeg.sh", + ] + ) + + public_action_status = public_reseed_status( + public_base_shortfall_raw, + public_quote_shortfall_raw, + public_base_funding, + public_quote_funding, + ) + needs_external_funding = ( + not defended_quote_funding["covered"] + or not public_base_funding["covered"] + or not public_quote_funding["covered"] + ) + recommended_next_action = "execute_verified_repair" + if public_quote_shortfall_raw > 0 and not public_quote_funding["covered"]: + recommended_next_action = "acquire_official_mainnet_usdc" + elif public_quote_shortfall_raw > 0 or public_price_distorted: + recommended_next_action = "run_quote_side_public_pair_repair_before_balanced_liquidity" + elif add_quote_raw > 0 and not defended_quote_funding["covered"]: + recommended_next_action = "acquire_official_mainnet_usdc_for_defended_parity" + + recommended_actions = [ { "step": "fund_manager_for_one_max_cycle", "quoteAmountRaw": str(max_automated_raw), @@ -292,11 +349,11 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_ "baseAmountUnits": str(normalize_units(public_base_shortfall_raw)), "quoteAmountRaw": str(public_quote_shortfall_raw), "quoteAmountUnits": str(normalize_units(public_quote_shortfall_raw)), - "status": ( - "ready" - if public_base_shortfall_raw == 0 - or (public_base_funding["covered"] and public_quote_funding["covered"]) - else "needs_inventory" + "status": public_action_status, + "guidance": ( + "Use quote-side repair before balanced liquidity; plain addLiquidity cannot repair an asymmetric public pair." + if public_quote_shortfall_raw > 0 or public_price_distorted + else "Use addLiquidity only after price and reserve gates pass." ), }, ] @@ -304,6 +361,23 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_ return { "generatedAt": datetime.now(timezone.utc).isoformat(), "mode": "read_only_repeg_plan", + "summary": { + "publicPairBaseReserveUnits": str(public_base_units), + "publicPairQuoteReserveUnits": str(public_quote_units), + "publicPairDeviationBps": str(summary["publicPairDeviationBps"]), + "publicPolicyFloorBaseShortfallUnits": str(normalize_units(public_base_shortfall_raw)), + "publicPolicyFloorQuoteShortfallUnits": str(normalize_units(public_quote_shortfall_raw)), + "publicPairRepairRequiresQuoteSideAction": public_quote_shortfall_raw > 0 or public_price_distorted, + "publicIndexedLpComplianceStatus": ( + "blocked_missing_quote_side_public_lp_evidence" + if public_quote_shortfall_raw > 0 or public_price_distorted + else "ready_for_evidence_review" + ), + "defendedAddQuoteUnits": str(normalize_units(add_quote_raw)), + "managerFundingShortfallUnits": manager_funding["shortfallUnits"], + "needsExternalFunding": needs_external_funding, + "recommendedNextAction": recommended_next_action, + }, "snapshotPath": str(LATEST_SNAPSHOT), "policyPath": str(POLICY_PATH), "inferenceNotes": [ @@ -370,11 +444,7 @@ def build_plan(snapshot: dict, policy: dict, env_values: dict[str, str], holder_ "blockers": holder_blockers + blockers, "status": { "canFullyReachSimple1To1WithCurrentHolder": len(holder_blockers + blockers) == 0, - "needsExternalFunding": ( - not defended_quote_funding["covered"] - or not public_base_funding["covered"] - or not public_quote_funding["covered"] - ), + "needsExternalFunding": needs_external_funding, "canFundManagerFromCurrentHolder": holder_usdc_raw >= max_automated_raw if holder_state else None, }, }