diff --git a/config/chain138-pmm-pools.json b/config/chain138-pmm-pools.json
index 243a869..d646ffa 100644
--- a/config/chain138-pmm-pools.json
+++ b/config/chain138-pmm-pools.json
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Desired-state pool spec for Chain 138 DODO PMM. Scripts should create and register only missing pools from this file, not redeploy contracts.",
"version": "1.1.0",
- "updated": "2026-04-19",
+ "updated": "2026-05-09",
"chainId": 138,
"defaults": {
"lpFeeRate": 3,
@@ -14,6 +14,10 @@
"cBTC": "0xe94260c555aC1d9D3CC9E1632883452ebDf0082E",
"cUSDT": "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22",
"cUSDC": "0xf22258f57794CC8E06237084b353Ab30fFfa640b",
+ "cUSDT_V2": "0x9FBfab33882Efe0038DAa608185718b772EE5660",
+ "cUSDC_V2": "0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d",
+ "cUSDW": "0xca6BfA614935F1aB71c9aB106baa6fbB6057095E",
+ "cAUSDT": "0x5fdDF65733e3d590463F68f93Cf16E8c04081271",
"cEURC": "0x8085961F9cF02b4d800A3c6d386D31da4B34266a",
"cEURT": "0xdf4b71c61E5912712C1Bdd451416B9aC26949d72",
"cGBPC": "0x003960f16D9d34F2e98d62723B6721Fb92074aD2",
@@ -32,6 +36,7 @@
"cStarSymbols": [
"cUSDT",
"cUSDC",
+ "cAUSDT",
"cEURC",
"cEURT",
"cGBPC",
@@ -60,8 +65,12 @@
{ "baseSymbol": "cBTC", "quoteSymbol": "cUSDC" },
{ "baseSymbol": "cBTC", "quoteSymbol": "cXAUC" },
{ "baseSymbol": "cUSDT", "quoteSymbol": "cUSDC" },
+ { "baseSymbol": "cUSDC_V2", "quoteSymbol": "cUSDT_V2" },
+ { "baseSymbol": "cUSDW", "quoteSymbol": "cUSDC" },
{ "baseSymbol": "cUSDT", "quoteSymbol": "USDT" },
{ "baseSymbol": "cUSDC", "quoteSymbol": "USDC" },
+ { "baseSymbol": "cAUSDT", "quoteSymbol": "cUSDC" },
+ { "baseSymbol": "cAUSDT", "quoteSymbol": "cUSDT" },
{ "baseSymbol": "cEURC", "quoteSymbol": "cUSDC" },
{ "baseSymbol": "cEURT", "quoteSymbol": "cUSDC" },
{ "baseSymbol": "cGBPC", "quoteSymbol": "cUSDC" },
diff --git a/contracts/flash/DBISEngineXFlashProofBorrower.sol b/contracts/flash/DBISEngineXFlashProofBorrower.sol
new file mode 100644
index 0000000..ad7f507
--- /dev/null
+++ b/contracts/flash/DBISEngineXFlashProofBorrower.sol
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
+import {IERC3156FlashLender} from "@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+
+/// @notice Minimal Engine X ERC-3156 borrower for proving same-transaction USDC working capital.
+/// @dev Prefund this contract with at least the flash fee before calling `runFlashProof`.
+contract DBISEngineXFlashProofBorrower is IERC3156FlashBorrower, Ownable {
+ using SafeERC20 for IERC20;
+
+ bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
+
+ IERC3156FlashLender public immutable lender;
+ IERC20 public immutable usdc;
+
+ mapping(bytes32 => bool) public usedProofIds;
+
+ event EngineXFlashProof(
+ bytes32 indexed proofId,
+ address indexed initiator,
+ address indexed lender,
+ address token,
+ uint256 amount,
+ uint256 fee,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ );
+
+ constructor(address lender_, address usdc_, address owner_) Ownable(owner_) {
+ require(lender_ != address(0) && usdc_ != address(0), "zero address");
+ lender = IERC3156FlashLender(lender_);
+ usdc = IERC20(usdc_);
+ }
+
+ function runFlashProof(
+ uint256 amount,
+ bytes32 proofId,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ ) external onlyOwner returns (bool) {
+ require(proofId != bytes32(0), "zero proof");
+ require(!usedProofIds[proofId], "proof used");
+ require(iso20022DocumentHash != bytes32(0), "zero iso hash");
+ require(auditEnvelopeHash != bytes32(0), "zero audit hash");
+ require(pegProofHash != bytes32(0), "zero peg hash");
+
+ bytes memory data = abi.encode(msg.sender, proofId, iso20022DocumentHash, auditEnvelopeHash, pegProofHash);
+ return lender.flashLoan(this, address(usdc), amount, data);
+ }
+
+ function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data)
+ external
+ override
+ returns (bytes32)
+ {
+ require(msg.sender == address(lender), "bad lender");
+ require(initiator == address(this), "bad initiator");
+ require(token == address(usdc), "bad token");
+
+ (
+ address operator,
+ bytes32 proofId,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ ) = abi.decode(data, (address, bytes32, bytes32, bytes32, bytes32));
+ require(!usedProofIds[proofId], "proof used");
+ usedProofIds[proofId] = true;
+
+ usdc.forceApprove(msg.sender, amount + fee);
+ emit EngineXFlashProof(
+ proofId, operator, msg.sender, token, amount, fee, iso20022DocumentHash, auditEnvelopeHash, pegProofHash
+ );
+ return _RETURN_VALUE;
+ }
+
+ function withdraw(address token, address to, uint256 amount) external onlyOwner {
+ require(to != address(0), "zero to");
+ IERC20(token).safeTransfer(to, amount);
+ }
+}
diff --git a/contracts/flash/DBISEngineXIndexedLiquidityVault.sol b/contracts/flash/DBISEngineXIndexedLiquidityVault.sol
new file mode 100644
index 0000000..1618624
--- /dev/null
+++ b/contracts/flash/DBISEngineXIndexedLiquidityVault.sol
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+
+interface IEngineXUniswapV3PoolLike {
+ function token0() external view returns (address);
+ function token1() external view returns (address);
+ function fee() external view returns (uint24);
+ function liquidity() external view returns (uint128);
+
+ function slot0()
+ external
+ view
+ returns (
+ uint160 sqrtPriceX96,
+ int24 tick,
+ uint16 observationIndex,
+ uint16 observationCardinality,
+ uint16 observationCardinalityNext,
+ uint8 feeProtocol,
+ bool unlocked
+ );
+}
+
+/// @notice Public indexed-liquidity proof anchor for Engine X.
+/// @dev This contract does not claim virtual DEX volume. It anchors Engine X proof IDs to a public UniV3 pool state.
+contract DBISEngineXIndexedLiquidityVault is Ownable {
+ address public immutable cWUSDC;
+ address public immutable usdc;
+ IEngineXUniswapV3PoolLike public immutable pool;
+ uint24 public immutable fee;
+
+ uint256 public positionTokenId;
+ int24 public maxAbsTick;
+ uint128 public minLiquidity;
+ uint256 public minToken0Balance;
+ uint256 public minToken1Balance;
+ uint256 public maxProofSwapAmount;
+ bool public paused;
+ bool public operatorAllowlistEnabled;
+
+ mapping(address => bool) public approvedOperator;
+ mapping(bytes32 => bool) public usedProofIds;
+
+ struct IndexedProof {
+ bytes32 proofId;
+ bytes32 publicSwapTxHash;
+ bytes32 liquidityTxHash;
+ address outputRecipient;
+ uint256 exactOutputAmount;
+ bytes32 iso20022DocumentHash;
+ bytes32 auditEnvelopeHash;
+ bytes32 pegProofHash;
+ }
+
+ event RiskControlsUpdated(
+ int24 maxAbsTick,
+ uint128 minLiquidity,
+ uint256 minToken0Balance,
+ uint256 minToken1Balance,
+ uint256 maxProofSwapAmount
+ );
+ event PositionTokenIdUpdated(uint256 positionTokenId);
+ event Paused(address indexed operator);
+ event Unpaused(address indexed operator);
+ event OperatorAllowlistEnabledUpdated(bool enabled);
+ event OperatorApprovalUpdated(address indexed operator, bool approved);
+ event IndexedLiquidityProof(
+ bytes32 indexed proofId,
+ address indexed operator,
+ address indexed outputRecipient,
+ address pool,
+ uint24 fee,
+ uint256 positionTokenId,
+ uint160 sqrtPriceX96,
+ int24 tick,
+ uint128 liquidity,
+ uint256 token0Balance,
+ uint256 token1Balance,
+ uint256 exactOutputAmount,
+ bytes32 publicSwapTxHash,
+ bytes32 liquidityTxHash,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ );
+
+ modifier whenNotPaused() {
+ require(!paused, "paused");
+ _;
+ }
+
+ modifier onlyApprovedOperator() {
+ require(!operatorAllowlistEnabled || approvedOperator[msg.sender], "operator not approved");
+ _;
+ }
+
+ constructor(
+ address cWUSDC_,
+ address usdc_,
+ address pool_,
+ address owner_,
+ int24 maxAbsTick_,
+ uint128 minLiquidity_,
+ uint256 maxProofSwapAmount_
+ ) Ownable(owner_) {
+ require(cWUSDC_ != address(0) && usdc_ != address(0) && pool_ != address(0), "zero address");
+ require(owner_ != address(0), "zero owner");
+ cWUSDC = cWUSDC_;
+ usdc = usdc_;
+ pool = IEngineXUniswapV3PoolLike(pool_);
+
+ address token0 = pool.token0();
+ address token1 = pool.token1();
+ require((token0 == cWUSDC_ && token1 == usdc_) || (token0 == usdc_ && token1 == cWUSDC_), "pool token mismatch");
+
+ fee = pool.fee();
+ _setRiskControls(maxAbsTick_, minLiquidity_, 0, 0, maxProofSwapAmount_);
+ }
+
+ function pause() external onlyOwner {
+ paused = true;
+ emit Paused(msg.sender);
+ }
+
+ function unpause() external onlyOwner {
+ paused = false;
+ emit Unpaused(msg.sender);
+ }
+
+ function setOperatorAllowlistEnabled(bool enabled) external onlyOwner {
+ operatorAllowlistEnabled = enabled;
+ emit OperatorAllowlistEnabledUpdated(enabled);
+ }
+
+ function setOperatorApproved(address operator, bool approved) external onlyOwner {
+ require(operator != address(0), "zero operator");
+ approvedOperator[operator] = approved;
+ emit OperatorApprovalUpdated(operator, approved);
+ }
+
+ function setPositionTokenId(uint256 positionTokenId_) external onlyOwner {
+ positionTokenId = positionTokenId_;
+ emit PositionTokenIdUpdated(positionTokenId_);
+ }
+
+ function setRiskControls(
+ int24 maxAbsTick_,
+ uint128 minLiquidity_,
+ uint256 minToken0Balance_,
+ uint256 minToken1Balance_,
+ uint256 maxProofSwapAmount_
+ ) external onlyOwner {
+ _setRiskControls(maxAbsTick_, minLiquidity_, minToken0Balance_, minToken1Balance_, maxProofSwapAmount_);
+ }
+
+ function currentPoolState()
+ public
+ view
+ returns (
+ uint160 sqrtPriceX96,
+ int24 tick,
+ uint128 activeLiquidity,
+ uint256 token0Balance,
+ uint256 token1Balance
+ )
+ {
+ (sqrtPriceX96, tick,,,,,) = pool.slot0();
+ activeLiquidity = pool.liquidity();
+ token0Balance = IERC20(pool.token0()).balanceOf(address(pool));
+ token1Balance = IERC20(pool.token1()).balanceOf(address(pool));
+ }
+
+ function recordIndexedProof(IndexedProof calldata proof)
+ external
+ whenNotPaused
+ onlyApprovedOperator
+ returns (uint160 sqrtPriceX96, int24 tick, uint128 activeLiquidity)
+ {
+ require(proof.proofId != bytes32(0), "zero proof");
+ require(!usedProofIds[proof.proofId], "proof used");
+ require(proof.publicSwapTxHash != bytes32(0), "zero swap hash");
+ require(proof.liquidityTxHash != bytes32(0), "zero liquidity hash");
+ require(proof.outputRecipient != address(0), "zero recipient");
+ require(proof.exactOutputAmount > 0, "zero output");
+ require(maxProofSwapAmount == 0 || proof.exactOutputAmount <= maxProofSwapAmount, "proof amount too high");
+ require(proof.iso20022DocumentHash != bytes32(0), "zero iso hash");
+ require(proof.auditEnvelopeHash != bytes32(0), "zero audit hash");
+ require(proof.pegProofHash != bytes32(0), "zero peg hash");
+
+ uint256 token0Balance;
+ uint256 token1Balance;
+ (sqrtPriceX96, tick, activeLiquidity, token0Balance, token1Balance) = currentPoolState();
+ require(_absTick(tick) <= uint24(maxAbsTick), "tick drift too high");
+ require(activeLiquidity >= minLiquidity, "insufficient liquidity");
+ require(token0Balance >= minToken0Balance, "insufficient token0 balance");
+ require(token1Balance >= minToken1Balance, "insufficient token1 balance");
+
+ usedProofIds[proof.proofId] = true;
+ emit IndexedLiquidityProof(
+ proof.proofId,
+ msg.sender,
+ proof.outputRecipient,
+ address(pool),
+ fee,
+ positionTokenId,
+ sqrtPriceX96,
+ tick,
+ activeLiquidity,
+ token0Balance,
+ token1Balance,
+ proof.exactOutputAmount,
+ proof.publicSwapTxHash,
+ proof.liquidityTxHash,
+ proof.iso20022DocumentHash,
+ proof.auditEnvelopeHash,
+ proof.pegProofHash
+ );
+ }
+
+ function _setRiskControls(
+ int24 maxAbsTick_,
+ uint128 minLiquidity_,
+ uint256 minToken0Balance_,
+ uint256 minToken1Balance_,
+ uint256 maxProofSwapAmount_
+ ) internal {
+ require(maxAbsTick_ >= 0, "negative tick gate");
+ maxAbsTick = maxAbsTick_;
+ minLiquidity = minLiquidity_;
+ minToken0Balance = minToken0Balance_;
+ minToken1Balance = minToken1Balance_;
+ maxProofSwapAmount = maxProofSwapAmount_;
+ emit RiskControlsUpdated(maxAbsTick_, minLiquidity_, minToken0Balance_, minToken1Balance_, maxProofSwapAmount_);
+ }
+
+ function _absTick(int24 tick) internal pure returns (uint24) {
+ return tick >= 0 ? uint24(tick) : uint24(-tick);
+ }
+}
diff --git a/contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol b/contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol
new file mode 100644
index 0000000..ede0fb0
--- /dev/null
+++ b/contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
+
+interface IEngineXDodoPoolLike {
+ function _BASE_TOKEN_() external view returns (address);
+ function _QUOTE_TOKEN_() external view returns (address);
+ function querySellBase(address trader, uint256 payBaseAmount)
+ external
+ view
+ returns (uint256 receiveQuoteAmount, uint256 mtFee);
+ function querySellQuote(address trader, uint256 payQuoteAmount)
+ external
+ view
+ returns (uint256 receiveBaseAmount, uint256 mtFee);
+ function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
+}
+
+interface IEngineXDodoIntegrationLike {
+ function addLiquidity(address pool, uint256 baseAmount, uint256 quoteAmount)
+ external
+ returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare);
+}
+
+/// @notice Engine X single-sided cWUSDC inventory wrapper for later DODO PMM promotion.
+/// @dev cWUSDC-only inventory is accounting/support inventory, not executable public DODO liquidity.
+/// DODO promotion is allowed only with nonzero base and quote amounts and passing canary guards.
+contract DBISEngineXSingleSidedDodoCwusdcVault is Ownable, ReentrancyGuard {
+ using SafeERC20 for IERC20;
+
+ IERC20 public immutable cWUSDC;
+ IERC20 public immutable quoteToken;
+ IEngineXDodoIntegrationLike public immutable dodoIntegration;
+
+ address public dodoPool;
+ bool public paused;
+
+ uint256 public accountedCwusdcInventory;
+ uint256 public accountedQuoteInventory;
+ uint256 public totalCwusdcPromotedToDodo;
+ uint256 public totalQuotePromotedToDodo;
+
+ uint256 public sampleBaseIn;
+ uint256 public minQuoteOut;
+ uint256 public sampleQuoteIn;
+ uint256 public minBaseOut;
+
+ event Paused(address indexed operator);
+ event Unpaused(address indexed operator);
+ event DodoPoolUpdated(address indexed pool);
+ event CanaryUpdated(uint256 sampleBaseIn, uint256 minQuoteOut, uint256 sampleQuoteIn, uint256 minBaseOut);
+ event CwusdcInventoryDeposited(address indexed from, uint256 amount);
+ event QuoteInventoryDeposited(address indexed from, uint256 amount);
+ event InventoryWithdrawn(address indexed token, address indexed to, uint256 amount);
+ event DodoLiquidityPromoted(
+ address indexed pool,
+ uint256 baseAmount,
+ uint256 quoteAmount,
+ uint256 baseShare,
+ uint256 quoteShare,
+ uint256 lpShare
+ );
+ event UnaccountedTokenWithdrawn(address indexed token, address indexed to, uint256 amount);
+
+ modifier whenNotPaused() {
+ require(!paused, "paused");
+ _;
+ }
+
+ constructor(address cWUSDC_, address quoteToken_, address dodoIntegration_, address owner_) Ownable(owner_) {
+ require(cWUSDC_ != address(0) && quoteToken_ != address(0), "zero token");
+ require(dodoIntegration_ != address(0), "zero integration");
+ require(owner_ != address(0), "zero owner");
+ require(cWUSDC_ != quoteToken_, "same token");
+ cWUSDC = IERC20(cWUSDC_);
+ quoteToken = IERC20(quoteToken_);
+ dodoIntegration = IEngineXDodoIntegrationLike(dodoIntegration_);
+ }
+
+ function pause() external onlyOwner {
+ paused = true;
+ emit Paused(msg.sender);
+ }
+
+ function unpause() external onlyOwner {
+ paused = false;
+ emit Unpaused(msg.sender);
+ }
+
+ function setDodoPool(address pool) external onlyOwner {
+ _validatePool(pool);
+ dodoPool = pool;
+ emit DodoPoolUpdated(pool);
+ }
+
+ function setCanary(uint256 sampleBaseIn_, uint256 minQuoteOut_, uint256 sampleQuoteIn_, uint256 minBaseOut_)
+ external
+ onlyOwner
+ {
+ require(sampleBaseIn_ > 0 || sampleQuoteIn_ > 0, "zero canary");
+ require((sampleBaseIn_ == 0) == (minQuoteOut_ == 0), "base canary mismatch");
+ require((sampleQuoteIn_ == 0) == (minBaseOut_ == 0), "quote canary mismatch");
+ sampleBaseIn = sampleBaseIn_;
+ minQuoteOut = minQuoteOut_;
+ sampleQuoteIn = sampleQuoteIn_;
+ minBaseOut = minBaseOut_;
+ emit CanaryUpdated(sampleBaseIn_, minQuoteOut_, sampleQuoteIn_, minBaseOut_);
+ }
+
+ function depositCwusdc(uint256 amount) external nonReentrant whenNotPaused {
+ require(amount > 0, "zero deposit");
+ cWUSDC.safeTransferFrom(msg.sender, address(this), amount);
+ accountedCwusdcInventory += amount;
+ emit CwusdcInventoryDeposited(msg.sender, amount);
+ }
+
+ function depositQuote(uint256 amount) external nonReentrant whenNotPaused {
+ require(amount > 0, "zero deposit");
+ quoteToken.safeTransferFrom(msg.sender, address(this), amount);
+ accountedQuoteInventory += amount;
+ emit QuoteInventoryDeposited(msg.sender, amount);
+ }
+
+ function withdrawCwusdcInventory(address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ require(amount <= accountedCwusdcInventory, "insufficient cwusdc inventory");
+ accountedCwusdcInventory -= amount;
+ cWUSDC.safeTransfer(to, amount);
+ _requireSolvent();
+ emit InventoryWithdrawn(address(cWUSDC), to, amount);
+ }
+
+ function withdrawQuoteInventory(address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ require(amount <= accountedQuoteInventory, "insufficient quote inventory");
+ accountedQuoteInventory -= amount;
+ quoteToken.safeTransfer(to, amount);
+ _requireSolvent();
+ emit InventoryWithdrawn(address(quoteToken), to, amount);
+ }
+
+ function promoteToDodo(
+ uint256 baseAmount,
+ uint256 quoteAmount,
+ uint256 minBaseShare,
+ uint256 minQuoteShare,
+ uint256 minLpShare
+ ) external onlyOwner nonReentrant whenNotPaused returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare) {
+ require(dodoPool != address(0), "pool not set");
+ require(baseAmount > 0 && quoteAmount > 0, "two-sided required");
+ require(baseAmount <= accountedCwusdcInventory, "insufficient cwusdc inventory");
+ require(quoteAmount <= accountedQuoteInventory, "insufficient quote inventory");
+
+ accountedCwusdcInventory -= baseAmount;
+ accountedQuoteInventory -= quoteAmount;
+
+ cWUSDC.forceApprove(address(dodoIntegration), baseAmount);
+ quoteToken.forceApprove(address(dodoIntegration), quoteAmount);
+ (baseShare, quoteShare, lpShare) = dodoIntegration.addLiquidity(dodoPool, baseAmount, quoteAmount);
+ cWUSDC.forceApprove(address(dodoIntegration), 0);
+ quoteToken.forceApprove(address(dodoIntegration), 0);
+
+ require(baseShare >= minBaseShare, "base share too low");
+ require(quoteShare >= minQuoteShare, "quote share too low");
+ require(lpShare >= minLpShare, "lp share too low");
+ require(canaryPasses(), "canary failed");
+
+ totalCwusdcPromotedToDodo += baseAmount;
+ totalQuotePromotedToDodo += quoteAmount;
+ _requireSolvent();
+ emit DodoLiquidityPromoted(dodoPool, baseAmount, quoteAmount, baseShare, quoteShare, lpShare);
+ }
+
+ function canaryPasses() public view returns (bool) {
+ if (dodoPool == address(0)) return false;
+ IEngineXDodoPoolLike pool = IEngineXDodoPoolLike(dodoPool);
+ (uint256 baseReserve, uint256 quoteReserve) = pool.getVaultReserve();
+ if (baseReserve == 0 || quoteReserve == 0) return false;
+ if (sampleBaseIn > 0) {
+ (uint256 quoteOut,) = pool.querySellBase(address(this), sampleBaseIn);
+ if (quoteOut < minQuoteOut) return false;
+ }
+ if (sampleQuoteIn > 0) {
+ (uint256 baseOut,) = pool.querySellQuote(address(this), sampleQuoteIn);
+ if (baseOut < minBaseOut) return false;
+ }
+ return true;
+ }
+
+ function solvencyState()
+ external
+ view
+ returns (
+ uint256 cwusdcBalance,
+ uint256 quoteBalance,
+ uint256 cwusdcInventory,
+ uint256 quoteInventory,
+ bool solvent,
+ bool executable
+ )
+ {
+ cwusdcBalance = cWUSDC.balanceOf(address(this));
+ quoteBalance = quoteToken.balanceOf(address(this));
+ cwusdcInventory = accountedCwusdcInventory;
+ quoteInventory = accountedQuoteInventory;
+ solvent = cwusdcBalance >= cwusdcInventory && quoteBalance >= quoteInventory;
+ executable = canaryPasses();
+ }
+
+ function rescueUnaccountedToken(address token, address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ IERC20(token).safeTransfer(to, amount);
+ _requireSolvent();
+ emit UnaccountedTokenWithdrawn(token, to, amount);
+ }
+
+ function _validatePool(address pool) internal view {
+ require(pool != address(0), "zero pool");
+ require(IEngineXDodoPoolLike(pool)._BASE_TOKEN_() == address(cWUSDC), "unexpected base");
+ require(IEngineXDodoPoolLike(pool)._QUOTE_TOKEN_() == address(quoteToken), "unexpected quote");
+ }
+
+ function _requireSolvent() internal view {
+ require(cWUSDC.balanceOf(address(this)) >= accountedCwusdcInventory, "cwusdc insolvent");
+ require(quoteToken.balanceOf(address(this)) >= accountedQuoteInventory, "quote insolvent");
+ }
+}
diff --git a/contracts/flash/DBISEngineXVirtualBatchVault.sol b/contracts/flash/DBISEngineXVirtualBatchVault.sol
index d9d2e20..8e8bb95 100644
--- a/contracts/flash/DBISEngineXVirtualBatchVault.sol
+++ b/contracts/flash/DBISEngineXVirtualBatchVault.sol
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
+import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
+import {IERC3156FlashLender} from "@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
@@ -8,9 +10,12 @@ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol
/// @notice Engine X maintained proof vault with virtual batch settlement.
/// @dev Use only for accounting proofs: it compresses identical maintained loops into one net settlement.
-contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
+contract DBISEngineXVirtualBatchVault is IERC3156FlashLender, Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
+ bytes32 private constant _FLASH_RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
+ uint256 public constant MAX_FLASH_FEE_BPS = 1_000;
+
IERC20 public immutable cWUSDC;
IERC20 public immutable usdc;
IERC20 public immutable xaut;
@@ -22,7 +27,13 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
uint256 public totalVirtualLoops;
uint256 public totalVirtualDebtUsdc;
uint256 public totalVirtualCwusdcIn;
+ uint256 public totalFlashFeesCollectedUsdc;
+ uint256 public flashFeeBps = 5;
+ uint256 public maxFlashLoanAmount;
+ bool public paused;
+ bool public flashBorrowerAllowlistEnabled;
mapping(bytes32 => bool) public usedProofIds;
+ mapping(address => bool) public approvedFlashBorrower;
uint256 public immutable xautUsdPrice6;
uint256 public immutable ltvBps;
@@ -58,7 +69,27 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
bytes32 auditEnvelopeHash,
bytes32 pegProofHash
);
- event OwnerWithdraw(address indexed token, address indexed to, uint256 amount);
+ event PoolLiquidityWithdrawn(address indexed to, uint256 cwusdcAmount, uint256 usdcAmount);
+ event LenderUsdcWithdrawn(address indexed to, uint256 amount);
+ event UnaccountedTokenWithdrawn(address indexed token, address indexed to, uint256 amount);
+ event FlashFeeBpsUpdated(uint256 feeBps);
+ event MaxFlashLoanAmountUpdated(uint256 amount);
+ event Paused(address indexed operator);
+ event Unpaused(address indexed operator);
+ event FlashBorrowerAllowlistEnabledUpdated(bool enabled);
+ event FlashBorrowerApprovalUpdated(address indexed borrower, bool approved);
+ event EngineXFlashLoan(
+ address indexed initiator,
+ IERC3156FlashBorrower indexed receiver,
+ address indexed token,
+ uint256 amount,
+ uint256 fee
+ );
+
+ modifier whenNotPaused() {
+ require(!paused, "paused");
+ _;
+ }
constructor(
address cWUSDC_,
@@ -85,7 +116,7 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
maxRoundTripLossBps = maxRoundTripLossBps_;
}
- function seedPool(uint256 cwusdcAmount, uint256 usdcAmount) external onlyOwner nonReentrant {
+ function seedPool(uint256 cwusdcAmount, uint256 usdcAmount) external onlyOwner nonReentrant whenNotPaused {
require(cwusdcAmount > 0 && usdcAmount > 0, "zero seed");
cWUSDC.safeTransferFrom(msg.sender, address(this), cwusdcAmount);
usdc.safeTransferFrom(msg.sender, address(this), usdcAmount);
@@ -94,7 +125,7 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
emit PoolSeeded(cwusdcAmount, usdcAmount);
}
- function fundLender(uint256 usdcAmount) external onlyOwner nonReentrant {
+ function fundLender(uint256 usdcAmount) external onlyOwner nonReentrant whenNotPaused {
require(usdcAmount > 0, "zero fund");
usdc.safeTransferFrom(msg.sender, address(this), usdcAmount);
lenderUsdcAvailable += usdcAmount;
@@ -107,6 +138,38 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
emit SurplusReceiverUpdated(receiver);
}
+ function pause() external onlyOwner {
+ paused = true;
+ emit Paused(msg.sender);
+ }
+
+ function unpause() external onlyOwner {
+ paused = false;
+ emit Unpaused(msg.sender);
+ }
+
+ function setFlashFeeBps(uint256 newFlashFeeBps) external onlyOwner {
+ require(newFlashFeeBps <= MAX_FLASH_FEE_BPS, "fee too high");
+ flashFeeBps = newFlashFeeBps;
+ emit FlashFeeBpsUpdated(newFlashFeeBps);
+ }
+
+ function setMaxFlashLoanAmount(uint256 amount) external onlyOwner {
+ maxFlashLoanAmount = amount;
+ emit MaxFlashLoanAmountUpdated(amount);
+ }
+
+ function setFlashBorrowerAllowlistEnabled(bool enabled) external onlyOwner {
+ flashBorrowerAllowlistEnabled = enabled;
+ emit FlashBorrowerAllowlistEnabledUpdated(enabled);
+ }
+
+ function setFlashBorrowerApproved(address borrower, bool approved) external onlyOwner {
+ require(borrower != address(0), "zero borrower");
+ approvedFlashBorrower[borrower] = approved;
+ emit FlashBorrowerApprovalUpdated(borrower, approved);
+ }
+
function previewCwusdcInForExactUsdc(uint256 usdcOut) public view returns (uint256) {
return _getAmountIn(usdcOut, poolCwusdcReserve, poolUsdcReserve);
}
@@ -119,6 +182,21 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
return poolCwusdcReserve > poolUsdcReserve ? poolCwusdcReserve - poolUsdcReserve : 0;
}
+ function maxFlashLoan(address token) external view override returns (uint256) {
+ if (paused || token != address(usdc)) {
+ return 0;
+ }
+ if (maxFlashLoanAmount == 0 || maxFlashLoanAmount > lenderUsdcAvailable) {
+ return lenderUsdcAvailable;
+ }
+ return maxFlashLoanAmount;
+ }
+
+ function flashFee(address token, uint256 amount) public view override returns (uint256) {
+ require(token == address(usdc), "unsupported flash token");
+ return (amount * flashFeeBps) / 10_000;
+ }
+
function minimumXautCollateral(uint256 debtUsdc) public view returns (uint256) {
uint256 numerator = debtUsdc * 1e6 * 10_000;
uint256 denominator = xautUsdPrice6 * ltvBps;
@@ -155,7 +233,11 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
totalNeutralizedCwusdcAmount = cwusdcLossPerLoop * virtualLoops;
}
- function runVirtualProof(bytes32 proofId, uint256 debtUsdcPerLoop, uint256 virtualLoops) external nonReentrant {
+ function runVirtualProof(bytes32 proofId, uint256 debtUsdcPerLoop, uint256 virtualLoops)
+ external
+ nonReentrant
+ whenNotPaused
+ {
_runVirtualProofFor(
msg.sender, proofId, debtUsdcPerLoop, virtualLoops, 0, msg.sender, bytes32(0), bytes32(0), bytes32(0)
);
@@ -164,6 +246,7 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
function runVirtualProofTo(bytes32 proofId, uint256 debtUsdcPerLoop, uint256 virtualLoops, address outputRecipient)
external
nonReentrant
+ whenNotPaused
{
require(outputRecipient != address(0), "zero output");
_runVirtualProofFor(
@@ -181,7 +264,7 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
bytes32 iso20022DocumentHash,
bytes32 auditEnvelopeHash,
bytes32 pegProofHash
- ) external nonReentrant {
+ ) external nonReentrant whenNotPaused {
require(outputRecipient != address(0), "zero output");
require(exactOutputAmount > 0, "zero exact output");
require(roundingReceiver != address(0), "zero rounding");
@@ -201,6 +284,48 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
);
}
+ function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data)
+ external
+ override
+ nonReentrant
+ whenNotPaused
+ returns (bool)
+ {
+ require(token == address(usdc), "unsupported flash token");
+ require(amount > 0, "zero flash amount");
+ require(amount <= lenderUsdcAvailable, "insufficient lender usdc");
+ require(maxFlashLoanAmount == 0 || amount <= maxFlashLoanAmount, "flash amount too high");
+ require(
+ !flashBorrowerAllowlistEnabled || approvedFlashBorrower[address(receiver)], "flash borrower not approved"
+ );
+
+ uint256 fee = flashFee(token, amount);
+ uint256 balanceBefore = usdc.balanceOf(address(this));
+ require(balanceBefore >= poolUsdcReserve + lenderUsdcAvailable, "accounting undercollateralized");
+
+ lenderUsdcAvailable -= amount;
+ usdc.safeTransfer(address(receiver), amount);
+
+ bytes32 retval = receiver.onFlashLoan(msg.sender, token, amount, fee, data);
+ require(retval == _FLASH_RETURN_VALUE, "invalid flash callback");
+
+ uint256 expectedBalance = balanceBefore + fee;
+ uint256 balanceAfterCallback = usdc.balanceOf(address(this));
+ if (balanceAfterCallback < expectedBalance) {
+ usdc.safeTransferFrom(address(receiver), address(this), expectedBalance - balanceAfterCallback);
+ }
+ require(usdc.balanceOf(address(this)) >= expectedBalance, "flash repayment failed");
+
+ lenderUsdcAvailable += amount + fee;
+ totalFlashFeesCollectedUsdc += fee;
+ require(
+ usdc.balanceOf(address(this)) >= poolUsdcReserve + lenderUsdcAvailable, "accounting undercollateralized"
+ );
+
+ emit EngineXFlashLoan(msg.sender, receiver, token, amount, fee);
+ return true;
+ }
+
function _runVirtualProofFor(
address outputRecipient,
bytes32 proofId,
@@ -272,10 +397,58 @@ contract DBISEngineXVirtualBatchVault is Ownable, ReentrancyGuard {
}
}
+ function withdrawPoolLiquidity(address to, uint256 cwusdcAmount, uint256 usdcAmount)
+ external
+ onlyOwner
+ nonReentrant
+ {
+ require(to != address(0), "zero to");
+ require(cwusdcAmount > 0 || usdcAmount > 0, "zero withdraw");
+ require(cwusdcAmount <= poolCwusdcReserve, "insufficient pool cwusdc");
+ require(usdcAmount <= poolUsdcReserve, "insufficient pool usdc");
+
+ uint256 nextCwusdcReserve = poolCwusdcReserve - cwusdcAmount;
+ uint256 nextUsdcReserve = poolUsdcReserve - usdcAmount;
+ require(nextCwusdcReserve == nextUsdcReserve, "would break maintained pool");
+
+ poolCwusdcReserve = nextCwusdcReserve;
+ poolUsdcReserve = nextUsdcReserve;
+
+ if (cwusdcAmount > 0) {
+ cWUSDC.safeTransfer(to, cwusdcAmount);
+ }
+ if (usdcAmount > 0) {
+ usdc.safeTransfer(to, usdcAmount);
+ }
+
+ emit PoolLiquidityWithdrawn(to, cwusdcAmount, usdcAmount);
+ }
+
+ function withdrawLenderUsdc(address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ require(amount <= lenderUsdcAvailable, "insufficient lender usdc");
+
+ lenderUsdcAvailable -= amount;
+ usdc.safeTransfer(to, amount);
+
+ emit LenderUsdcWithdrawn(to, amount);
+ }
+
+ /// @notice Rescue or migrate only tokens that are not backing Engine X pool/lender accounting.
function withdraw(address token, address to, uint256 amount) external onlyOwner nonReentrant {
require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
IERC20(token).safeTransfer(to, amount);
- emit OwnerWithdraw(token, to, amount);
+ _requireAccountingCollateralized();
+ emit UnaccountedTokenWithdrawn(token, to, amount);
+ }
+
+ function _requireAccountingCollateralized() internal view {
+ require(cWUSDC.balanceOf(address(this)) >= poolCwusdcReserve, "accounting undercollateralized");
+ require(
+ usdc.balanceOf(address(this)) >= poolUsdcReserve + lenderUsdcAvailable, "accounting undercollateralized"
+ );
}
function _getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256) {
diff --git a/contracts/flash/DBISEngineXXautUsdcBorrowVault.sol b/contracts/flash/DBISEngineXXautUsdcBorrowVault.sol
new file mode 100644
index 0000000..026b3b0
--- /dev/null
+++ b/contracts/flash/DBISEngineXXautUsdcBorrowVault.sol
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
+import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
+
+/// @notice Engine X XAUt-backed USDC borrowing vault.
+/// @dev This vault lends only pre-funded USDC. cWUSDC can be referenced as proof
+/// provenance, but debt is always repaid in official USDC.
+contract DBISEngineXXautUsdcBorrowVault is Ownable, ReentrancyGuard {
+ using SafeERC20 for IERC20;
+
+ uint256 public constant BPS = 10_000;
+ uint256 public constant MAX_LTV_BPS = 9_500;
+ uint256 public constant MAX_LIQUIDATION_THRESHOLD_BPS = 9_800;
+ uint256 public constant MAX_LIQUIDATION_BONUS_BPS = 2_000;
+
+ IERC20 public immutable xaut;
+ IERC20 public immutable usdc;
+ IERC20 public immutable cWUSDC;
+
+ uint256 public xautUsdPrice6;
+ uint256 public lastPriceUpdate;
+ bytes32 public lastPriceSourceHash;
+
+ uint256 public ltvBps;
+ uint256 public liquidationThresholdBps;
+ uint256 public minHealthFactorBps;
+ uint256 public liquidationBonusBps;
+ uint256 public maxBorrowUsdc;
+ bool public paused;
+
+ uint256 public lenderUsdcAvailable;
+ uint256 public totalCollateralXaut;
+ uint256 public totalDebtUsdc;
+ uint256 public totalCwusdcProofRepayUsdc;
+
+ struct Position {
+ uint256 collateralXaut;
+ uint256 debtUsdc;
+ }
+
+ mapping(address => Position) public positions;
+
+ event PriceUpdated(uint256 price6, bytes32 indexed sourceHash);
+ event RiskParamsUpdated(
+ uint256 ltvBps,
+ uint256 liquidationThresholdBps,
+ uint256 minHealthFactorBps,
+ uint256 liquidationBonusBps,
+ uint256 maxBorrowUsdc
+ );
+ event Paused(address indexed operator);
+ event Unpaused(address indexed operator);
+ event LenderFunded(address indexed funder, uint256 amount);
+ event LenderUsdcWithdrawn(address indexed to, uint256 amount);
+ event CollateralSupplied(address indexed account, uint256 amount);
+ event CollateralWithdrawn(address indexed account, address indexed to, uint256 amount);
+ event UsdcBorrowed(address indexed account, address indexed to, uint256 amount, uint256 debtAfter);
+ event UsdcRepaid(address indexed account, address indexed payer, uint256 amount, uint256 debtAfter);
+ event CwusdcSourcedRepay(
+ address indexed account,
+ address indexed payer,
+ uint256 amount,
+ bytes32 indexed publicSwapTxHash,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ );
+ event Liquidated(
+ address indexed account,
+ address indexed liquidator,
+ uint256 repayUsdc,
+ uint256 seizedXaut,
+ uint256 debtAfter
+ );
+ event UnaccountedTokenWithdrawn(address indexed token, address indexed to, uint256 amount);
+
+ modifier whenNotPaused() {
+ require(!paused, "paused");
+ _;
+ }
+
+ constructor(
+ address xaut_,
+ address usdc_,
+ address cWUSDC_,
+ address owner_,
+ uint256 xautUsdPrice6_,
+ uint256 ltvBps_,
+ uint256 liquidationThresholdBps_,
+ uint256 minHealthFactorBps_,
+ uint256 liquidationBonusBps_,
+ uint256 maxBorrowUsdc_,
+ bytes32 priceSourceHash_
+ ) Ownable(owner_) {
+ require(xaut_ != address(0) && usdc_ != address(0) && cWUSDC_ != address(0), "zero token");
+ require(owner_ != address(0), "zero owner");
+ xaut = IERC20(xaut_);
+ usdc = IERC20(usdc_);
+ cWUSDC = IERC20(cWUSDC_);
+ _setPrice(xautUsdPrice6_, priceSourceHash_);
+ _setRiskParams(
+ ltvBps_, liquidationThresholdBps_, minHealthFactorBps_, liquidationBonusBps_, maxBorrowUsdc_
+ );
+ }
+
+ function pause() external onlyOwner {
+ paused = true;
+ emit Paused(msg.sender);
+ }
+
+ function unpause() external onlyOwner {
+ paused = false;
+ emit Unpaused(msg.sender);
+ }
+
+ function setXautUsdPrice6(uint256 price6, bytes32 sourceHash) external onlyOwner {
+ _setPrice(price6, sourceHash);
+ }
+
+ function setRiskParams(
+ uint256 ltvBps_,
+ uint256 liquidationThresholdBps_,
+ uint256 minHealthFactorBps_,
+ uint256 liquidationBonusBps_,
+ uint256 maxBorrowUsdc_
+ ) external onlyOwner {
+ _setRiskParams(
+ ltvBps_, liquidationThresholdBps_, minHealthFactorBps_, liquidationBonusBps_, maxBorrowUsdc_
+ );
+ }
+
+ function fundLender(uint256 amount) external nonReentrant whenNotPaused {
+ require(amount > 0, "zero fund");
+ usdc.safeTransferFrom(msg.sender, address(this), amount);
+ lenderUsdcAvailable += amount;
+ emit LenderFunded(msg.sender, amount);
+ }
+
+ function withdrawLenderUsdc(address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ require(amount <= lenderUsdcAvailable, "insufficient lender usdc");
+ lenderUsdcAvailable -= amount;
+ usdc.safeTransfer(to, amount);
+ emit LenderUsdcWithdrawn(to, amount);
+ }
+
+ function supplyCollateral(uint256 amount) external nonReentrant whenNotPaused {
+ require(amount > 0, "zero collateral");
+ xaut.safeTransferFrom(msg.sender, address(this), amount);
+ positions[msg.sender].collateralXaut += amount;
+ totalCollateralXaut += amount;
+ emit CollateralSupplied(msg.sender, amount);
+ }
+
+ function withdrawCollateral(uint256 amount, address to) external nonReentrant whenNotPaused {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero collateral");
+ Position storage position = positions[msg.sender];
+ require(amount <= position.collateralXaut, "insufficient collateral");
+ position.collateralXaut -= amount;
+ totalCollateralXaut -= amount;
+ _requireHealthy(msg.sender);
+ xaut.safeTransfer(to, amount);
+ emit CollateralWithdrawn(msg.sender, to, amount);
+ }
+
+ function borrowUsdc(uint256 amount, address to) external nonReentrant whenNotPaused {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero borrow");
+ require(amount <= lenderUsdcAvailable, "insufficient lender usdc");
+ if (maxBorrowUsdc != 0) {
+ require(totalDebtUsdc + amount <= maxBorrowUsdc, "max borrow exceeded");
+ }
+
+ Position storage position = positions[msg.sender];
+ position.debtUsdc += amount;
+ totalDebtUsdc += amount;
+ lenderUsdcAvailable -= amount;
+ _requireHealthy(msg.sender);
+
+ usdc.safeTransfer(to, amount);
+ emit UsdcBorrowed(msg.sender, to, amount, position.debtUsdc);
+ }
+
+ function repayUsdc(uint256 amount) external nonReentrant whenNotPaused returns (uint256 repaid) {
+ repaid = _repay(msg.sender, msg.sender, amount);
+ }
+
+ function repayUsdcFor(address account, uint256 amount) external nonReentrant whenNotPaused returns (uint256 repaid) {
+ require(account != address(0), "zero account");
+ repaid = _repay(account, msg.sender, amount);
+ }
+
+ function repayUsdcFromCwusdcProof(
+ uint256 amount,
+ bytes32 publicSwapTxHash,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ ) external nonReentrant whenNotPaused returns (uint256 repaid) {
+ require(publicSwapTxHash != bytes32(0), "zero swap hash");
+ require(iso20022DocumentHash != bytes32(0), "zero iso hash");
+ require(auditEnvelopeHash != bytes32(0), "zero audit hash");
+ require(pegProofHash != bytes32(0), "zero peg hash");
+
+ repaid = _repay(msg.sender, msg.sender, amount);
+ totalCwusdcProofRepayUsdc += repaid;
+ emit CwusdcSourcedRepay(
+ msg.sender, msg.sender, repaid, publicSwapTxHash, iso20022DocumentHash, auditEnvelopeHash, pegProofHash
+ );
+ }
+
+ function liquidate(address account, uint256 repayAmount) external nonReentrant whenNotPaused returns (uint256 seized) {
+ require(account != address(0), "zero account");
+ require(repayAmount > 0, "zero repay");
+ require(healthFactorBps(account) < minHealthFactorBps, "position healthy");
+
+ Position storage position = positions[account];
+ uint256 repaid = repayAmount > position.debtUsdc ? position.debtUsdc : repayAmount;
+ require(repaid > 0, "zero debt");
+ seized = _xautForUsdcWithBonus(repaid);
+ require(seized <= position.collateralXaut, "insufficient collateral");
+
+ usdc.safeTransferFrom(msg.sender, address(this), repaid);
+ position.debtUsdc -= repaid;
+ totalDebtUsdc -= repaid;
+ lenderUsdcAvailable += repaid;
+ position.collateralXaut -= seized;
+ totalCollateralXaut -= seized;
+ xaut.safeTransfer(msg.sender, seized);
+
+ emit Liquidated(account, msg.sender, repaid, seized, position.debtUsdc);
+ }
+
+ function rescueUnaccountedToken(address token, address to, uint256 amount) external onlyOwner nonReentrant {
+ require(to != address(0), "zero to");
+ require(amount > 0, "zero withdraw");
+ IERC20(token).safeTransfer(to, amount);
+ _requireAccountingCollateralized();
+ emit UnaccountedTokenWithdrawn(token, to, amount);
+ }
+
+ function collateralValueUsd6(address account) public view returns (uint256) {
+ return collateralValueUsd6ForRaw(positions[account].collateralXaut);
+ }
+
+ function collateralValueUsd6ForRaw(uint256 xautRaw) public view returns (uint256) {
+ return (xautRaw * xautUsdPrice6) / 1e6;
+ }
+
+ function maxDebtForCollateral(uint256 xautRaw) public view returns (uint256) {
+ uint256 collateralUsd6 = collateralValueUsd6ForRaw(xautRaw);
+ uint256 byLtv = (collateralUsd6 * ltvBps) / BPS;
+ uint256 byHealth = (collateralUsd6 * liquidationThresholdBps) / minHealthFactorBps;
+ return byLtv < byHealth ? byLtv : byHealth;
+ }
+
+ function maxAdditionalBorrow(address account) public view returns (uint256) {
+ uint256 maxDebt = maxDebtForCollateral(positions[account].collateralXaut);
+ if (positions[account].debtUsdc >= maxDebt) {
+ return 0;
+ }
+ return maxDebt - positions[account].debtUsdc;
+ }
+
+ function healthFactorBps(address account) public view returns (uint256) {
+ Position memory position = positions[account];
+ if (position.debtUsdc == 0) {
+ return type(uint256).max;
+ }
+ uint256 collateralUsd6 = collateralValueUsd6ForRaw(position.collateralXaut);
+ return (collateralUsd6 * liquidationThresholdBps) / position.debtUsdc;
+ }
+
+ function _repay(address account, address payer, uint256 amount) internal returns (uint256 repaid) {
+ require(amount > 0, "zero repay");
+ Position storage position = positions[account];
+ require(position.debtUsdc > 0, "zero debt");
+ repaid = amount > position.debtUsdc ? position.debtUsdc : amount;
+ usdc.safeTransferFrom(payer, address(this), repaid);
+ position.debtUsdc -= repaid;
+ totalDebtUsdc -= repaid;
+ lenderUsdcAvailable += repaid;
+ emit UsdcRepaid(account, payer, repaid, position.debtUsdc);
+ }
+
+ function _requireHealthy(address account) internal view {
+ Position memory position = positions[account];
+ if (position.debtUsdc == 0) {
+ return;
+ }
+ require(position.debtUsdc <= maxDebtForCollateral(position.collateralXaut), "exceeds collateral");
+ require(healthFactorBps(account) >= minHealthFactorBps, "health too low");
+ }
+
+ function _xautForUsdcWithBonus(uint256 usdcAmount) internal view returns (uint256) {
+ uint256 numerator = usdcAmount * 1e6 * (BPS + liquidationBonusBps);
+ uint256 denominator = xautUsdPrice6 * BPS;
+ return (numerator + denominator - 1) / denominator;
+ }
+
+ function _setPrice(uint256 price6, bytes32 sourceHash) internal {
+ require(price6 > 0, "zero price");
+ require(sourceHash != bytes32(0), "zero source");
+ xautUsdPrice6 = price6;
+ lastPriceUpdate = block.timestamp;
+ lastPriceSourceHash = sourceHash;
+ emit PriceUpdated(price6, sourceHash);
+ }
+
+ function _setRiskParams(
+ uint256 ltvBps_,
+ uint256 liquidationThresholdBps_,
+ uint256 minHealthFactorBps_,
+ uint256 liquidationBonusBps_,
+ uint256 maxBorrowUsdc_
+ ) internal {
+ require(ltvBps_ > 0 && ltvBps_ <= MAX_LTV_BPS, "bad ltv");
+ require(
+ liquidationThresholdBps_ >= ltvBps_ && liquidationThresholdBps_ <= MAX_LIQUIDATION_THRESHOLD_BPS,
+ "bad threshold"
+ );
+ require(minHealthFactorBps_ >= BPS, "bad health");
+ require(liquidationBonusBps_ <= MAX_LIQUIDATION_BONUS_BPS, "bad bonus");
+ ltvBps = ltvBps_;
+ liquidationThresholdBps = liquidationThresholdBps_;
+ minHealthFactorBps = minHealthFactorBps_;
+ liquidationBonusBps = liquidationBonusBps_;
+ maxBorrowUsdc = maxBorrowUsdc_;
+ emit RiskParamsUpdated(
+ ltvBps_, liquidationThresholdBps_, minHealthFactorBps_, liquidationBonusBps_, maxBorrowUsdc_
+ );
+ }
+
+ function _requireAccountingCollateralized() internal view {
+ require(xaut.balanceOf(address(this)) >= totalCollateralXaut, "xaut undercollateralized");
+ require(usdc.balanceOf(address(this)) >= lenderUsdcAvailable, "usdc undercollateralized");
+ }
+}
diff --git a/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md b/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md
index 569814d..795df11 100644
--- a/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md
+++ b/docs/deployment/MULTI_CHAIN_DEPLOYMENT_GUIDE.md
@@ -1,7 +1,7 @@
# 🌐 Multi-Chain Deployment Guide - Complete Package
**Version**: 1.0
-**Last Updated**: 2026-01-24
+**Last Updated**: 2026-05-09
**Status**: ✅ Foundation Complete - Ready for Expansion
---
@@ -80,7 +80,7 @@ This guide covers the complete deployment of the Universal Cross-Chain Asset Hub
- [ ] Hedera adapter + oracle service
- [ ] Tron adapter + oracle service
- [ ] TON adapter + oracle service
-- [ ] Cosmos adapter (IBC integration)
+- [ ] Cosmos adapter (IBC integration) — optional streams **A–E**: [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](../../../docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md), [`config/cosmos-chain138-optional/README.md`](../../../config/cosmos-chain138-optional/README.md)
- [ ] Solana adapter (Wormhole integration)
### **Phase 4: Hyperledger** ⚠️
diff --git a/docs/deployment/REMAINING_TASKS_COMPLETE_LIST.md b/docs/deployment/REMAINING_TASKS_COMPLETE_LIST.md
index 621c2bf..df9e77c 100644
--- a/docs/deployment/REMAINING_TASKS_COMPLETE_LIST.md
+++ b/docs/deployment/REMAINING_TASKS_COMPLETE_LIST.md
@@ -83,7 +83,7 @@
- [ ] **BRG-DEP-002**: Deploy WETH10 bridge to ChainID 138 - 2-3h
- [ ] **BRG-DEP-003**: Deploy LINK token to canonical address - 2-3h
- [ ] **BRG-DEP-004**: Deploy remaining EVM adapters (Polygon, Arbitrum, Optimism, Base, Avalanche, BSC, Ethereum) - 10-15h
-- [ ] **BRG-DEP-005**: Deploy remaining non-EVM adapters (Stellar, Algorand, Hedera, Tron, TON, Cosmos, Solana) - 20-30h
+- [ ] **BRG-DEP-005**: Deploy remaining non-EVM adapters (Stellar, Algorand, Hedera, Tron, TON, Cosmos, Solana) - 20-30h — **Cosmos:** optional streams A–E (not production IBC today): [COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md](../../../docs/11-references/COSMOS_ECOSYSTEM_CHAIN138_OPTIONAL_INTEGRATIONS_RUNBOOK.md), [`config/cosmos-chain138-optional/README.md`](../../../config/cosmos-chain138-optional/README.md)
- [ ] **BRG-DEP-006**: Deploy Hyperledger components (Cacti, Fabric, Indy) - 15-20h
---
diff --git a/metamask/verified-contracts.json b/metamask/verified-contracts.json
index d2c7d75..a21b2ab 100644
--- a/metamask/verified-contracts.json
+++ b/metamask/verified-contracts.json
@@ -5,63 +5,63 @@
"name": "WETH",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/tokens/WETH.sol"
},
{
"name": "Multicall",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/utils/Multicall.sol"
},
{
"name": "CREATE2Factory",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/utils/CREATE2Factory.sol"
},
{
"name": "Aggregator",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/oracle/Aggregator.sol"
},
{
"name": "CCIPRouter",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/ccip/CCIPRouter.sol"
},
{
"name": "CCIPSender",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/ccip/CCIPSender.sol"
},
{
"name": "CCIPReceiver",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/ccip/CCIPReceiver.sol"
},
{
"name": "Voting",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/governance/Voting.sol"
},
{
"name": "MultiSig",
"address": "0x0000000000000000000000000000000000000000",
"verified": false,
- "explorer": "https://explorer.d-bis.org/address/0x0000000000000000000000000000000000000000",
+ "explorer": "https://explorer.d-bis.org/addresses/0x0000000000000000000000000000000000000000",
"source": "contracts/governance/MultiSig.sol"
}
],
diff --git a/scripts/deployment/deploy-dapp-lxc.sh b/scripts/deployment/deploy-dapp-lxc.sh
index ab6ca76..3500117 100755
--- a/scripts/deployment/deploy-dapp-lxc.sh
+++ b/scripts/deployment/deploy-dapp-lxc.sh
@@ -3,10 +3,12 @@
# Usage: ./scripts/deployment/deploy-dapp-lxc.sh [--dry-run] [--skip-create]
# --dry-run Print commands only.
# --skip-create Use existing container 5801 (only install/build/serve).
-# Env: PROXMOX_HOST (optional; if unset, run pct on current host), NODE (optional; pct --node),
+# Env: PROXMOX_HOST (defaults from VMID via proxmox scripts/lib/load-project-env.sh get_host_for_vmid),
+# NODE (optional; only with DEPLOY_PCT_ON_LOCAL_PVE=1 on a PVE node: pct --node),
# VMID, HOSTNAME, IP_DAPP_LXC, TEMPLATE, STORAGE, NETWORK, MEMORY_MB, CORES, DISK_GB,
# REPO_URL (git URL to clone) or REPO_PATH (local path to copy; no git in container),
# ENV_FILE (path to .env for VITE_*).
+# DEPLOY_PCT_ON_LOCAL_PVE=1 — only on a Proxmox hypervisor: run pct locally (no SSH).
# See: docs/03-deployment/DAPP_LXC_DEPLOYMENT.md
set -euo pipefail
@@ -48,6 +50,16 @@ for a in "$@"; do
[[ "$a" == "--skip-create" ]] && SKIP_CREATE=true
done
+PROXMOX_MONOREPO_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
+if [[ -f "$PROXMOX_MONOREPO_ROOT/scripts/lib/require-proxmox-ssh-for-pct.sh" ]]; then
+ # shellcheck disable=SC1091
+ source "$PROXMOX_MONOREPO_ROOT/scripts/lib/require-proxmox-ssh-for-pct.sh"
+ require_proxmox_ssh_for_pct || exit 1
+else
+ echo "ERROR: Missing $PROXMOX_MONOREPO_ROOT/scripts/lib/require-proxmox-ssh-for-pct.sh (run from proxmox monorepo checkout)." >&2
+ exit 1
+fi
+
run_cmd() {
if [[ -n "$PROXMOX_HOST" ]]; then
ssh $SSH_OPTS root@"$PROXMOX_HOST" "$@"
@@ -76,7 +88,7 @@ echo ""
if ! $SKIP_CREATE; then
if $DRY_RUN; then
- echo "[DRY-RUN] Would create LXC $VMID on ${PROXMOX_HOST:-local} with hostname=$HOSTNAME, ip=$IP/24"
+ echo "[DRY-RUN] Would create LXC $VMID on ${PROXMOX_HOST:-this Proxmox node (local pct)} with hostname=$HOSTNAME, ip=$IP/24"
exit 0
fi
diff --git a/scripts/deployment/export-cronos-verification-sources.sh b/scripts/deployment/export-cronos-verification-sources.sh
index 50a7ebd..f3aa0f6 100755
--- a/scripts/deployment/export-cronos-verification-sources.sh
+++ b/scripts/deployment/export-cronos-verification-sources.sh
@@ -7,6 +7,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
OUT_DIR="$PROJECT_ROOT/.cronos-verify"
cd "$PROJECT_ROOT"
+# Cronos mainnet requires Paris EVM bytecode (no PUSH0). Match `foundry.toml` profile `cronos_legacy`.
+export FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-cronos_legacy}"
# Load .env via dotenv (RPC CR/LF trim). Fallback: raw source.
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
@@ -68,5 +70,7 @@ echo "Next: https://explorer.cronos.org/verifyContract"
echo " 1. Select 'Solidity (Standard-Json-Input)'"
echo " 2. Upload the *_standard_input.json file for each contract"
echo " 3. See docs/deployment/CRONOS_VERIFICATION_RUNBOOK.md"
+echo " 4. The verify form uses Google reCAPTCHA — Submit stays disabled until solved in the browser."
+echo " 5. Compiler picker may list 0.8.21+ only; Foundry uses solc 0.8.20 by default — pick the version that matches deploy bytecode if verification fails."
echo ""
echo "Sources: $OUT_DIR/"
diff --git a/services/token-aggregation/.env.example b/services/token-aggregation/.env.example
index aa53b48..5c37376 100644
--- a/services/token-aggregation/.env.example
+++ b/services/token-aggregation/.env.example
@@ -26,8 +26,8 @@ CUSDC_ADDRESS_138=0xf22258f57794CC8E06237084b353Ab30fFfa640b
# Compliant USD V2 (ERC-2612 / ERC-3009) — x402 / GRU transport.
# Reference: docs/04-configuration/CHAIN138_X402_TOKEN_SUPPORT.md
-CUSDT_V2_ADDRESS_138=0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29
-CUSDC_V2_ADDRESS_138=0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99
+CUSDT_V2_ADDRESS_138=0x9FBfab33882Efe0038DAa608185718b772EE5660
+CUSDC_V2_ADDRESS_138=0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d
# Live ALL Mainnet AUSDT compliant landing surface on Chain 138.
CAUSDT_ADDRESS_138=0x5fdDF65733e3d590463F68f93Cf16E8c04081271
diff --git a/services/token-aggregation/config/live-uniswap-v2-pool-catalog.json b/services/token-aggregation/config/live-uniswap-v2-pool-catalog.json
new file mode 100644
index 0000000..7716ea8
--- /dev/null
+++ b/services/token-aggregation/config/live-uniswap-v2-pool-catalog.json
@@ -0,0 +1,468 @@
+{
+ "schema": "token-aggregation-live-uniswap-v2-pool-catalog/v1",
+ "generatedAt": "2026-05-09T05:43:40.305Z",
+ "sourceArtifacts": [
+ "reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json",
+ "cross-chain-pmm-lps/config/deployment-status.json"
+ ],
+ "poolCount": 19,
+ "positiveLiquidityPoolCount": 19,
+ "pools": [
+ {
+ "chainId": 1,
+ "chainName": "Ethereum Mainnet",
+ "poolAddress": "0x422608c5ddff909675ac2c5f872fd42f16b9287a",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
+ "routerAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0xaf5017d0163ecb99d9b5d94e3b4d7b09af44d8ae",
+ "quoteAddress": "0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a",
+ "baseReserveRaw": "9900803423129",
+ "quoteReserveRaw": "9900803423131",
+ "baseReserveUnits": "9900803.423129",
+ "quoteReserveUnits": "9900803.423131",
+ "priceQuotePerBase": "1.000000000000202003808633131",
+ "deviationBps": "2.020038086331310000E-9",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 9900803.423129,
+ "reserve1Usd": 9900803.423131,
+ "totalLiquidityUsd": 19801606.84626
+ },
+ {
+ "chainId": 1,
+ "chainName": "Ethereum Mainnet",
+ "poolAddress": "0xc28706f899266b36bc43cc072b3a921bdf2c48d9",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
+ "routerAddress": "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
+ "baseSymbol": "cWUSDC",
+ "quoteSymbol": "USDC",
+ "baseAddress": "0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a",
+ "quoteAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ "baseReserveRaw": "1267071063797",
+ "quoteReserveRaw": "2167762",
+ "baseReserveUnits": "1267071.063797",
+ "quoteReserveUnits": "2.167762",
+ "priceQuotePerBase": "0.000001710844846779092339761738687",
+ "deviationBps": "9999.982891551532209076602383",
+ "depthOk": false,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1267071.063797,
+ "reserve1Usd": 2.167762,
+ "totalLiquidityUsd": 1267073.2315590002
+ },
+ {
+ "chainId": 10,
+ "chainName": "Optimism",
+ "poolAddress": "0xe28bff306442a8a512d2441847c27211a7c4c613",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x0c3c1c532F1e39EdF36BE9Fe0bE1410313E074Bf",
+ "routerAddress": "0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x04b2ae3c3bb3d70df506fad8717b0fbfc78ed7e6",
+ "quoteAddress": "0x377a5faa3162b3fc6f4e267301a3c817bad18105",
+ "baseReserveRaw": "3000000000",
+ "quoteReserveRaw": "3000000000",
+ "baseReserveUnits": "3000",
+ "quoteReserveUnits": "3000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 3000,
+ "reserve1Usd": 3000,
+ "totalLiquidityUsd": 6000
+ },
+ {
+ "chainId": 25,
+ "chainName": "Cronos",
+ "poolAddress": "0x438d8e1a8e311d2ae4b75a38e0044675fd324133",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x3B44B2a187a7b3824131F8db5a74194D0a42Fc15",
+ "routerAddress": "0x145863Eb42Cf62847A6Ca784e6416C1682b1b2Ae",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x72948a7a813b60b37cd0c920c4657dbff54312b8",
+ "quoteAddress": "0x932566e5bb6bebf6b035b94f3de1f75f126304ec",
+ "baseReserveRaw": "3000000000",
+ "quoteReserveRaw": "3000000000",
+ "baseReserveUnits": "3000",
+ "quoteReserveUnits": "3000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 3000,
+ "reserve1Usd": 3000,
+ "totalLiquidityUsd": 6000
+ },
+ {
+ "chainId": 56,
+ "chainName": "BSC (BNB Chain)",
+ "poolAddress": "0x639d7e64c6f1fc676226f20a0c42aecdd66545e8",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",
+ "routerAddress": "0x10ED43C718714eb63d5aA57B78B54704E256024E",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0xe1a51bc037a79ab36767561b147eb41780124934",
+ "quoteAddress": "0x5355148c4740fcc3d7a96f05edd89ab14851206b",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 56,
+ "chainName": "BSC (BNB Chain)",
+ "poolAddress": "0x7e308c12bd609607df9c4137e30235d5a9da2a64",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",
+ "routerAddress": "0x10ED43C718714eb63d5aA57B78B54704E256024E",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x9a1d0dbee997929ed02fd19e0e199704d20914db",
+ "quoteAddress": "0x5355148c4740fcc3d7a96f05edd89ab14851206b",
+ "baseReserveRaw": "3000000000",
+ "quoteReserveRaw": "3000000000",
+ "baseReserveUnits": "3000",
+ "quoteReserveUnits": "3000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 3000,
+ "reserve1Usd": 3000,
+ "totalLiquidityUsd": 6000
+ },
+ {
+ "chainId": 56,
+ "chainName": "BSC (BNB Chain)",
+ "poolAddress": "0xe9b082baa73fa4dec7cb3cbd99b19d30bbfe1523",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",
+ "routerAddress": "0x10ED43C718714eb63d5aA57B78B54704E256024E",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDT",
+ "baseAddress": "0xe1a51bc037a79ab36767561b147eb41780124934",
+ "quoteAddress": "0x9a1d0dbee997929ed02fd19e0e199704d20914db",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 100,
+ "chainName": "Gnosis Chain",
+ "poolAddress": "0x064d782be0113cb427f3af0de9335c9f34a1de34",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0xc35DADB65012eC5796536bD9864eD8773aBc74C4",
+ "routerAddress": "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x0cb0192c056aa425c557bdead8e56c7eeabf7acf",
+ "quoteAddress": "0xd6969bc19b53f866c64f2148ae271b2dae0c58e4",
+ "baseReserveRaw": "4000000000",
+ "quoteReserveRaw": "4000000000",
+ "baseReserveUnits": "4000",
+ "quoteReserveUnits": "4000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 4000,
+ "reserve1Usd": 4000,
+ "totalLiquidityUsd": 8000
+ },
+ {
+ "chainId": 137,
+ "chainName": "Polygon",
+ "poolAddress": "0x3411a20c39773d1a18cb53864893b236f41f1e99",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32",
+ "routerAddress": "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x0cb0192c056aa425c557bdead8e56c7eeabf7acf",
+ "quoteAddress": "0xd6969bc19b53f866c64f2148ae271b2dae0c58e4",
+ "baseReserveRaw": "10994302668",
+ "quoteReserveRaw": "10996392462",
+ "baseReserveUnits": "10994.302668",
+ "quoteReserveUnits": "10996.392462",
+ "priceQuotePerBase": "1.000190079722480494476414209",
+ "deviationBps": "1.900797224804944764142090000",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 10994.302668,
+ "reserve1Usd": 10996.392462,
+ "totalLiquidityUsd": 21990.69513
+ },
+ {
+ "chainId": 137,
+ "chainName": "Polygon",
+ "poolAddress": "0x8cd2cb42b81f894eb10d15446db22a3b31d6fb2e",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32",
+ "routerAddress": "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDT",
+ "baseAddress": "0xf12e262f85107df26741726b074606cafa24aae7",
+ "quoteAddress": "0x0cb0192c056aa425c557bdead8e56c7eeabf7acf",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 137,
+ "chainName": "Polygon",
+ "poolAddress": "0xe6a5cb164d4af7e9794aed09ec373392d0e7216c",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32",
+ "routerAddress": "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0xf12e262f85107df26741726b074606cafa24aae7",
+ "quoteAddress": "0xd6969bc19b53f866c64f2148ae271b2dae0c58e4",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 8453,
+ "chainName": "Base",
+ "poolAddress": "0x56eb93f747d3b8251d43849cc72b39c1899fcaca",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
+ "routerAddress": "0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x04b2ae3c3bb3d70df506fad8717b0fbfc78ed7e6",
+ "quoteAddress": "0x377a5faa3162b3fc6f4e267301a3c817bad18105",
+ "baseReserveRaw": "1000000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1000",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 1000,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2000
+ },
+ {
+ "chainId": 42161,
+ "chainName": "Arbitrum One",
+ "poolAddress": "0x2b2ea2ea9e7617de09fcb5063befafa01a9ef2b4",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
+ "routerAddress": "0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x73adaf7dba95221c080db5631466d2bc54f6a76b",
+ "quoteAddress": "0x0cb0192c056aa425c557bdead8e56c7eeabf7acf",
+ "baseReserveRaw": "3000000000",
+ "quoteReserveRaw": "3000000000",
+ "baseReserveUnits": "3000",
+ "quoteReserveUnits": "3000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 3000,
+ "reserve1Usd": 3000,
+ "totalLiquidityUsd": 6000
+ },
+ {
+ "chainId": 42220,
+ "chainName": "Celo",
+ "poolAddress": "0x6f97de8ab68c722dcbc02cea0ce6b587b8210052",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x62d5b84bE28a183aBB507E125B384122D2C25fAE",
+ "routerAddress": "0xE3D8bd6Aed4F159bc8000a9cD47CffDb95F96121",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x73376eb92c16977b126db9112936a20fa0de3442",
+ "quoteAddress": "0x4c38f9a5ed68a04cd28a72e8c68c459ec34576f3",
+ "baseReserveRaw": "1000000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1000",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 1000,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2000
+ },
+ {
+ "chainId": 42220,
+ "chainName": "Celo",
+ "poolAddress": "0xd3b55d6d7c08fdbf5f201e486992643cff410d91",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x62d5b84bE28a183aBB507E125B384122D2C25fAE",
+ "routerAddress": "0xE3D8bd6Aed4F159bc8000a9cD47CffDb95F96121",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0xc158b6cd3a3088c52f797d41f5aa02825361629e",
+ "quoteAddress": "0x4c38f9a5ed68a04cd28a72e8c68c459ec34576f3",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 42220,
+ "chainName": "Celo",
+ "poolAddress": "0xee9eebf89c1424e63efc888929e43a9423357d39",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x62d5b84bE28a183aBB507E125B384122D2C25fAE",
+ "routerAddress": "0xE3D8bd6Aed4F159bc8000a9cD47CffDb95F96121",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDT",
+ "baseAddress": "0xc158b6cd3a3088c52f797d41f5aa02825361629e",
+ "quoteAddress": "0x73376eb92c16977b126db9112936a20fa0de3442",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 43114,
+ "chainName": "Avalanche C-Chain",
+ "poolAddress": "0x418322f48d857277ec4bcc96bc1580accb7ea253",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
+ "routerAddress": "0x60aE616a2155Ee3d9A68541Ba4544862310933d4",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDT",
+ "baseAddress": "0xff3084410a732231472ee9f93f5855da89cc5254",
+ "quoteAddress": "0x8142ba530b08f3950128601f00daaa678213dfdf",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ },
+ {
+ "chainId": 43114,
+ "chainName": "Avalanche C-Chain",
+ "poolAddress": "0x79c8ea153e77bc69b989f59f69bfa44c466d5dee",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
+ "routerAddress": "0x60aE616a2155Ee3d9A68541Ba4544862310933d4",
+ "baseSymbol": "cWUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0x8142ba530b08f3950128601f00daaa678213dfdf",
+ "quoteAddress": "0x0c242b513008cd49c89078f5afb237a3112251eb",
+ "baseReserveRaw": "10000800000",
+ "quoteReserveRaw": "10000800000",
+ "baseReserveUnits": "10000.8",
+ "quoteReserveUnits": "10000.8",
+ "priceQuotePerBase": "1",
+ "deviationBps": "0",
+ "depthOk": true,
+ "parityOk": true,
+ "healthy": true,
+ "reserve0Usd": 10000.8,
+ "reserve1Usd": 10000.8,
+ "totalLiquidityUsd": 20001.6
+ },
+ {
+ "chainId": 43114,
+ "chainName": "Avalanche C-Chain",
+ "poolAddress": "0xaad6aed8d28b0195d19b4d17f8ee9a1837ff2dce",
+ "dex": "uniswap_v2",
+ "factoryAddress": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
+ "routerAddress": "0x60aE616a2155Ee3d9A68541Ba4544862310933d4",
+ "baseSymbol": "cWAUSDT",
+ "quoteSymbol": "cWUSDC",
+ "baseAddress": "0xff3084410a732231472ee9f93f5855da89cc5254",
+ "quoteAddress": "0x0c242b513008cd49c89078f5afb237a3112251eb",
+ "baseReserveRaw": "1500000000",
+ "quoteReserveRaw": "1000000000",
+ "baseReserveUnits": "1500",
+ "quoteReserveUnits": "1000",
+ "priceQuotePerBase": "0.6666666666666666666666666667",
+ "deviationBps": "3333.333333333333333333333333",
+ "depthOk": true,
+ "parityOk": false,
+ "healthy": false,
+ "reserve0Usd": 1500,
+ "reserve1Usd": 1000,
+ "totalLiquidityUsd": 2500
+ }
+ ]
+}
diff --git a/services/token-aggregation/config/supply-proof-catalog.json b/services/token-aggregation/config/supply-proof-catalog.json
new file mode 100644
index 0000000..96861f3
--- /dev/null
+++ b/services/token-aggregation/config/supply-proof-catalog.json
@@ -0,0 +1,2113 @@
+{
+ "schema": "token-aggregation-supply-proof-catalog/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "proofs": [
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 1,
+ "name": "Ethereum Mainnet",
+ "referenceBlock": 25054993
+ },
+ "token": {
+ "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "55047993783402955",
+ "totalSupplyUnits": "55047993783.402955"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "55047993783.402955",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 1,
+ "name": "Ethereum Mainnet",
+ "referenceBlock": 25054993
+ },
+ "token": {
+ "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USDT",
+ "decimals": 6,
+ "totalSupplyRaw": "97071875962553520",
+ "totalSupplyUnits": "97071875962.55352"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "97071875962.55352",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "mainnet-cwusdc-supply-proof/v1",
+ "generatedAt": "2026-05-08T03:16:54Z",
+ "network": {
+ "chainId": 1,
+ "name": "Ethereum Mainnet",
+ "referenceBlock": 25047586
+ },
+ "token": {
+ "address": "0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a",
+ "name": "Wrapped cUSDC",
+ "symbol": "cWUSDC",
+ "decimals": 6,
+ "verifiedSource": {
+ "etherscan": "https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a",
+ "sourcifyFullMatch": true,
+ "compiler": "0.8.20+commit.a1b79de6"
+ },
+ "totalSupplyRaw": "10451316981309788",
+ "totalSupplyUnits": "10451316981.309788"
+ },
+ "knownBalances": {
+ "operator": {
+ "address": "0x4A666F96fC8764181194447A7dFdb7d471b301C8",
+ "balanceRaw": "56772617595692",
+ "balanceUnits": "56772617.595692"
+ },
+ "engineXVirtualBatchVault": {
+ "address": "0xf108586d1FC330EA1D4EA4ff8fd983cde94279B1",
+ "balanceRaw": "0",
+ "balanceUnits": "0"
+ },
+ "uniswapV3CwusdcUsdcPool": {
+ "address": "0x1Cf2e685682C7F7beF508F0Af15Dfb5CDda01ee3",
+ "balanceRaw": "183290270",
+ "balanceUnits": "183.290270"
+ },
+ "uniswapV2CwusdcUsdcPair": {
+ "address": "0xC28706F899266b36BC43cc072b3a921BDf2C48D9",
+ "balanceRaw": "1266860669082",
+ "balanceUnits": "1266860.669082"
+ }
+ },
+ "trackerSurfaces": {
+ "etherscan": {
+ "status": "verified_contract_page_live",
+ "url": "https://etherscan.io/token/0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a"
+ },
+ "dexscreener": {
+ "status": "indexed",
+ "tokenUrl": "https://dexscreener.com/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a",
+ "pools": [
+ "https://dexscreener.com/ethereum/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3",
+ "https://dexscreener.com/ethereum/0xc28706f899266b36bc43cc072b3a921bdf2c48d9"
+ ]
+ },
+ "geckoTerminal": {
+ "status": "indexed",
+ "pools": [
+ {
+ "address": "0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3",
+ "url": "https://www.geckoterminal.com/eth/pools/0x1cf2e685682c7f7bef508f0af15dfb5cdda01ee3",
+ "reserveUsd": "179.1416",
+ "volume24hUsd": "90.6693898501"
+ },
+ {
+ "address": "0xc28706f899266b36bc43cc072b3a921bdf2c48d9",
+ "url": "https://www.geckoterminal.com/eth/pools/0xc28706f899266b36bc43cc072b3a921bdf2c48d9",
+ "reserveUsd": "4.3241",
+ "volume24hUsd": "114770.233085417"
+ }
+ ]
+ },
+ "coinMarketCapDex": {
+ "status": "token_page_reachable",
+ "url": "https://dex.coinmarketcap.com/token/ethereum/0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a/",
+ "note": "CMC DEX discoverability is not the same as full CoinMarketCap listing acceptance."
+ },
+ "coinGecko": {
+ "status": "not_listed_by_contract_api",
+ "note": "CoinGecko Ethereum contract lookup returned 404 during the 2026-05-08 readiness pass."
+ }
+ },
+ "circulatingSupplyMethodology": {
+ "status": "ready_for_tracker_review",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "10451316981.309788",
+ "notes": [
+ "No public tracker has accepted a circulating-supply value for this contract yet.",
+ "If a tracker requires exclusion of operator, treasury, bridge, or protocol-controlled balances, use the knownBalances section plus any additional signed treasury inventory supplied at submission time.",
+ "The value above is an on-chain supply proof, not a third-party listing approval."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 1,
+ "name": "Ethereum Mainnet",
+ "referenceBlock": 25054993
+ },
+ "token": {
+ "address": "0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "136106860161405",
+ "totalSupplyUnits": "136106860.161405"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "136106860.161405",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 10,
+ "name": "Optimism",
+ "referenceBlock": 151350237
+ },
+ "token": {
+ "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "216872376232530",
+ "totalSupplyUnits": "216872376.23253"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "216872376.23253",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 10,
+ "name": "Optimism",
+ "referenceBlock": 151350239
+ },
+ "token": {
+ "address": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USDT",
+ "decimals": 6,
+ "totalSupplyRaw": "201850485038093",
+ "totalSupplyUnits": "201850485.038093"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "201850485.038093",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 10,
+ "name": "Optimism",
+ "referenceBlock": 151350241
+ },
+ "token": {
+ "address": "0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "16001000000",
+ "totalSupplyUnits": "16001.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "16001.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 10,
+ "name": "Optimism",
+ "referenceBlock": 151350241
+ },
+ "token": {
+ "address": "0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "16001000000",
+ "totalSupplyUnits": "16001.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "16001.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 25,
+ "name": "Cronos",
+ "referenceBlock": 70089434
+ },
+ "token": {
+ "address": "0xc21223249CA28397B4B6541dfFaEcC539BfF0c59",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "179445548315541",
+ "totalSupplyUnits": "179445548.315541"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "179445548.315541",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 25,
+ "name": "Cronos",
+ "referenceBlock": 70089340
+ },
+ "token": {
+ "address": "0x66e428c3f67a68878562e79A0234c1F83c208770",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USDT",
+ "decimals": 6,
+ "totalSupplyRaw": "89024964111949",
+ "totalSupplyUnits": "89024964.111949"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "89024964.111949",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212275
+ },
+ "token": {
+ "address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 18,
+ "totalSupplyRaw": "1288999879439803987446539541",
+ "totalSupplyUnits": "1288999879.439803987446539541"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1288999879.439803987446539541",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212284
+ },
+ "token": {
+ "address": "0x55d398326f99059fF775485246999027B3197955",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USDT",
+ "decimals": 18,
+ "totalSupplyRaw": "9184992541113839747386612540",
+ "totalSupplyUnits": "9184992541.11383974738661254"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "9184992541.11383974738661254",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212289
+ },
+ "token": {
+ "address": "0xe1a51Bc037a79AB36767561B147eb41780124934",
+ "name": "Alltra USD Token (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWAUSDT",
+ "onchainName": "Wrapped cAUSDT",
+ "onchainSymbol": "cWAUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "362506000000",
+ "totalSupplyUnits": "362506.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "362506.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212291
+ },
+ "token": {
+ "address": "0x5355148C4740fcc3D7a96F05EdD89AB14851206b",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "1993283000000",
+ "totalSupplyUnits": "1993283.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1993283.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212294
+ },
+ "token": {
+ "address": "0x9a1D0dBEE997929ED02fD19E0E199704d20914dB",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "1993283000000",
+ "totalSupplyUnits": "1993283.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1993283.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 56,
+ "name": "BSC (BNB Chain)",
+ "referenceBlock": 97212298
+ },
+ "token": {
+ "address": "0xC2FA05F12a75Ac84ea778AF9D6935cA807275E55",
+ "name": "USD W (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDW",
+ "onchainName": "Wrapped cUSDW",
+ "onchainSymbol": "cWUSDW",
+ "decimals": 6,
+ "totalSupplyRaw": "361506000000",
+ "totalSupplyUnits": "361506.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "361506.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 100,
+ "name": "Gnosis Chain",
+ "referenceBlock": 46080039
+ },
+ "token": {
+ "address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD//C on xDai",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "18374759200634",
+ "totalSupplyUnits": "18374759.200634"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "18374759.200634",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 100,
+ "name": "Gnosis Chain",
+ "referenceBlock": 46080039
+ },
+ "token": {
+ "address": "0x4ECaBa5870353805a9F068101A40E0f32ed605C6",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD on xDai",
+ "onchainSymbol": "USDT",
+ "decimals": 6,
+ "totalSupplyRaw": "1055804706651",
+ "totalSupplyUnits": "1055804.706651"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1055804.706651",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 100,
+ "name": "Gnosis Chain",
+ "referenceBlock": 46080040
+ },
+ "token": {
+ "address": "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "15000000000",
+ "totalSupplyUnits": "15000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "15000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 100,
+ "name": "Gnosis Chain",
+ "referenceBlock": 46080041
+ },
+ "token": {
+ "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "15000000000",
+ "totalSupplyUnits": "15000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "15000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 137,
+ "name": "Polygon",
+ "referenceBlock": 86605454
+ },
+ "token": {
+ "address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "695940174693096",
+ "totalSupplyUnits": "695940174.693096"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "695940174.693096",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 137,
+ "name": "Polygon",
+ "referenceBlock": 86605456
+ },
+ "token": {
+ "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "USDT0",
+ "onchainSymbol": "USDT0",
+ "decimals": 6,
+ "totalSupplyRaw": "831909856146147",
+ "totalSupplyUnits": "831909856.146147"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "831909856.146147",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 137,
+ "name": "Polygon",
+ "referenceBlock": 86605457
+ },
+ "token": {
+ "address": "0xf12e262F85107df26741726b074606CaFa24AAe7",
+ "name": "Alltra USD Token (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWAUSDT",
+ "onchainName": "Wrapped cAUSDT",
+ "onchainSymbol": "cWAUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "3000000000",
+ "totalSupplyUnits": "3000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "3000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 137,
+ "name": "Polygon",
+ "referenceBlock": 86605458
+ },
+ "token": {
+ "address": "0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "22000000000",
+ "totalSupplyUnits": "22000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "22000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 137,
+ "name": "Polygon",
+ "referenceBlock": 86605459
+ },
+ "token": {
+ "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "21998313972",
+ "totalSupplyUnits": "21998.313972"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "21998.313972",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0xD51482e567c03899eecE3CAe8a058161FD56069D",
+ "name": "Australian Dollar (Compliant)",
+ "symbol": "cAUDC",
+ "onchainName": "Australian Dollar (Compliant)",
+ "onchainSymbol": "cAUDC",
+ "decimals": 6,
+ "totalSupplyRaw": "56804486580000",
+ "totalSupplyUnits": "56804486.58"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "56804486.58",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952086
+ },
+ "token": {
+ "address": "0x5fdDF65733e3d590463F68f93Cf16E8c04081271",
+ "name": "Alltra USD Token (Compliant)",
+ "symbol": "cAUSDT",
+ "onchainName": "Alltra USD Token (Compliant)",
+ "onchainSymbol": "cAUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "32707131000004",
+ "totalSupplyUnits": "32707131.000004"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "32707131.000004",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0x54dBd40cF05e15906A2C21f600937e96787f5679",
+ "name": "Canadian Dollar (Compliant)",
+ "symbol": "cCADC",
+ "onchainName": "Canadian Dollar (Compliant)",
+ "onchainSymbol": "cCADC",
+ "decimals": 6,
+ "totalSupplyRaw": "44871934240000",
+ "totalSupplyUnits": "44871934.24"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "44871934.24",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0x873990849DDa5117d7C644f0aF24370797C03885",
+ "name": "Swiss Franc (Compliant)",
+ "symbol": "cCHFC",
+ "onchainName": "Swiss Franc (Compliant)",
+ "onchainSymbol": "cCHFC",
+ "decimals": 6,
+ "totalSupplyRaw": "32918682790000",
+ "totalSupplyUnits": "32918682.79"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "32918682.79",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952094
+ },
+ "token": {
+ "address": "0x8085961F9cF02b4d800A3c6d386D31da4B34266a",
+ "name": "Euro Coin (Compliant)",
+ "symbol": "cEURC",
+ "onchainName": "Euro Coin (Compliant)",
+ "onchainSymbol": "cEURC",
+ "decimals": 6,
+ "totalSupplyRaw": "72706988330000",
+ "totalSupplyUnits": "72706988.33"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "72706988.33",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952094
+ },
+ "token": {
+ "address": "0xdf4b71c61E5912712C1Bdd451416B9aC26949d72",
+ "name": "Tether EUR (Compliant)",
+ "symbol": "cEURT",
+ "onchainName": "Tether EUR (Compliant)",
+ "onchainSymbol": "cEURT",
+ "decimals": 6,
+ "totalSupplyRaw": "71235330330000",
+ "totalSupplyUnits": "71235330.33"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "71235330.33",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952094
+ },
+ "token": {
+ "address": "0x003960f16D9d34F2e98d62723B6721Fb92074aD2",
+ "name": "Pound Sterling (Compliant)",
+ "symbol": "cGBPC",
+ "onchainName": "Pound Sterling (Compliant)",
+ "onchainSymbol": "cGBPC",
+ "decimals": 6,
+ "totalSupplyRaw": "34076968470000",
+ "totalSupplyUnits": "34076968.47"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "34076968.47",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0x350f54e4D23795f86A9c03988c7135357CCaD97c",
+ "name": "Tether GBP (Compliant)",
+ "symbol": "cGBPT",
+ "onchainName": "Tether GBP (Compliant)",
+ "onchainSymbol": "cGBPT",
+ "decimals": 6,
+ "totalSupplyRaw": "75066546470000",
+ "totalSupplyUnits": "75066546.47"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "75066546.47",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0xEe269e1226a334182aace90056EE4ee5Cc8A6770",
+ "name": "Japanese Yen (Compliant)",
+ "symbol": "cJPYC",
+ "onchainName": "Japanese Yen (Compliant)",
+ "onchainSymbol": "cJPYC",
+ "decimals": 6,
+ "totalSupplyRaw": "91977287100000",
+ "totalSupplyUnits": "91977287.1"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "91977287.1",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952084
+ },
+ "token": {
+ "address": "0xf22258f57794CC8E06237084b353Ab30fFfa640b",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin (Compliant)",
+ "onchainSymbol": "cUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "38601011267000000",
+ "totalSupplyUnits": "38601011267.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "38601011267.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952085
+ },
+ "token": {
+ "address": "0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d",
+ "name": "USD Coin (Compliant V2)",
+ "symbol": "cUSDC_V2",
+ "onchainName": "USD Coin (Compliant V2)",
+ "onchainSymbol": "cUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "380017953834",
+ "totalSupplyUnits": "380017.953834"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "380017.953834",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952085
+ },
+ "token": {
+ "address": "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD (Compliant)",
+ "onchainSymbol": "cUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "928784229000000",
+ "totalSupplyUnits": "928784229.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "928784229.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952086
+ },
+ "token": {
+ "address": "0x9FBfab33882Efe0038DAa608185718b772EE5660",
+ "name": "Tether USD (Compliant V2)",
+ "symbol": "cUSDT_V2",
+ "onchainName": "Tether USD (Compliant V2)",
+ "onchainSymbol": "cUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "530001062485",
+ "totalSupplyUnits": "530001.062485"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "530001.062485",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952086
+ },
+ "token": {
+ "address": "0xcA6BFa614935f1AB71c9aB106bAA6FBB6057095e",
+ "name": "USD W (Compliant)",
+ "symbol": "cUSDW",
+ "onchainName": "USDW (Compliant)",
+ "onchainSymbol": "cUSDW",
+ "decimals": 6,
+ "totalSupplyRaw": "0",
+ "totalSupplyUnits": "0.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "0.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0x290E52a8819A4fbD0714E517225429aA2B70EC6b",
+ "name": "Gold (Compliant)",
+ "symbol": "cXAUC",
+ "onchainName": "Gold (Compliant)",
+ "onchainSymbol": "cXAUC",
+ "decimals": 6,
+ "totalSupplyRaw": "1007568265651",
+ "totalSupplyUnits": "1007568.265651"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1007568.265651",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 138,
+ "name": "DeFi Oracle Meta Mainnet",
+ "referenceBlock": 4952095
+ },
+ "token": {
+ "address": "0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E",
+ "name": "Tether XAU (Compliant)",
+ "symbol": "cXAUT",
+ "onchainName": "Tether XAU (Compliant)",
+ "onchainSymbol": "cXAUT",
+ "decimals": 6,
+ "totalSupplyRaw": "1007568265651",
+ "totalSupplyUnits": "1007568.265651"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1007568.265651",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 1111,
+ "name": "Wemix",
+ "referenceBlock": 112065356
+ },
+ "token": {
+ "address": "0xE3F5a90F9cb311505cd691a46596599aA1A0AD7D",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "11014520532789",
+ "totalSupplyUnits": "11014520.532789"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "11014520.532789",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 1111,
+ "name": "Wemix",
+ "referenceBlock": 112065360
+ },
+ "token": {
+ "address": "0xA649325Aa7C5093d12D6F98EB4378deAe68CE23F",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USDT",
+ "decimals": 6,
+ "totalSupplyRaw": "168569642765",
+ "totalSupplyUnits": "168569.642765"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "168569.642765",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 8453,
+ "name": "Base",
+ "referenceBlock": 45754953
+ },
+ "token": {
+ "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "4374380611328711",
+ "totalSupplyUnits": "4374380611.328711"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "4374380611.328711",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 8453,
+ "name": "Base",
+ "referenceBlock": 45754955
+ },
+ "token": {
+ "address": "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "23301271161496",
+ "totalSupplyUnits": "23301271.161496"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "23301271.161496",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 8453,
+ "name": "Base",
+ "referenceBlock": 45754956
+ },
+ "token": {
+ "address": "0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "11000000000",
+ "totalSupplyUnits": "11000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "11000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 8453,
+ "name": "Base",
+ "referenceBlock": 45754957
+ },
+ "token": {
+ "address": "0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "11000000000",
+ "totalSupplyUnits": "11000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "11000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42161,
+ "name": "Arbitrum One",
+ "referenceBlock": 460893941
+ },
+ "token": {
+ "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "5732089638330937",
+ "totalSupplyUnits": "5732089638.330937"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "5732089638.330937",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42161,
+ "name": "Arbitrum One",
+ "referenceBlock": 460893954
+ },
+ "token": {
+ "address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "USD₮0",
+ "onchainSymbol": "USD₮0",
+ "decimals": 6,
+ "totalSupplyRaw": "995151040492349",
+ "totalSupplyUnits": "995151040.492349"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "995151040.492349",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42161,
+ "name": "Arbitrum One",
+ "referenceBlock": 460893966
+ },
+ "token": {
+ "address": "0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "13000000000",
+ "totalSupplyUnits": "13000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "13000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42161,
+ "name": "Arbitrum One",
+ "referenceBlock": 460893974
+ },
+ "token": {
+ "address": "0x73ADaF7dBa95221c080db5631466d2bC54f6a76B",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "13000000000",
+ "totalSupplyUnits": "13000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "13000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42220,
+ "name": "Celo",
+ "referenceBlock": 66398494
+ },
+ "token": {
+ "address": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USDC",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "19007517220000",
+ "totalSupplyUnits": "19007517.22"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "19007517.22",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42220,
+ "name": "Celo",
+ "referenceBlock": 66398497
+ },
+ "token": {
+ "address": "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Tether USD",
+ "onchainSymbol": "USD₮",
+ "decimals": 6,
+ "totalSupplyRaw": "470000000997000",
+ "totalSupplyUnits": "470000000.997"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "470000000.997",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42220,
+ "name": "Celo",
+ "referenceBlock": 66398498
+ },
+ "token": {
+ "address": "0xC158b6cD3A3088C52F797D41f5Aa02825361629e",
+ "name": "Alltra USD Token (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWAUSDT",
+ "onchainName": "Wrapped cAUSDT",
+ "onchainSymbol": "cWAUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "3000000000",
+ "totalSupplyUnits": "3000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "3000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42220,
+ "name": "Celo",
+ "referenceBlock": 66398500
+ },
+ "token": {
+ "address": "0x4C38F9A5ed68A04cd28a72E8c68C459Ec34576f3",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "13000000000",
+ "totalSupplyUnits": "13000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "13000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 42220,
+ "name": "Celo",
+ "referenceBlock": 66398502
+ },
+ "token": {
+ "address": "0x73376eB92c16977B126dB9112936A20Fa0De3442",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "13000000000",
+ "totalSupplyUnits": "13000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "13000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963312
+ },
+ "token": {
+ "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "USD Coin",
+ "onchainSymbol": "USDC",
+ "decimals": 6,
+ "totalSupplyRaw": "545865067327219",
+ "totalSupplyUnits": "545865067.327219"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "545865067.327219",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963315
+ },
+ "token": {
+ "address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "TetherToken",
+ "onchainSymbol": "USDt",
+ "decimals": 6,
+ "totalSupplyRaw": "1847205574047338",
+ "totalSupplyUnits": "1847205574.047338"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1847205574.047338",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963318
+ },
+ "token": {
+ "address": "0xff3084410A732231472Ee9f93F5855dA89CC5254",
+ "name": "Alltra USD Token (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWAUSDT",
+ "onchainName": "Wrapped cAUSDT",
+ "onchainSymbol": "cWAUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "362506000000",
+ "totalSupplyUnits": "362506.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "362506.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963318
+ },
+ "token": {
+ "address": "0x0C242b513008Cd49C89078F5aFb237A3112251EB",
+ "name": "USD Coin (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDC",
+ "onchainName": "Wrapped cUSDC",
+ "onchainSymbol": "cWUSDC",
+ "decimals": 6,
+ "totalSupplyRaw": "2000283800000",
+ "totalSupplyUnits": "2000283.8"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "2000283.8",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963321
+ },
+ "token": {
+ "address": "0x8142BA530B08f3950128601F00DaaA678213DFdf",
+ "name": "Tether USD (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDT",
+ "onchainName": "Wrapped cUSDT",
+ "onchainSymbol": "cWUSDT",
+ "decimals": 6,
+ "totalSupplyRaw": "2000286268227",
+ "totalSupplyUnits": "2000286.268227"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "2000286.268227",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 43114,
+ "name": "Avalanche C-Chain",
+ "referenceBlock": 84963321
+ },
+ "token": {
+ "address": "0xcfdCe5E660FC2C8052BDfa7aEa1865DD753411Ae",
+ "name": "USD W (Compliant Wrapped ISO-4217 M1)",
+ "symbol": "cWUSDW",
+ "onchainName": "Wrapped cUSDW",
+ "onchainSymbol": "cWUSDW",
+ "decimals": 6,
+ "totalSupplyRaw": "361506000000",
+ "totalSupplyUnits": "361506.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "361506.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 651940,
+ "name": "ALL Mainnet",
+ "referenceBlock": 33419742
+ },
+ "token": {
+ "address": "0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881",
+ "name": "USD Coin (Compliant)",
+ "symbol": "cUSDC",
+ "onchainName": "AUSDC",
+ "onchainSymbol": "USDC",
+ "decimals": 18,
+ "totalSupplyRaw": "1000000000000000000000000",
+ "totalSupplyUnits": "1000000.0"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "1000000.0",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ },
+ {
+ "schema": "token-aggregation-supply-proof/v1",
+ "generatedAt": "2026-05-09T04:00:51.739Z",
+ "network": {
+ "chainId": 651940,
+ "name": "ALL Mainnet",
+ "referenceBlock": 33419743
+ },
+ "token": {
+ "address": "0x015B1897Ed5279930bC2Be46F661894d219292A6",
+ "name": "Tether USD (Compliant)",
+ "symbol": "cUSDT",
+ "onchainName": "Alltra USD Token",
+ "onchainSymbol": "AUSDT",
+ "decimals": 18,
+ "totalSupplyRaw": "535053697365559770228576548644",
+ "totalSupplyUnits": "535053697365.559770228576548644"
+ },
+ "circulatingSupplyMethodology": {
+ "status": "onchain_total_supply_proved_circulating_review_required",
+ "recommendedFormula": "circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances",
+ "currentConservativeReportableCirculatingSupplyUnits": "535053697365.559770228576548644",
+ "notes": [
+ "totalSupply was read from the mapped token contract at the reference block.",
+ "circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.",
+ "Tracker acceptance is external and not implied by this API response."
+ ]
+ }
+ }
+ ],
+ "proofFailures": [
+ {
+ "chainId": 138,
+ "symbol": "cBTC",
+ "address": "0xcb7c000000000000000000000000000000000138",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cETH",
+ "address": "0xce7e00000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cETHL2",
+ "address": "0xce7200000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cBNB",
+ "address": "0xcb6b00000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cPOL",
+ "address": "0xc90100000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cAVAX",
+ "address": "0xcaaa00000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cCRO",
+ "address": "0xcc2000000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cXDAI",
+ "address": "0xcda100000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cCELO",
+ "address": "0xcce100000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 138,
+ "symbol": "cWEMIX",
+ "address": "0xc11100000000000000000000000000000000008a",
+ "reason": "https://rpc-http-pub.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc2.d-bis.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.defi-oracle.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 1,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000001",
+ "reason": "https://eth.llamarpc.com: missing revert data (action=\"call\", data=null, reason=null, transaction={ \"data\": \"0x18160ddd\", \"to\": \"0xCb7C000000000000000000000000000000000001\" }, invocation=null, revert=null, code=CALL_EXCEPTION, version=6.16.0) | https://rpc.ankr.com/eth: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0) | https://ethereum.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://1rpc.io/eth: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 10,
+ "symbol": "cWBTC",
+ "address": "0xcb7c00000000000000000000000000000000000a",
+ "reason": "https://mainnet.optimism.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://optimism.llamarpc.com: getaddrinfo ENOTFOUND optimism.llamarpc.com"
+ },
+ {
+ "chainId": 25,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000019",
+ "reason": "https://evm.cronos.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://cronos-rpc.publicnode.com: could not coalesce error (error={ \"code\": -32601, \"message\": \"Method not found\" }, payload={ \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] }, code=UNKNOWN_ERROR, version=6.16.0)"
+ },
+ {
+ "chainId": 56,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000038",
+ "reason": "https://bsc-dataseed.binance.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://bsc-dataseed1.defibit.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 100,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000064",
+ "reason": "https://rpc.gnosischain.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.ankr.com/gnosis: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 137,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000089",
+ "reason": "https://polygon-bor-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://1rpc.io/matic: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://polygon.drpc.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://polygon-rpc.com: server response 401 Unauthorized (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-rpc.com\", \"responseBody\": \"{\\\"error\\\":\\\"message: API key disabled, reason: tenant disabled, json-rpc code: -32051, rest code: 403\\\"}\", \"responseStatus\": \"401 Unauthorized\" }, code=SERVER_ERROR, version=6.16.0) | https://rpc.ankr.com/polygon: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 1111,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000000457",
+ "reason": "https://api.wemix.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://wemix-rpc.publicnode.com: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://wemix-rpc.publicnode.com\", \"responseBody\": null, \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.16.0)"
+ },
+ {
+ "chainId": 8453,
+ "symbol": "cWBTC",
+ "address": "0xcb7c000000000000000000000000000000002105",
+ "reason": "https://mainnet.base.org: missing revert data (action=\"call\", data=null, reason=null, transaction={ \"data\": \"0x18160ddd\", \"to\": \"0xcB7c000000000000000000000000000000002105\" }, invocation=null, revert=null, code=CALL_EXCEPTION, version=6.16.0) | https://base.llamarpc.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 42161,
+ "symbol": "cWBTC",
+ "address": "0xcb7c00000000000000000000000000000000a4b1",
+ "reason": "https://arb1.arbitrum.io/rpc: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://arbitrum.llamarpc.com: getaddrinfo ENOTFOUND arbitrum.llamarpc.com"
+ },
+ {
+ "chainId": 42220,
+ "symbol": "cWBTC",
+ "address": "0xcb7c00000000000000000000000000000000a4ec",
+ "reason": "https://forno.celo.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://celo-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 43114,
+ "symbol": "cWBTC",
+ "address": "0xcb7c00000000000000000000000000000000a86a",
+ "reason": "https://api.avax.network/ext/bc/C/rpc: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://avalanche-c-chain-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 1,
+ "symbol": "cWETH",
+ "address": "0xce7e000000000000000000000000000000000001",
+ "reason": "https://eth.llamarpc.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.ankr.com/eth: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0) | https://ethereum.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://1rpc.io/eth: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 10,
+ "symbol": "cWETHL2",
+ "address": "0xce7200000000000000000000000000000000000a",
+ "reason": "https://mainnet.optimism.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://optimism.llamarpc.com: getaddrinfo ENOTFOUND optimism.llamarpc.com"
+ },
+ {
+ "chainId": 8453,
+ "symbol": "cWETHL2",
+ "address": "0xce72000000000000000000000000000000002105",
+ "reason": "https://mainnet.base.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://base.llamarpc.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 42161,
+ "symbol": "cWETHL2",
+ "address": "0xce7200000000000000000000000000000000a4b1",
+ "reason": "https://arb1.arbitrum.io/rpc: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://arbitrum.llamarpc.com: getaddrinfo ENOTFOUND arbitrum.llamarpc.com"
+ },
+ {
+ "chainId": 56,
+ "symbol": "cWBNB",
+ "address": "0xcb6b000000000000000000000000000000000038",
+ "reason": "https://bsc-dataseed.binance.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://bsc-dataseed1.defibit.io: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 137,
+ "symbol": "cWPOL",
+ "address": "0xc901000000000000000000000000000000000089",
+ "reason": "https://polygon-bor-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://1rpc.io/matic: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://polygon.drpc.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://polygon-rpc.com: server response 401 Unauthorized (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-rpc.com\", \"responseBody\": \"{\\\"error\\\":\\\"message: API key disabled, reason: tenant disabled, json-rpc code: -32051, rest code: 403\\\"}\", \"responseStatus\": \"401 Unauthorized\" }, code=SERVER_ERROR, version=6.16.0) | https://rpc.ankr.com/polygon: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 43114,
+ "symbol": "cWAVAX",
+ "address": "0xcaaa00000000000000000000000000000000a86a",
+ "reason": "https://api.avax.network/ext/bc/C/rpc: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://avalanche-c-chain-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 25,
+ "symbol": "cWCRO",
+ "address": "0xcc20000000000000000000000000000000000019",
+ "reason": "https://evm.cronos.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://cronos-rpc.publicnode.com: could not coalesce error (error={ \"code\": -32601, \"message\": \"Method not found\" }, payload={ \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] }, code=UNKNOWN_ERROR, version=6.16.0)"
+ },
+ {
+ "chainId": 100,
+ "symbol": "cWXDAI",
+ "address": "0xcda1000000000000000000000000000000000064",
+ "reason": "https://rpc.gnosischain.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://rpc.ankr.com/gnosis: missing response for request (value=[ { \"error\": { \"code\": -32000, \"message\": \"Unauthorized: You must authenticate your request with an API key. Create an account on https://www.ankr.com/rpc/ and generate your personal API key for free.\" }, \"id\": null, \"jsonrpc\": \"2.0\" } ], info={ \"payload\": { \"id\": 1, \"jsonrpc\": \"2.0\", \"method\": \"eth_blockNumber\", \"params\": [ ] } }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 42220,
+ "symbol": "cWCELO",
+ "address": "0xcce100000000000000000000000000000000a4ec",
+ "reason": "https://forno.celo.org: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://celo-rpc.publicnode.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0)"
+ },
+ {
+ "chainId": 1111,
+ "symbol": "cWWEMIX",
+ "address": "0xc111000000000000000000000000000000000457",
+ "reason": "https://api.wemix.com: could not decode result data (value=\"0x\", info={ \"method\": \"totalSupply\", \"signature\": \"totalSupply()\" }, code=BAD_DATA, version=6.16.0) | https://wemix-rpc.publicnode.com: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://wemix-rpc.publicnode.com\", \"responseBody\": null, \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.16.0)"
+ }
+ ]
+}
diff --git a/services/token-aggregation/deploy-to-vmid.sh b/services/token-aggregation/deploy-to-vmid.sh
index d42a21c..683c868 100755
--- a/services/token-aggregation/deploy-to-vmid.sh
+++ b/services/token-aggregation/deploy-to-vmid.sh
@@ -26,8 +26,8 @@ log_ok "Built"
# Package
log_info "Creating package..."
-PACKAGE_ITEMS=(dist src package.json package-lock.json tsconfig.json scripts)
-for optional in .env.example .env; do
+PACKAGE_ITEMS=(dist src config package.json package-lock.json tsconfig.json scripts)
+for optional in public docs frontend .env.example .env; do
[ -e "$SCRIPT_DIR/$optional" ] && PACKAGE_ITEMS+=("$optional")
done
(cd "$SCRIPT_DIR" && tar czf /tmp/token-agg.tar.gz --exclude=node_modules "${PACKAGE_ITEMS[@]}")
diff --git a/services/token-aggregation/package-lock.json b/services/token-aggregation/package-lock.json
index 1bed3a3..ee03c4a 100644
--- a/services/token-aggregation/package-lock.json
+++ b/services/token-aggregation/package-lock.json
@@ -15,7 +15,7 @@
"dotenv": "^16.6.1",
"ethers": "^6.16.0",
"express": "^5.1.0",
- "express-rate-limit": "^8.4.1",
+ "express-rate-limit": "^8.5.1",
"jsonwebtoken": "^9.0.3",
"node-cron": "^4.2.1",
"pg": "^8.18.0",
@@ -3309,12 +3309,12 @@
}
},
"node_modules/express-rate-limit": {
- "version": "8.4.1",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz",
- "integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==",
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.1.tgz",
+ "integrity": "sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==",
"license": "MIT",
"dependencies": {
- "ip-address": "10.1.0"
+ "ip-address": "^10.2.0"
},
"engines": {
"node": ">= 16"
@@ -3941,9 +3941,9 @@
"license": "ISC"
},
"node_modules/ip-address": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
- "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
+ "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"license": "MIT",
"engines": {
"node": ">= 12"
diff --git a/services/token-aggregation/package.json b/services/token-aggregation/package.json
index 066fc2e..1e64921 100644
--- a/services/token-aggregation/package.json
+++ b/services/token-aggregation/package.json
@@ -13,6 +13,8 @@
"test:omnl": "jest --runInBand --testPathPattern=omnl",
"lint": "eslint src --ext .ts",
"backfill:historical-pricing": "node dist/backfill-historical-pricing.js",
+ "generate:supply-proof-catalog": "ts-node scripts/generate-supply-proof-catalog.ts",
+ "generate:live-uniswap-v2-pool-catalog": "ts-node scripts/generate-live-uniswap-v2-pool-catalog.ts",
"generate:route-matrix:v2": "ts-node scripts/generate-route-matrix-v2.ts",
"migrate": "node -r dotenv/config dist/database/migrations.js",
"example:partner-payloads": "node scripts/resolve-partner-payloads-example.mjs",
@@ -26,7 +28,7 @@
"dotenv": "^16.6.1",
"ethers": "^6.16.0",
"express": "^5.1.0",
- "express-rate-limit": "^8.4.1",
+ "express-rate-limit": "^8.5.1",
"jsonwebtoken": "^9.0.3",
"node-cron": "^4.2.1",
"pg": "^8.18.0",
diff --git a/services/token-aggregation/public/token-logos/gru/AVAX.svg b/services/token-aggregation/public/token-logos/gru/AVAX.svg
new file mode 100644
index 0000000..610093f
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/AVAX.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/BNB.svg b/services/token-aggregation/public/token-logos/gru/BNB.svg
new file mode 100644
index 0000000..fdb806d
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/BNB.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/CELO.svg b/services/token-aggregation/public/token-logos/gru/CELO.svg
new file mode 100644
index 0000000..31805ae
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/CELO.svg
@@ -0,0 +1,7 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/CRO.svg b/services/token-aggregation/public/token-logos/gru/CRO.svg
new file mode 100644
index 0000000..b2c0abd
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/CRO.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/ETH.svg b/services/token-aggregation/public/token-logos/gru/ETH.svg
new file mode 100644
index 0000000..096de40
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/ETH.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/LINK.svg b/services/token-aggregation/public/token-logos/gru/LINK.svg
new file mode 100644
index 0000000..b7d60a2
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/LINK.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/POL.svg b/services/token-aggregation/public/token-logos/gru/POL.svg
new file mode 100644
index 0000000..7c5d2b1
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/POL.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/WEMIX.svg b/services/token-aggregation/public/token-logos/gru/WEMIX.svg
new file mode 100644
index 0000000..3f910c9
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/WEMIX.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/XDAI.svg b/services/token-aggregation/public/token-logos/gru/XDAI.svg
new file mode 100644
index 0000000..4ab833a
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/XDAI.svg
@@ -0,0 +1,6 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cAUDC.svg b/services/token-aggregation/public/token-logos/gru/cAUDC.svg
new file mode 100644
index 0000000..e9c73cd
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cAUDC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cCADC.svg b/services/token-aggregation/public/token-logos/gru/cCADC.svg
new file mode 100644
index 0000000..ed91cb1
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cCADC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cCHFC.svg b/services/token-aggregation/public/token-logos/gru/cCHFC.svg
new file mode 100644
index 0000000..4d4d53f
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cCHFC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cEURC.svg b/services/token-aggregation/public/token-logos/gru/cEURC.svg
new file mode 100644
index 0000000..63f5066
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cEURC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cEURT.svg b/services/token-aggregation/public/token-logos/gru/cEURT.svg
new file mode 100644
index 0000000..a47ae61
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cEURT.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cGBPC.svg b/services/token-aggregation/public/token-logos/gru/cGBPC.svg
new file mode 100644
index 0000000..92e4833
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cGBPC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cGBPT.svg b/services/token-aggregation/public/token-logos/gru/cGBPT.svg
new file mode 100644
index 0000000..176992f
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cGBPT.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cJPYC.svg b/services/token-aggregation/public/token-logos/gru/cJPYC.svg
new file mode 100644
index 0000000..46cc514
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cJPYC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cUSDC.svg b/services/token-aggregation/public/token-logos/gru/cUSDC.svg
new file mode 100644
index 0000000..b7cac38
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cUSDC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cUSDT.svg b/services/token-aggregation/public/token-logos/gru/cUSDT.svg
new file mode 100644
index 0000000..3a27a1e
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cUSDT.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cXAUC.svg b/services/token-aggregation/public/token-logos/gru/cXAUC.svg
new file mode 100644
index 0000000..733f40b
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cXAUC.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/cXAUT.svg b/services/token-aggregation/public/token-logos/gru/cXAUT.svg
new file mode 100644
index 0000000..eaf5a9e
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/cXAUT.svg
@@ -0,0 +1,23 @@
+
diff --git a/services/token-aggregation/public/token-logos/gru/chain-138.svg b/services/token-aggregation/public/token-logos/gru/chain-138.svg
new file mode 100644
index 0000000..6d7fc7b
--- /dev/null
+++ b/services/token-aggregation/public/token-logos/gru/chain-138.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/services/token-aggregation/public/token-logos/ipfs/QmPh16PY241zNtePyeK7ep1uf1RcARV2ynGAuRU8U7sSqS b/services/token-aggregation/public/token-logos/ipfs/QmPh16PY241zNtePyeK7ep1uf1RcARV2ynGAuRU8U7sSqS
new file mode 100644
index 0000000..389411d
Binary files /dev/null and b/services/token-aggregation/public/token-logos/ipfs/QmPh16PY241zNtePyeK7ep1uf1RcARV2ynGAuRU8U7sSqS differ
diff --git a/services/token-aggregation/public/token-logos/ipfs/QmT2nJ6WyhYBCsYJ6NfS1BPAqiGKkCEuMxiC8ye93Co1hF b/services/token-aggregation/public/token-logos/ipfs/QmT2nJ6WyhYBCsYJ6NfS1BPAqiGKkCEuMxiC8ye93Co1hF
new file mode 100644
index 0000000..3d6ecf9
Binary files /dev/null and b/services/token-aggregation/public/token-logos/ipfs/QmT2nJ6WyhYBCsYJ6NfS1BPAqiGKkCEuMxiC8ye93Co1hF differ
diff --git a/services/token-aggregation/public/token-logos/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong b/services/token-aggregation/public/token-logos/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong
new file mode 100644
index 0000000..59d0980
Binary files /dev/null and b/services/token-aggregation/public/token-logos/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong differ
diff --git a/services/token-aggregation/public/token-logos/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K b/services/token-aggregation/public/token-logos/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K
new file mode 100644
index 0000000..fb0af0e
Binary files /dev/null and b/services/token-aggregation/public/token-logos/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K differ
diff --git a/services/token-aggregation/public/token-logos/trustwallet/btc-logo.png b/services/token-aggregation/public/token-logos/trustwallet/btc-logo.png
new file mode 100644
index 0000000..335d6b7
Binary files /dev/null and b/services/token-aggregation/public/token-logos/trustwallet/btc-logo.png differ
diff --git a/services/token-aggregation/scripts/generate-live-uniswap-v2-pool-catalog.ts b/services/token-aggregation/scripts/generate-live-uniswap-v2-pool-catalog.ts
new file mode 100644
index 0000000..ed290b3
--- /dev/null
+++ b/services/token-aggregation/scripts/generate-live-uniswap-v2-pool-catalog.ts
@@ -0,0 +1,163 @@
+import fs from 'fs';
+import path from 'path';
+
+type DiscoveryPayload = {
+ generated_at?: string;
+ entries?: Array<{
+ chain_id: number;
+ network?: string;
+ factoryAddress?: string;
+ routerAddress?: string;
+ pairsChecked?: Array<{
+ base: string;
+ quote: string;
+ poolAddress: string;
+ live: boolean;
+ health?: {
+ baseReserveRaw?: string;
+ quoteReserveRaw?: string;
+ baseReserveUnits?: string;
+ quoteReserveUnits?: string;
+ priceQuotePerBase?: string;
+ deviationBps?: string;
+ healthy?: boolean;
+ depthOk?: boolean;
+ parityOk?: boolean;
+ };
+ }>;
+ }>;
+};
+
+type DeploymentStatusChain = {
+ name?: string;
+ cwTokens?: Record;
+ anchorAddresses?: Record;
+ gasMirrors?: Record;
+ gasQuoteAddresses?: Record;
+};
+
+type DeploymentStatus = {
+ chains?: Record;
+};
+
+const PRICE_USD: Record = {
+ USDC: 1,
+ USDT: 1,
+ cUSDC: 1,
+ cUSDT: 1,
+ cWUSDC: 1,
+ cWUSDT: 1,
+ cWAUSDT: 1,
+};
+
+function findRepoRoot(): string {
+ const candidates = [
+ process.env.PROXMOX_REPO_ROOT,
+ process.env.PROJECT_ROOT,
+ path.resolve(process.cwd(), '..', '..', '..'),
+ path.resolve(process.cwd(), '..'),
+ process.cwd(),
+ ].filter(Boolean) as string[];
+
+ for (const candidate of candidates) {
+ if (fs.existsSync(path.join(candidate, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json'))) {
+ return candidate;
+ }
+ }
+ return path.resolve(process.cwd(), '..', '..', '..');
+}
+
+function readJson(filePath: string): T {
+ return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;
+}
+
+function resolveTokenAddress(chain: DeploymentStatusChain, symbol: string): string | undefined {
+ return (
+ chain.cwTokens?.[symbol] ||
+ chain.anchorAddresses?.[symbol] ||
+ chain.gasMirrors?.[symbol] ||
+ chain.gasQuoteAddresses?.[symbol]
+ );
+}
+
+function parsePositiveNumber(value?: string): number {
+ const parsed = Number(value);
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
+}
+
+function main(): void {
+ const repoRoot = findRepoRoot();
+ const discoveryPath = path.join(repoRoot, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json');
+ const deploymentStatusPath = path.join(repoRoot, 'cross-chain-pmm-lps/config/deployment-status.json');
+ const outPath = path.join(process.cwd(), 'config/live-uniswap-v2-pool-catalog.json');
+
+ const discovery = readJson(discoveryPath);
+ const deploymentStatus = readJson(deploymentStatusPath);
+ const pools = [];
+
+ for (const entry of discovery.entries ?? []) {
+ const chainId = Number(entry.chain_id);
+ const chain = deploymentStatus.chains?.[String(chainId)];
+ if (!chain) continue;
+
+ for (const pair of entry.pairsChecked ?? []) {
+ if (!pair.live || !pair.poolAddress?.startsWith('0x') || !pair.health) continue;
+ const baseAddress = resolveTokenAddress(chain, pair.base);
+ const quoteAddress = resolveTokenAddress(chain, pair.quote);
+ if (!baseAddress || !quoteAddress) continue;
+
+ const baseReserveUnits = parsePositiveNumber(pair.health.baseReserveUnits);
+ const quoteReserveUnits = parsePositiveNumber(pair.health.quoteReserveUnits);
+ const basePriceUsd = PRICE_USD[pair.base] ?? 0;
+ const quotePriceUsd = PRICE_USD[pair.quote] ?? 0;
+ const reserve0Usd = baseReserveUnits * basePriceUsd;
+ const reserve1Usd = quoteReserveUnits * quotePriceUsd;
+ const totalLiquidityUsd = reserve0Usd + reserve1Usd;
+ if (totalLiquidityUsd <= 0) continue;
+
+ pools.push({
+ chainId,
+ chainName: entry.network ?? chain.name ?? `Chain ${chainId}`,
+ poolAddress: pair.poolAddress.toLowerCase(),
+ dex: 'uniswap_v2',
+ factoryAddress: entry.factoryAddress,
+ routerAddress: entry.routerAddress,
+ baseSymbol: pair.base,
+ quoteSymbol: pair.quote,
+ baseAddress: baseAddress.toLowerCase(),
+ quoteAddress: quoteAddress.toLowerCase(),
+ baseReserveRaw: pair.health.baseReserveRaw,
+ quoteReserveRaw: pair.health.quoteReserveRaw,
+ baseReserveUnits: pair.health.baseReserveUnits,
+ quoteReserveUnits: pair.health.quoteReserveUnits,
+ priceQuotePerBase: pair.health.priceQuotePerBase,
+ deviationBps: pair.health.deviationBps,
+ depthOk: pair.health.depthOk,
+ parityOk: pair.health.parityOk,
+ healthy: pair.health.healthy,
+ reserve0Usd,
+ reserve1Usd,
+ totalLiquidityUsd,
+ });
+ }
+ }
+
+ pools.sort((a, b) => a.chainId - b.chainId || a.poolAddress.localeCompare(b.poolAddress));
+ const payload = {
+ schema: 'token-aggregation-live-uniswap-v2-pool-catalog/v1',
+ generatedAt: new Date().toISOString(),
+ sourceArtifacts: [
+ 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json',
+ 'cross-chain-pmm-lps/config/deployment-status.json',
+ ],
+ poolCount: pools.length,
+ positiveLiquidityPoolCount: pools.filter((pool) => pool.totalLiquidityUsd > 0).length,
+ pools,
+ };
+
+ fs.mkdirSync(path.dirname(outPath), { recursive: true });
+ fs.writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}\n`);
+ console.log(outPath);
+}
+
+main();
diff --git a/services/token-aggregation/scripts/generate-supply-proof-catalog.ts b/services/token-aggregation/scripts/generate-supply-proof-catalog.ts
new file mode 100644
index 0000000..c54c299
--- /dev/null
+++ b/services/token-aggregation/scripts/generate-supply-proof-catalog.ts
@@ -0,0 +1,238 @@
+#!/usr/bin/env ts-node
+
+import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
+import path from 'path';
+import { Contract, JsonRpcProvider, formatUnits } from 'ethers';
+import { CANONICAL_TOKENS, getTokenRegistryFamily } from '../src/config/canonical-tokens';
+import { getChainConfig } from '../src/config/chains';
+import { NETWORKS } from '../src/config/networks';
+
+const ERC20_ABI = [
+ 'function totalSupply() view returns (uint256)',
+ 'function decimals() view returns (uint8)',
+ 'function symbol() view returns (string)',
+ 'function name() view returns (string)',
+];
+
+type SupplyProof = {
+ schema: string;
+ generatedAt: string;
+ network: {
+ chainId: number;
+ name: string;
+ referenceBlock: number;
+ };
+ token: {
+ address: string;
+ name: string;
+ symbol: string;
+ onchainName?: string;
+ onchainSymbol?: string;
+ decimals: number;
+ totalSupplyRaw: string;
+ totalSupplyUnits: string;
+ };
+ circulatingSupplyMethodology: {
+ status: string;
+ recommendedFormula: string;
+ currentConservativeReportableCirculatingSupplyUnits: string;
+ notes: string[];
+ };
+ knownBalances?: unknown;
+ trackerSurfaces?: unknown;
+ [key: string]: unknown;
+};
+
+type SupplyProofCatalog = {
+ schema: string;
+ generatedAt: string;
+ proofs: SupplyProof[];
+ proofFailures?: Array<{
+ chainId: number;
+ symbol: string;
+ address: string;
+ reason: string;
+ }>;
+};
+
+function isCandidate(symbol: string, type?: string, registryFamily?: string): boolean {
+ return (
+ /^c[A-Z0-9]/.test(symbol) &&
+ (type === 'base' ||
+ type === 'c' ||
+ type === 'w' ||
+ ['iso4217', 'monetary_unit', 'gas_native', 'commodity'].includes(String(registryFamily || '')))
+ );
+}
+
+function catalogKey(chainId: number, address: string): string {
+ return `${chainId}:${address.toLowerCase()}`;
+}
+
+function rpcUrlsForChain(chainId: number, primary?: string): string[] {
+ const network = NETWORKS.find((row) => row.chainIdDecimal === chainId);
+ return Array.from(new Set([primary, ...(network?.rpcUrls ?? [])].filter(Boolean) as string[]));
+}
+
+function loadProofFile(proofPath: string): SupplyProof[] {
+ if (!existsSync(proofPath)) return [];
+ const parsed = JSON.parse(readFileSync(proofPath, 'utf8')) as Partial | SupplyProof;
+ const proofs = Array.isArray((parsed as Partial).proofs)
+ ? ((parsed as SupplyProofCatalog).proofs)
+ : [parsed as SupplyProof];
+ return proofs.filter((proof) => proof?.network?.chainId && proof?.token?.address);
+}
+
+function loadExistingCatalog(seedPaths: string[]): Map {
+ const proofs = new Map();
+ for (const seedPath of seedPaths) {
+ for (const proof of loadProofFile(seedPath)) {
+ proofs.set(catalogKey(proof.network.chainId, proof.token.address), proof);
+ }
+ }
+ return proofs;
+}
+
+function shouldPreserveExistingProof(proof: SupplyProof | undefined): boolean {
+ if (!proof) return false;
+ return (
+ proof.knownBalances !== undefined ||
+ proof.trackerSurfaces !== undefined ||
+ proof.circulatingSupplyMethodology?.status === 'ready_for_tracker_review'
+ );
+}
+
+async function withTimeout(promise: Promise, timeoutMs: number, label: string): Promise {
+ let timeout: NodeJS.Timeout | undefined;
+ const timer = new Promise((_, reject) => {
+ timeout = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
+ });
+ try {
+ return await Promise.race([promise, timer]);
+ } finally {
+ if (timeout) clearTimeout(timeout);
+ }
+}
+
+async function main() {
+ const serviceRoot = path.resolve(__dirname, '..');
+ const catalogPath = path.resolve(
+ process.env.TOKEN_AGGREGATION_SUPPLY_PROOF_CATALOG_JSON ||
+ path.join(serviceRoot, 'config/supply-proof-catalog.json')
+ );
+ const repoRoot = path.resolve(serviceRoot, '..', '..', '..');
+ const timeoutMs = Number(process.env.SUPPLY_PROOF_RPC_TIMEOUT_MS || 12000);
+ const generatedAt = new Date().toISOString();
+ const existing = loadExistingCatalog([
+ catalogPath,
+ path.join(repoRoot, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'),
+ ]);
+ const proofs = new Map(existing);
+ const failures: SupplyProofCatalog['proofFailures'] = [];
+
+ for (const spec of CANONICAL_TOKENS) {
+ const registryFamily = getTokenRegistryFamily(spec);
+ if (!isCandidate(spec.symbol, spec.type, registryFamily)) continue;
+
+ for (const [chainIdText, rawAddress] of Object.entries(spec.addresses)) {
+ const chainId = Number(chainIdText);
+ const address = String(rawAddress || '').trim();
+ if (!address.startsWith('0x')) continue;
+ const chain = getChainConfig(chainId);
+ if (!chain?.rpcUrl) {
+ failures.push({ chainId, symbol: spec.symbol, address, reason: 'missing_rpc_url' });
+ continue;
+ }
+
+ let proved = false;
+ const reasons: string[] = [];
+ for (const rpcUrl of rpcUrlsForChain(chainId, chain.rpcUrl)) {
+ try {
+ const provider = new JsonRpcProvider(rpcUrl, chainId, { staticNetwork: true });
+ const contract = new Contract(address, ERC20_ABI, provider);
+ const [referenceBlock, totalSupplyRaw, decimals, onchainSymbol, onchainName] = await withTimeout(
+ Promise.all([
+ provider.getBlockNumber(),
+ contract.totalSupply() as Promise,
+ contract.decimals().catch(() => spec.decimals) as Promise,
+ contract.symbol().catch(() => undefined) as Promise,
+ contract.name().catch(() => undefined) as Promise,
+ ]),
+ timeoutMs,
+ `${chainId}:${spec.symbol}`
+ );
+ const decimalsNumber = Number(decimals);
+ const totalSupplyRawText = totalSupplyRaw.toString();
+ const totalSupplyUnits = formatUnits(totalSupplyRaw, decimalsNumber);
+ const key = catalogKey(chainId, address);
+ if (!shouldPreserveExistingProof(proofs.get(key))) {
+ proofs.set(key, {
+ schema: 'token-aggregation-supply-proof/v1',
+ generatedAt,
+ network: {
+ chainId,
+ name: chain.name,
+ referenceBlock,
+ },
+ token: {
+ address,
+ name: spec.name,
+ symbol: spec.symbol,
+ onchainName,
+ onchainSymbol,
+ decimals: decimalsNumber,
+ totalSupplyRaw: totalSupplyRawText,
+ totalSupplyUnits,
+ },
+ circulatingSupplyMethodology: {
+ status: 'onchain_total_supply_proved_circulating_review_required',
+ recommendedFormula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
+ currentConservativeReportableCirculatingSupplyUnits: totalSupplyUnits,
+ notes: [
+ 'totalSupply was read from the mapped token contract at the reference block.',
+ 'circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.',
+ 'Tracker acceptance is external and not implied by this API response.',
+ ],
+ },
+ });
+ }
+ proved = true;
+ break;
+ } catch (error) {
+ reasons.push(`${rpcUrl}: ${error instanceof Error ? error.message : String(error)}`);
+ }
+ }
+
+ if (!proved) {
+ failures.push({
+ chainId,
+ symbol: spec.symbol,
+ address,
+ reason: reasons.join(' | '),
+ });
+ }
+ }
+ }
+
+ const catalog: SupplyProofCatalog = {
+ schema: 'token-aggregation-supply-proof-catalog/v1',
+ generatedAt,
+ proofs: Array.from(proofs.values()).sort(
+ (a, b) => a.network.chainId - b.network.chainId || a.token.symbol.localeCompare(b.token.symbol)
+ ),
+ proofFailures: failures,
+ };
+
+ mkdirSync(path.dirname(catalogPath), { recursive: true });
+ writeFileSync(catalogPath, `${JSON.stringify(catalog, null, 2)}\n`);
+ console.log(JSON.stringify({
+ catalogPath,
+ proofs: catalog.proofs.length,
+ failures: failures.length,
+ }, null, 2));
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/services/token-aggregation/src/api/routes/bridge.ts b/services/token-aggregation/src/api/routes/bridge.ts
index bfb6762..196ae69 100644
--- a/services/token-aggregation/src/api/routes/bridge.ts
+++ b/services/token-aggregation/src/api/routes/bridge.ts
@@ -78,6 +78,22 @@ function uniquePaths(paths: Array): string[] {
return out;
}
+/** Non-empty built-in CCIP / trustless lane counts for /bridge/metrics (telemetry-friendly). */
+function summarizeBuiltInBridgeLanes() {
+ const payload = buildDefaultBridgeRoutes();
+ const trustlessKeys = payload.routes.trustless ? Object.keys(payload.routes.trustless) : [];
+ const chain138 = payload.chain138Bridges as Record;
+ return {
+ weth9Destinations: Object.keys(payload.routes.weth9).length,
+ weth10Destinations: Object.keys(payload.routes.weth10).length,
+ trustlessDestinations: trustlessKeys.length,
+ chain138ConfiguredBridges: Object.keys(chain138).filter((k) => {
+ const v = chain138[k];
+ return typeof v === 'string' && v.startsWith('0x');
+ }),
+ };
+}
+
function resolveBridgeRoutesPath(): string | null {
const candidates = uniquePaths([
process.env.BRIDGE_LIST_JSON_PATH,
@@ -176,16 +192,35 @@ router.get('/status', (_req: Request, res: Response) => {
router.get('/metrics', (_req: Request, res: Response) => {
const gruTransport = buildGruTransportStatus();
+ const builtIn = summarizeBuiltInBridgeLanes();
res.json({
ok: true,
- lanes: [],
+ lanes: [
+ {
+ kind: 'ccip-weth9',
+ label: 'WETH9 bridge destinations (built-in catalog)',
+ count: builtIn.weth9Destinations,
+ },
+ {
+ kind: 'ccip-weth10',
+ label: 'WETH10 bridge destinations (built-in catalog)',
+ count: builtIn.weth10Destinations,
+ },
+ {
+ kind: 'trustless',
+ label: 'Trustless / Lockbox destinations (env-backed)',
+ count: builtIn.trustlessDestinations,
+ },
+ ],
+ chain138Bridges: builtIn.chain138ConfiguredBridges,
gruTransport: gruTransport
? {
system: gruTransport.system,
summary: gruTransport.summary,
}
: null,
- message: 'Bridge metrics include GRU Transport summary counts. Use /api/v1/report/cross-chain for aggregated data.',
+ message:
+ 'Lane counts reflect the built-in CCIP route catalog plus GRU Transport summary. Use /api/v1/bridge/routes for full JSON and /api/v1/report/cross-chain for volumes.',
});
});
diff --git a/services/token-aggregation/src/api/routes/config.test.ts b/services/token-aggregation/src/api/routes/config.test.ts
index 8d6b311..59944ae 100644
--- a/services/token-aggregation/src/api/routes/config.test.ts
+++ b/services/token-aggregation/src/api/routes/config.test.ts
@@ -139,4 +139,83 @@ describe('Config API runtime networks loader', () => {
await new Promise((resolve, reject) => remoteServer.close((err) => (err ? reject(err) : resolve())));
}
});
+
+ it('serves wallet-facing MetaMask aliases with absolute token images', async () => {
+ const networksRes = await fetch(`${baseUrl}/api/v1/config/networks`);
+ expect(networksRes.status).toBe(200);
+ const networksBody = (await networksRes.json()) as Record;
+ expect(networksBody.networks).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ chainIdDecimal: 138,
+ iconUrls: expect.arrayContaining([
+ 'https://explorer.d-bis.org/token-icons/chain-138.png',
+ 'https://explorer.d-bis.org/api/v1/report/logo/chain-138',
+ ]),
+ }),
+ ])
+ );
+
+ const metamaskRes = await fetch(`${baseUrl}/api/v1/config/metamask?chainId=138`);
+ expect(metamaskRes.status).toBe(200);
+ const metamaskBody = (await metamaskRes.json()) as Record;
+ expect(metamaskBody.addEthereumChain).toMatchObject({
+ chainId: '0x8a',
+ chainName: 'DeFi Oracle Meta Mainnet',
+ });
+ expect(metamaskBody.watchAssets).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ type: 'ERC20',
+ options: expect.objectContaining({
+ symbol: 'cUSDC',
+ image: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+\/api\/v1\/report\/logo\/cUSDC\?v=20260510$/),
+ }),
+ }),
+ expect.objectContaining({
+ type: 'ERC20',
+ options: expect.objectContaining({
+ address: '0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03',
+ symbol: 'LINK',
+ image: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+\/api\/v1\/report\/logo\/LINK\?v=20260510$/),
+ }),
+ }),
+ expect.objectContaining({
+ type: 'ERC20',
+ options: expect.objectContaining({
+ symbol: 'WETH',
+ image: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+\/api\/v1\/report\/logo\/ETH\?v=20260510$/),
+ }),
+ }),
+ expect.objectContaining({
+ type: 'ERC20',
+ options: expect.objectContaining({
+ symbol: 'cWEMIX',
+ image: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+\/api\/v1\/report\/logo\/WEMIX\?v=20260510$/),
+ }),
+ }),
+ ])
+ );
+ expect(metamaskBody.watchAssets).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ type: 'ERC20',
+ options: expect.objectContaining({
+ address: '0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d',
+ symbol: 'cUSDC',
+ }),
+ metadata: expect.objectContaining({
+ catalogSymbol: 'cUSDC_V2',
+ familySymbol: 'cUSDC',
+ deploymentVersion: 'v2',
+ }),
+ }),
+ ])
+ );
+ expect(
+ metamaskBody.watchAssets.some(
+ (entry: Record) => entry.options?.symbol === 'cUSDC_V2' || entry.options?.symbol === 'cUSDT_V2'
+ )
+ ).toBe(false);
+ });
});
diff --git a/services/token-aggregation/src/api/routes/config.ts b/services/token-aggregation/src/api/routes/config.ts
index 8ad088a..a39f736 100644
--- a/services/token-aggregation/src/api/routes/config.ts
+++ b/services/token-aggregation/src/api/routes/config.ts
@@ -1,12 +1,75 @@
import { Router, Request, Response } from 'express';
import fs from 'fs';
import path from 'path';
-import { getNetworks, getConfigByChain, API_VERSION } from '../../config/networks';
+import { getNetworks, getConfigByChain, API_VERSION, type NetworkEntry } from '../../config/networks';
+import { getCanonicalTokensByChain, getLogoUriForSpec, getTokenRegistryFamily } from '../../config/canonical-tokens';
import { cacheMiddleware } from '../middleware/cache';
import { fetchRemoteJson } from '../utils/fetch-remote-json';
import { logger } from '../../utils/logger';
const router: Router = Router();
+const DEFAULT_PUBLIC_BASE_URL = 'https://explorer.d-bis.org';
+const DEFAULT_WALLET_METADATA_VERSION = '20260510';
+
+function resolvePublicBaseUrl(req: Request): string {
+ const configured = (
+ process.env.TOKEN_AGGREGATION_PUBLIC_BASE_URL ??
+ process.env.PUBLIC_API_BASE_URL ??
+ process.env.PUBLIC_BASE_URL ??
+ ''
+ ).trim();
+ if (configured) return configured.replace(/\/+$/, '');
+
+ const host = String(req.get('x-forwarded-host') || req.get('host') || '').split(',')[0].trim();
+ if (host) {
+ let proto = String(req.get('x-forwarded-proto') || 'https').split(',')[0].trim() || 'https';
+ if (proto === 'http' && !/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i.test(host)) {
+ proto = 'https';
+ }
+ return `${proto}://${host}`.replace(/\/+$/, '');
+ }
+
+ return DEFAULT_PUBLIC_BASE_URL;
+}
+
+function absolutePublicUrl(req: Request, value: string | undefined): string | undefined {
+ if (!value) return undefined;
+ if (/^https?:\/\//i.test(value)) return value;
+ if (!value.startsWith('/')) return value;
+ return `${resolvePublicBaseUrl(req)}${value}`;
+}
+
+function appendWalletMetadataVersion(value: string | undefined): string | undefined {
+ if (!value) return undefined;
+ const version = (process.env.WALLET_METADATA_IMAGE_VERSION || DEFAULT_WALLET_METADATA_VERSION).trim();
+ if (!version) return value;
+ const separator = value.includes('?') ? '&' : '?';
+ return `${value}${separator}v=${encodeURIComponent(version)}`;
+}
+
+function localLogoPathForSymbol(symbol: string, originalLogoUri: string): string {
+ if (originalLogoUri.includes('/token-lists/logos/gru/')) {
+ const fileName = originalLogoUri.split('/').pop()?.replace(/\.svg$/i, '');
+ if (fileName) return `/api/v1/report/logo/${fileName}`;
+ }
+ if (originalLogoUri.includes('/blockchains/bitcoin/info/logo.png')) return '/api/v1/report/logo/cWBTC';
+ if (originalLogoUri.includes('/ipfs/')) {
+ const cid = originalLogoUri.split('/').pop();
+ if (cid) return `/api/v1/report/logo/ipfs-${cid}`;
+ }
+ if (symbol === 'cWUSDC') return '/api/v1/report/logo/cUSDC';
+ return originalLogoUri;
+}
+
+function resolveWalletWatchAssetSymbol(spec: { symbol: string; familySymbol?: string; deploymentVersion?: string }): string {
+ // MetaMask validates wallet_watchAsset.symbol against ERC-20 symbol().
+ // Staged V2 Chain 138 deployments currently keep the family symbol on-chain
+ // (for example cUSDC), while the catalog symbol distinguishes the row
+ // (for example cUSDC_V2). Keep the catalog identity in metadata and send the
+ // contract-facing symbol in options.symbol so EIP-747 succeeds.
+ if (spec.deploymentVersion && spec.familySymbol) return spec.familySymbol;
+ return spec.symbol;
+}
type RuntimeNetworksPayload = {
version?: string | { major?: number; minor?: number; patch?: number };
@@ -124,16 +187,77 @@ async function resolveNetworksPayload(): Promise {
};
}
+async function sendNetworks(_req: Request, res: Response): Promise {
+ res.set('Cache-Control', 'public, max-age=0, must-revalidate');
+ const payload = await resolveNetworksPayload();
+ res.json(payload);
+}
+
/**
* GET /api/v1/networks
* Full EIP-3085 chain params for wallet_addEthereumChain (Chain 138, 1, 651940).
* If NETWORKS_JSON_URL is set (e.g. GitHub raw URL), fetches and returns that JSON; otherwise uses built-in networks.
*/
-router.get('/networks', cacheMiddleware(5 * 60 * 1000), async (req: Request, res: Response) => {
+router.get(['/networks', '/config/networks', '/metamask/networks'], cacheMiddleware(5 * 60 * 1000), async (req: Request, res: Response) => {
+ try {
+ await sendNetworks(req, res);
+ } catch (error) {
+ res.status(500).json({ error: 'Internal server error' });
+ }
+});
+
+/**
+ * GET /api/v1/config/metamask
+ * Wallet-facing aliases for Chain 138 add-chain params and watchAsset token entries.
+ */
+router.get(['/config/metamask', '/metamask'], cacheMiddleware(5 * 60 * 1000), async (req: Request, res: Response) => {
try {
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
+ const chainId = parseInt(String(req.query.chainId ?? '138'), 10) || 138;
const payload = await resolveNetworksPayload();
- res.json(payload);
+ const networks = payload.networks as NetworkEntry[];
+ const network = networks.find((entry) => Number(entry.chainIdDecimal) === chainId);
+ if (!network) {
+ res.status(404).json({ error: 'Chain not found', chainId });
+ return;
+ }
+
+ const watchAssets = getCanonicalTokensByChain(chainId)
+ .map((spec) => {
+ const address = spec.addresses[chainId];
+ if (!address) return null;
+ const originalLogoURI = getLogoUriForSpec(spec);
+ return {
+ type: 'ERC20',
+ options: {
+ address,
+ symbol: resolveWalletWatchAssetSymbol(spec),
+ decimals: spec.decimals,
+ image: appendWalletMetadataVersion(absolutePublicUrl(req, localLogoPathForSymbol(spec.symbol, originalLogoURI))),
+ },
+ metadata: {
+ name: spec.name,
+ catalogSymbol: spec.symbol,
+ registryFamily: getTokenRegistryFamily(spec),
+ familySymbol: spec.familySymbol,
+ deploymentVersion: spec.deploymentVersion,
+ deploymentStatus: spec.deploymentStatus,
+ },
+ };
+ })
+ .filter(Boolean);
+
+ res.json({
+ source: payload.source,
+ version: payload.version,
+ chainId,
+ addEthereumChain: network,
+ watchAssets,
+ caveats: [
+ 'MetaMask custom-token prices are controlled by MetaMask and its upstream asset/price providers; this endpoint supplies wallet metadata, logo URLs, and token-add payloads but cannot force MetaMask to render fiat prices.',
+ 'After metadata changes, remove and re-add the custom network/token in MetaMask or use the companion UI to call wallet_addEthereumChain and wallet_watchAsset again.',
+ ],
+ });
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
diff --git a/services/token-aggregation/src/api/routes/report.test.ts b/services/token-aggregation/src/api/routes/report.test.ts
index ef70e3c..a74fd21 100644
--- a/services/token-aggregation/src/api/routes/report.test.ts
+++ b/services/token-aggregation/src/api/routes/report.test.ts
@@ -91,6 +91,24 @@ describe('Report API', () => {
const body = (await res.json()) as Record;
expect(body.chainId).toBe(651940);
});
+
+ it('enriches Mainnet cWUSDC with supply proof fields for CMC reports', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/cmc?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdc = body.tokens.find((token: Record) => token.symbol === 'cWUSDC');
+ expect(cwusdc).toMatchObject({
+ contract_address: '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a',
+ total_supply: 10451316981.309788,
+ total_supply_raw: '10451316981309788',
+ circulating_supply: 10451316981.309788,
+ market_cap: 10451316981.309788,
+ supply_proof_provenance: expect.objectContaining({
+ status: 'ready_for_tracker_review',
+ }),
+ });
+ expect(cwusdc.tracker_caveats).toEqual(expect.arrayContaining([expect.stringContaining('on-chain supply proof')]));
+ });
});
describe('GET /api/v1/report/coingecko', () => {
@@ -104,6 +122,62 @@ describe('Report API', () => {
expect(body).toHaveProperty('tokens');
expect(Array.isArray(body.tokens)).toBe(true);
});
+
+ it('enriches Mainnet cWUSDC with supply proof, circulating supply, and market cap', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/coingecko?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdc = body.tokens.find((token: Record) => token.symbol === 'cWUSDC');
+ expect(cwusdc).toMatchObject({
+ contract_address: '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a',
+ total_supply: 10451316981.309788,
+ total_supply_raw: '10451316981309788',
+ circulating_supply: 10451316981.309788,
+ circulating_supply_formula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
+ supply_proof_provenance: expect.objectContaining({
+ schema: 'mainnet-cwusdc-supply-proof/v1',
+ referenceBlock: 25047586,
+ }),
+ market_data: expect.objectContaining({
+ current_price: { usd: 1 },
+ market_cap: 10451316981.309788,
+ }),
+ });
+ expect(cwusdc.tracker_caveats).toEqual(expect.arrayContaining([expect.stringContaining('No public tracker')]));
+ });
+
+ it('surfaces GRU v2 deployment-status pools in tracker-facing token reports', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/coingecko?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdc = body.tokens.find((token: Record) => token.symbol === 'cWUSDC');
+ expect(cwusdc.liquidity_pools).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ pool_address: '0x69776fc607e9eda8042e320e7e43f54d06c68f0e',
+ source: 'gru-v2-deployment-status',
+ status: 'live',
+ role: 'defense',
+ }),
+ ])
+ );
+ });
+
+ it('surfaces explicit supply-proof gaps for Mainnet GRU assets without proof artifacts', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/coingecko?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdt = body.tokens.find((token: Record) => token.symbol === 'cWUSDT');
+ expect(cwusdt).toMatchObject({
+ contract_address: '0xaf5017d0163ecb99d9b5d94e3b4d7b09af44d8ae',
+ supply_proof_provenance: {
+ source: 'missing-supply-proof',
+ status: 'proof_required',
+ },
+ });
+ expect(cwusdt).not.toHaveProperty('total_supply');
+ expect(cwusdt.tracker_caveats).toEqual(expect.arrayContaining([expect.stringContaining('tracker-grade supply proof')]));
+ });
});
describe('GET /api/v1/report/all', () => {
@@ -149,6 +223,72 @@ describe('Report API', () => {
}),
});
});
+
+ it('includes Mainnet cWUSDC supply proof enrichment in unified reports', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/all?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdc = body.tokens?.['1']?.find((token: Record) => token.symbol === 'cWUSDC');
+ expect(cwusdc).toMatchObject({
+ totalSupply: '10451316981.309788',
+ totalSupplyRaw: '10451316981309788',
+ circulatingSupply: '10451316981.309788',
+ circulatingSupplyFormula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
+ market: expect.objectContaining({
+ priceUsd: 1,
+ marketCapUsd: 10451316981.309788,
+ }),
+ supplyProofProvenance: expect.objectContaining({
+ schema: 'mainnet-cwusdc-supply-proof/v1',
+ referenceBlock: 25047586,
+ }),
+ });
+ });
+
+ it('distinguishes proof-gated Mainnet cW assets from deterministic placeholder bindings', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/all?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const proofGated = body.tokens?.['1']?.find((entry: Record) => entry.symbol === 'cWUSDT');
+ expect(proofGated).toMatchObject({
+ supplyProofProvenance: {
+ source: 'missing-supply-proof',
+ status: 'proof_required',
+ },
+ });
+ expect(proofGated.totalSupply).toBeUndefined();
+ expect(proofGated.trackerCaveats).toEqual(expect.arrayContaining([expect.stringContaining('proof artifact')]));
+
+ const placeholderSymbols = ['cWBTC', 'cWETH'];
+ for (const symbol of placeholderSymbols) {
+ const token = body.tokens?.['1']?.find((entry: Record) => entry.symbol === symbol);
+ expect(token).toMatchObject({
+ supplyProofProvenance: {
+ source: 'deterministic-placeholder-address',
+ status: 'non_reportable_until_erc20_deployed',
+ },
+ });
+ expect(token.totalSupply).toBeUndefined();
+ expect(token.trackerCaveats).toEqual(expect.arrayContaining([expect.stringContaining('deterministic placeholder')]));
+ }
+ });
+
+ it('marks proofless base GRU c assets as proof gated instead of leaving silent supply fields', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/all?chainId=138`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cusdc = body.tokens?.['138']?.find((entry: Record) => entry.symbol === 'cUSDC');
+ expect(cusdc).toMatchObject({
+ type: 'base',
+ registryFamily: 'iso4217',
+ supplyProofProvenance: {
+ source: 'missing-supply-proof',
+ status: 'proof_required',
+ },
+ });
+ expect(cusdc.totalSupply).toBeUndefined();
+ expect(cusdc.trackerCaveats).toEqual(expect.arrayContaining([expect.stringContaining('tracker-grade supply proof')]));
+ });
});
describe('GET /api/v1/report/gas-registry', () => {
@@ -185,6 +325,63 @@ describe('Report API', () => {
});
});
+ describe('GET /api/v1/report/adoption-readiness', () => {
+ it('summarizes proved, proof-gated, pool-indexed, and scoring gates', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/adoption-readiness`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ expect(body.scope).toBe('gru-c-and-cw-assets');
+ expect(body.counts).toMatchObject({
+ candidates: expect.any(Number),
+ reportableCandidates: expect.any(Number),
+ nonReportablePlaceholder: expect.any(Number),
+ proved: expect.any(Number),
+ proofRequired: expect.any(Number),
+ silent: 0,
+ liquidityMissing: expect.any(Number),
+ liquidityMissingWithPools: expect.any(Number),
+ liquidityMissingWithoutPools: expect.any(Number),
+ gruV2PoolsWithStatus: expect.any(Number),
+ });
+ expect(body.institutional.score).toEqual(expect.any(Number));
+ expect(body.cryptoListing.score).toEqual(expect.any(Number));
+ expect(Array.isArray(body.blockerInventory.proofRequiredByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.liquidityMissingByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.liquidityMissingWithPoolsByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.liquidityMissingWithoutPoolsByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.liquidityMissingDetails)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.externalOfficialQuoteLiquidityByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.nonReportablePlaceholderByChain)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.gruV2PoolsMissingStatus)).toBe(true);
+ expect(Array.isArray(body.blockerInventory.notes)).toBe(true);
+ });
+
+ it('does not treat external official USDC/USDT mirrors as GRU liquidity blockers', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/adoption-readiness`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ expect(body.counts.externalOfficialQuoteLiquidity).toBeGreaterThan(0);
+ expect(body.blockerInventory.externalOfficialQuoteLiquidityByChain).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ chainId: 1,
+ symbols: expect.arrayContaining(['cUSDC', 'cUSDT']),
+ }),
+ expect.objectContaining({
+ chainId: 56,
+ symbols: expect.arrayContaining(['cUSDC', 'cUSDT']),
+ }),
+ ])
+ );
+ expect(body.blockerInventory.liquidityMissingDetails).not.toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ chainId: 1, symbol: 'cUSDT' }),
+ expect.objectContaining({ chainId: 56, symbol: 'cUSDC' }),
+ ])
+ );
+ });
+ });
+
describe('GET /api/v1/report/token-list', () => {
it('surfaces both V1 and V2 Chain 138 canonical GRU deployments explicitly', async () => {
const res = await fetch(`${baseUrl}/api/v1/report/token-list?chainId=138`);
@@ -379,6 +576,17 @@ describe('Report API', () => {
])
);
});
+
+ it('uses packaged DBIS-level local logo assets while preserving original logo references', async () => {
+ const res = await fetch(`${baseUrl}/api/v1/report/token-list?chainId=1`);
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as Record;
+ const cwusdc = body.tokens.find((token: Record) => token.symbol === 'cWUSDC');
+ expect(cwusdc).toMatchObject({
+ logoURI: expect.stringMatching(/^https:\/\/127\.0\.0\.1:\d+\/api\/v1\/report\/logo\/cUSDC$/),
+ originalLogoURI: expect.stringContaining('/token-lists/logos/gru/cUSDC.svg'),
+ });
+ });
});
describe('GET /api/v1/report/cw-registry', () => {
@@ -488,6 +696,8 @@ describe('Report API', () => {
expect((body.pools as Array<{ poolAddress: string }>)[0]).toMatchObject({
poolAddress: '0x1111111111111111111111111111111111111111',
section: 'pmmPools',
+ status: 'routing_enabled',
+ statusReason: expect.stringContaining('public routing is enabled'),
});
} finally {
await import('fs/promises').then((fs) => fs.unlink(tempPath).catch(() => undefined));
diff --git a/services/token-aggregation/src/api/routes/report.ts b/services/token-aggregation/src/api/routes/report.ts
index 4f06c95..1e3b9f7 100644
--- a/services/token-aggregation/src/api/routes/report.ts
+++ b/services/token-aggregation/src/api/routes/report.ts
@@ -4,6 +4,8 @@
*/
import { Router, Request, Response } from 'express';
+import { existsSync, readFileSync } from 'fs';
+import path from 'path';
import { TokenRepository } from '../../database/repositories/token-repo';
import { MarketDataRepository } from '../../database/repositories/market-data-repo';
import { PoolRepository } from '../../database/repositories/pool-repo';
@@ -19,21 +21,543 @@ import { cacheMiddleware } from '../middleware/cache';
import { fetchRemoteJson } from '../utils/fetch-remote-json';
import { buildCrossChainReport } from '../../indexer/cross-chain-indexer';
import { logger } from '../../utils/logger';
-import { filterPoolsForExposure, getActiveTransportPairs, getGruTransportMetadata } from '../../config/gru-transport';
+import {
+ filterPoolsForExposure,
+ getActiveTransportPairs,
+ getGruTransportMetadata,
+ type GruTransportGasAssetFamily,
+ type GruTransportPair,
+} from '../../config/gru-transport';
import {
buildCwRegistryChains,
buildGasRegistryChains,
loadDeploymentStatusFile,
+ type DeploymentStatusFile,
type CwRegistryChain,
} from '../../config/deployment-status';
import { getGruV2DeploymentPoolRows } from '../../config/gru-v2-deployment-pools';
import { getCanonicalPriceSnapshotGeneratedAt, getCanonicalPriceUsd } from '../../services/canonical-price-oracle';
+import { pmmVaultReserveFromChain, resolvePmmQuoteRpcUrl } from '../../services/pmm-onchain-quote';
const router: Router = Router();
const tokenRepo = new TokenRepository();
const marketDataRepo = new MarketDataRepository();
const poolRepo = new PoolRepository();
+const MAINNET_CWUSDC_ADDRESS = '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a';
+const DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH = 'config/supply-proof-catalog.json';
+const DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH = 'config/live-uniswap-v2-pool-catalog.json';
+const DEFAULT_PUBLIC_REPORT_BASE_URL = 'https://explorer.d-bis.org';
+const DBIS_CHAIN_138_LOGO_PATH = '/api/v1/report/logo/chain-138';
+const CWUSDC_SUPPLY_PROOF_FALLBACK = {
+ schema: 'mainnet-cwusdc-supply-proof/v1',
+ generatedAt: '2026-05-08T03:16:54Z',
+ network: {
+ chainId: 1,
+ name: 'Ethereum Mainnet',
+ referenceBlock: 25047586,
+ },
+ token: {
+ address: MAINNET_CWUSDC_ADDRESS,
+ name: 'Wrapped cUSDC',
+ symbol: 'cWUSDC',
+ decimals: 6,
+ totalSupplyRaw: '10451316981309788',
+ totalSupplyUnits: '10451316981.309788',
+ },
+ circulatingSupplyMethodology: {
+ status: 'ready_for_tracker_review',
+ recommendedFormula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
+ currentConservativeReportableCirculatingSupplyUnits: '10451316981.309788',
+ notes: [
+ 'No public tracker has accepted a circulating-supply value for this contract yet.',
+ 'If a tracker requires exclusion of operator, treasury, bridge, or protocol-controlled balances, use the knownBalances section plus any additional signed treasury inventory supplied at submission time.',
+ 'The value above is an on-chain supply proof, not a third-party listing approval.',
+ ],
+ },
+};
+
+type SupplyProofSnapshot = typeof CWUSDC_SUPPLY_PROOF_FALLBACK & {
+ _sourcePath?: string;
+ _source?: string;
+};
+
+type SupplyProofEnrichment = {
+ totalSupply?: string;
+ totalSupplyRaw?: string;
+ circulatingSupply?: string;
+ circulatingSupplyFormula?: string;
+ marketCapUsd?: number;
+ supplyProofProvenance: {
+ source: string;
+ path?: string;
+ schema?: string;
+ generatedAt?: string;
+ referenceBlock?: number;
+ status?: string;
+ };
+ trackerCaveats: string[];
+};
+
+type ReportPoolEntry = {
+ poolAddress: string;
+ dex: string;
+ token0: string;
+ token1: string;
+ token0Symbol?: string;
+ token1Symbol?: string;
+ tvl: number;
+ volume24h: number;
+ source?: string;
+ status?: string;
+ statusReason?: string;
+ role?: string;
+ section?: string;
+ publicRoutingEnabled?: boolean;
+ reserveSource?: string;
+ reserveUpdatedAt?: string;
+ reserve0Raw?: string;
+ reserve1Raw?: string;
+};
+
+type LiveUniswapV2PoolCatalogRow = {
+ chainId: number;
+ poolAddress: string;
+ dex?: string;
+ baseSymbol: string;
+ quoteSymbol: string;
+ baseAddress: string;
+ quoteAddress: string;
+ totalLiquidityUsd: number;
+ reserve0Usd?: number;
+ reserve1Usd?: number;
+ depthOk?: boolean;
+ parityOk?: boolean;
+ healthy?: boolean;
+ deviationBps?: string;
+};
+
+type LiveUniswapV2PoolCatalog = {
+ schema?: string;
+ generatedAt?: string;
+ pools?: LiveUniswapV2PoolCatalogRow[];
+};
+
+function resolveRepoRoot(): string {
+ const candidates = [
+ process.env.PROXMOX_REPO_ROOT,
+ process.env.PROJECT_ROOT,
+ path.resolve(process.cwd(), '..', '..', '..'),
+ path.resolve(process.cwd(), '..'),
+ process.cwd(),
+ ].filter(Boolean) as string[];
+
+ for (const candidate of candidates) {
+ if (
+ existsSync(path.join(candidate, DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH)) ||
+ existsSync(path.join(candidate, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH)) ||
+ existsSync(path.join(candidate, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'))
+ ) {
+ return candidate;
+ }
+ }
+ return process.cwd();
+}
+
+function loadLiveUniswapV2PoolCatalog(): LiveUniswapV2PoolCatalogRow[] {
+ const configuredCatalog = process.env.TOKEN_AGGREGATION_LIVE_UNISWAP_V2_POOL_CATALOG_JSON?.trim();
+ const repoRoot = resolveRepoRoot();
+ const candidates = [
+ configuredCatalog,
+ path.join(repoRoot, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH),
+ ].filter(Boolean) as string[];
+
+ for (const candidate of candidates) {
+ if (!existsSync(candidate)) continue;
+ try {
+ const parsed = JSON.parse(readFileSync(candidate, 'utf8')) as LiveUniswapV2PoolCatalog;
+ return (parsed.pools ?? []).filter(
+ (pool) =>
+ Number.isFinite(pool.chainId) &&
+ pool.poolAddress?.startsWith('0x') &&
+ pool.baseAddress?.startsWith('0x') &&
+ pool.quoteAddress?.startsWith('0x')
+ );
+ } catch (error) {
+ logger.warn('Unable to parse live Uniswap V2 pool catalog; skipping file', { candidate, error });
+ }
+ }
+
+ return [];
+}
+
+function normalizeSupplyProofKey(chainId: number, address: string): string {
+ return `${chainId}:${address.toLowerCase()}`;
+}
+
+function loadSupplyProofFile(proofPath: string, source: string): SupplyProofSnapshot[] {
+ if (!proofPath || !existsSync(proofPath)) return [];
+
+ try {
+ const parsed = JSON.parse(readFileSync(proofPath, 'utf8')) as { proofs?: SupplyProofSnapshot[] } | SupplyProofSnapshot;
+ const proofs: SupplyProofSnapshot[] = Array.isArray((parsed as { proofs?: SupplyProofSnapshot[] })?.proofs)
+ ? ((parsed as { proofs: SupplyProofSnapshot[] }).proofs)
+ : [parsed as SupplyProofSnapshot];
+ return proofs
+ .filter((proof) => proof?.network?.chainId && proof?.token?.address)
+ .map((proof) => ({
+ ...proof,
+ _source: source,
+ _sourcePath: proofPath,
+ }));
+ } catch (error) {
+ logger.warn('Unable to parse supply proof file; skipping file', { proofPath, error });
+ return [];
+ }
+}
+
+function loadSupplyProofCatalog(): Map {
+ const configuredCatalog = process.env.TOKEN_AGGREGATION_SUPPLY_PROOF_CATALOG_JSON?.trim();
+ const configuredSingleProof = process.env.CWUSDC_SUPPLY_PROOF_JSON?.trim();
+ const repoRoot = resolveRepoRoot();
+ const candidates = [
+ configuredCatalog ? { path: configuredCatalog, source: 'configured-supply-proof-catalog' } : null,
+ { path: path.join(repoRoot, DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH), source: 'repo-supply-proof-catalog' },
+ configuredSingleProof ? { path: configuredSingleProof, source: 'configured-supply-proof-file' } : null,
+ { path: path.join(repoRoot, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'), source: 'repo-supply-proof-file' },
+ ].filter(Boolean) as Array<{ path: string; source: string }>;
+
+ const byToken = new Map();
+ for (const candidate of candidates) {
+ for (const proof of loadSupplyProofFile(candidate.path, candidate.source)) {
+ const key = normalizeSupplyProofKey(Number(proof.network.chainId), String(proof.token.address));
+ if (!byToken.has(key)) {
+ byToken.set(key, proof);
+ }
+ }
+ }
+
+ if (!byToken.has(normalizeSupplyProofKey(1, MAINNET_CWUSDC_ADDRESS))) {
+ byToken.set(normalizeSupplyProofKey(1, MAINNET_CWUSDC_ADDRESS), {
+ ...CWUSDC_SUPPLY_PROOF_FALLBACK,
+ _source: 'embedded-fallback-snapshot',
+ });
+ }
+
+ return byToken;
+}
+
+function isGruSupplyTrackedCandidate(symbol: string, type?: string, registryFamily?: string): boolean {
+ return (
+ /^c[A-Z0-9]/.test(symbol) &&
+ (type === 'base' ||
+ type === 'c' ||
+ type === 'w' ||
+ ['iso4217', 'monetary_unit', 'gas_native', 'commodity'].includes(String(registryFamily || '')))
+ );
+}
+
+const EXTERNAL_OFFICIAL_QUOTE_ASSETS = new Set([
+ '1:cUSDC:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
+ '1:cUSDT:0xdac17f958d2ee523a2206206994597c13d831ec7',
+ '10:cUSDC:0x0b2c639c533813f4aa9d7837caf62653d097ff85',
+ '10:cUSDT:0x94b008aa00579c1307b0ef2c499ad98a8ce58e58',
+ '25:cUSDC:0xc21223249ca28397b4b6541dffaecc539bff0c59',
+ '25:cUSDT:0x66e428c3f67a68878562e79a0234c1f83c208770',
+ '56:cUSDC:0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
+ '56:cUSDT:0x55d398326f99059ff775485246999027b3197955',
+ '100:cUSDC:0xddafbb505ad214d7b80b1f830fccc89b60fb7a83',
+ '100:cUSDT:0x4ecaba5870353805a9f068101a40e0f32ed605c6',
+ '137:cUSDC:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
+ '137:cUSDT:0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
+ '8453:cUSDC:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
+ '8453:cUSDT:0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
+ '42161:cUSDC:0xaf88d065e77c8cc2239327c5edb3a432268e5831',
+ '42161:cUSDT:0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
+ '42220:cUSDC:0xceba9300f2b948710d2653dd7b07f33a8b32118c',
+ '42220:cUSDT:0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e',
+ '43114:cUSDC:0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
+ '43114:cUSDT:0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7',
+ '1111:cUSDC:0xe3f5a90f9cb311505cd691a46596599aa1a0ad7d',
+ '1111:cUSDT:0xa649325aa7c5093d12d6f98eb4378deae68ce23f',
+ '651940:cUSDC:0xa95eed79f84e6a0151eaeb9d441f9ffd50e8e881',
+ '651940:cUSDT:0x015b1897ed5279930bc2be46f661894d219292a6',
+]);
+
+function hasExternalOfficialQuoteLiquidity(token: { chainId: number; symbol: string; address: string }): boolean {
+ return EXTERNAL_OFFICIAL_QUOTE_ASSETS.has(`${token.chainId}:${token.symbol}:${token.address.toLowerCase()}`);
+}
+
+function isDeterministicPlaceholderAddress(address: string): boolean {
+ const normalized = address.toLowerCase();
+ return /^0x[a-f0-9]{4}0{24,}[a-f0-9]{1,8}$/.test(normalized);
+}
+
+function buildSupplyProofEnrichment(
+ chainId: number,
+ address: string,
+ symbol: string,
+ type: string | undefined,
+ registryFamily: string | undefined,
+ priceUsd?: number
+): SupplyProofEnrichment | undefined {
+ const proof = loadSupplyProofCatalog().get(normalizeSupplyProofKey(chainId, address));
+
+ if (!proof) {
+ if (!isGruSupplyTrackedCandidate(symbol, type, registryFamily)) return undefined;
+ if (isDeterministicPlaceholderAddress(address)) {
+ return {
+ supplyProofProvenance: {
+ source: 'deterministic-placeholder-address',
+ status: 'non_reportable_until_erc20_deployed',
+ },
+ trackerCaveats: [
+ 'This token binding is a deterministic placeholder address and does not currently behave as an ERC-20 contract.',
+ 'Do not submit totalSupply, circulatingSupply, marketCapUsd, or liquidity claims for this asset until a deployed ERC-20 binding replaces the placeholder.',
+ 'The placeholder is kept visible for registry and routing roadmap traceability only.',
+ ],
+ };
+ }
+ return {
+ supplyProofProvenance: {
+ source: 'missing-supply-proof',
+ status: 'proof_required',
+ },
+ trackerCaveats: [
+ 'No token-specific on-chain supply proof artifact is currently attached to this report response.',
+ 'Do not submit totalSupply, circulatingSupply, or marketCapUsd for this asset until a current proof artifact is generated and linked.',
+ 'Registry and pool visibility are not the same as tracker-grade supply proof.',
+ ],
+ };
+ }
+
+ const totalSupply = String(proof.token?.totalSupplyUnits ?? '');
+ const totalSupplyRaw = String(proof.token?.totalSupplyRaw ?? '');
+ const circulatingSupply = String(
+ proof.circulatingSupplyMethodology?.currentConservativeReportableCirculatingSupplyUnits ?? totalSupply
+ );
+ const parsedCirculatingSupply = Number(circulatingSupply);
+ const marketCapUsd =
+ priceUsd !== undefined && Number.isFinite(parsedCirculatingSupply) ? parsedCirculatingSupply * priceUsd : undefined;
+
+ return {
+ totalSupply,
+ totalSupplyRaw,
+ circulatingSupply,
+ circulatingSupplyFormula: String(
+ proof.circulatingSupplyMethodology?.recommendedFormula ??
+ 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances'
+ ),
+ marketCapUsd,
+ supplyProofProvenance: {
+ source: proof._source ?? 'repo-supply-proof-file',
+ path: proof._sourcePath,
+ schema: proof.schema,
+ generatedAt: proof.generatedAt,
+ referenceBlock: proof.network?.referenceBlock,
+ status: proof.circulatingSupplyMethodology?.status,
+ },
+ trackerCaveats: proof.circulatingSupplyMethodology?.notes ?? [
+ 'Tracker acceptance is external and not implied by this API response.',
+ ],
+ };
+}
+
+function resolveGruV2ReserveRpcUrl(chainId: number): string {
+ const configured = resolvePmmQuoteRpcUrl();
+ if (chainId === 138 && process.env.NODE_ENV !== 'test') return configured || 'http://192.168.11.211:8545';
+ if (chainId === 56 && process.env.NODE_ENV !== 'test') {
+ return process.env.BSC_RPC_URL || process.env.BSC_MAINNET_RPC || process.env.RPC_URL_56 || 'https://bsc-rpc.publicnode.com';
+ }
+ if (chainId === 43114 && process.env.NODE_ENV !== 'test') {
+ return (
+ process.env.AVALANCHE_RPC_URL ||
+ process.env.AVALANCHE_MAINNET_RPC ||
+ process.env.RPC_URL_43114 ||
+ 'https://avalanche-c-chain-rpc.publicnode.com'
+ );
+ }
+ if (chainId === 1111 && process.env.NODE_ENV !== 'test') {
+ return process.env.WEMIX_MAINNET_RPC || process.env.WEMIX_RPC || process.env.RPC_URL_1111 || 'https://api.wemix.com';
+ }
+ return '';
+}
+
+function decimalFromRawForReport(raw: bigint, decimals: number): number {
+ const scale = 10 ** decimals;
+ return Number(raw) / scale;
+}
+
+function fallbackPoolDecimals(symbol?: string): number {
+ const normalized = String(symbol || '').toUpperCase();
+ if (normalized === 'WETH' || normalized === 'ETH') return 18;
+ return 6;
+}
+
+function fallbackPoolUsdPrice(symbol?: string): number {
+ const normalized = String(symbol || '').toUpperCase();
+ if (normalized.includes('XAU')) return Number(process.env.TOKEN_AGGREGATION_XAU_USD || '0') || 0;
+ if (normalized === 'WETH' || normalized === 'ETH') return Number(process.env.TOKEN_AGGREGATION_ETH_USD || '0') || 0;
+ return 1;
+}
+
+async function enrichGruV2FallbackPoolWithReserves(chainId: number, pool: ReportPoolEntry): Promise {
+ const rpcUrl = resolveGruV2ReserveRpcUrl(chainId);
+ if (!rpcUrl) return pool;
+
+ const reserves = await pmmVaultReserveFromChain({ rpcUrl, poolAddress: pool.poolAddress });
+ if (!reserves) return pool;
+
+ const baseUnits = decimalFromRawForReport(reserves.baseReserveRaw, fallbackPoolDecimals(pool.token0Symbol));
+ const quoteUnits = decimalFromRawForReport(reserves.quoteReserveRaw, fallbackPoolDecimals(pool.token1Symbol));
+ const reserveUsd =
+ baseUnits * fallbackPoolUsdPrice(pool.token0Symbol) + quoteUnits * fallbackPoolUsdPrice(pool.token1Symbol);
+
+ if (!Number.isFinite(reserveUsd) || reserveUsd <= 0) {
+ return {
+ ...pool,
+ reserveSource: 'onchain-gru-v2-pmm-getVaultReserve',
+ reserveUpdatedAt: new Date().toISOString(),
+ reserve0Raw: reserves.baseReserveRaw.toString(),
+ reserve1Raw: reserves.quoteReserveRaw.toString(),
+ };
+ }
+
+ return {
+ ...pool,
+ tvl: reserveUsd,
+ status: pool.status ?? 'reserve_visible',
+ statusReason:
+ pool.statusReason ??
+ 'Pool is present in deployment-status and has positive on-chain reserves via getVaultReserve().',
+ reserveSource: 'onchain-gru-v2-pmm-getVaultReserve',
+ reserveUpdatedAt: new Date().toISOString(),
+ reserve0Raw: reserves.baseReserveRaw.toString(),
+ reserve1Raw: reserves.quoteReserveRaw.toString(),
+ };
+}
+
+async function buildGruV2FallbackPoolsForToken(
+ chainId: number,
+ tokenAddress: string,
+ existingPools: ReportPoolEntry[]
+): Promise {
+ const token = tokenAddress.toLowerCase();
+ const existing = new Set(existingPools.map((pool) => pool.poolAddress.toLowerCase()));
+ const pools = getGruV2DeploymentPoolRows()
+ .filter((pool) => pool.chainId === chainId)
+ .filter((pool) => pool.baseAddress.toLowerCase() === token || pool.quoteAddress.toLowerCase() === token)
+ .filter((pool) => !existing.has(pool.poolAddress.toLowerCase()))
+ .map((pool) => ({
+ poolAddress: pool.poolAddress,
+ dex: pool.venue ?? 'gru_v2_pmm',
+ token0: pool.baseAddress,
+ token1: pool.quoteAddress,
+ token0Symbol: pool.baseSymbol,
+ token1Symbol: pool.quoteSymbol,
+ tvl: 0,
+ volume24h: 0,
+ source: 'gru-v2-deployment-status',
+ status: pool.status,
+ statusReason: pool.statusReason,
+ role: pool.role,
+ section: pool.section,
+ publicRoutingEnabled: pool.publicRoutingEnabled,
+ }));
+
+ return Promise.all(pools.map((pool) => enrichGruV2FallbackPoolWithReserves(chainId, pool)));
+}
+
+function buildLiveUniswapV2FallbackPoolsForToken(
+ chainId: number,
+ tokenAddress: string,
+ existingPools: ReportPoolEntry[]
+): ReportPoolEntry[] {
+ const token = tokenAddress.toLowerCase();
+ const existing = new Set(existingPools.map((pool) => pool.poolAddress.toLowerCase()));
+ return loadLiveUniswapV2PoolCatalog()
+ .filter((pool) => pool.chainId === chainId)
+ .filter((pool) => pool.baseAddress.toLowerCase() === token || pool.quoteAddress.toLowerCase() === token)
+ .filter((pool) => !existing.has(pool.poolAddress.toLowerCase()))
+ .map((pool) => ({
+ poolAddress: pool.poolAddress,
+ dex: pool.dex ?? 'uniswap_v2',
+ token0: pool.baseAddress,
+ token1: pool.quoteAddress,
+ token0Symbol: pool.baseSymbol,
+ token1Symbol: pool.quoteSymbol,
+ tvl: Number(pool.totalLiquidityUsd ?? 0),
+ volume24h: 0,
+ source: 'live-uniswap-v2-pair-discovery',
+ status: pool.healthy ? 'indexed_live_healthy' : 'indexed_live_needs_repair',
+ statusReason: pool.healthy
+ ? 'Live Uniswap V2 pair discovered with on-chain reserves, depth, and parity checks passing.'
+ : `Live Uniswap V2 pair discovered; depthOk=${pool.depthOk === true}, parityOk=${pool.parityOk === true}, deviationBps=${pool.deviationBps ?? 'unknown'}.`,
+ role: 'public_indexable_liquidity',
+ publicRoutingEnabled: pool.healthy === true,
+ }));
+}
+
+function resolveLocalLogoUri(remoteLogoUri: string, symbol: string): string | undefined {
+ if (remoteLogoUri.startsWith('/api/v1/report/logo/')) {
+ return remoteLogoUri;
+ }
+ if (remoteLogoUri.includes('/token-lists/logos/gru/')) {
+ const fileName = remoteLogoUri.split('/').pop()?.replace(/\.svg$/i, '');
+ return fileName ? `/api/v1/report/logo/${fileName}` : undefined;
+ }
+ if (remoteLogoUri.includes('/blockchains/bitcoin/info/logo.png')) {
+ return '/api/v1/report/logo/cWBTC';
+ }
+ if (remoteLogoUri.includes('/ipfs/')) {
+ const cid = remoteLogoUri.split('/').pop();
+ return cid ? `/api/v1/report/logo/ipfs-${cid}` : undefined;
+ }
+ if (symbol === 'cWUSDC') {
+ return '/api/v1/report/logo/cUSDC';
+ }
+ return undefined;
+}
+
+function resolvePublicBaseUrl(req: Request): string {
+ const configured = (
+ process.env.TOKEN_AGGREGATION_PUBLIC_BASE_URL ??
+ process.env.PUBLIC_API_BASE_URL ??
+ process.env.PUBLIC_BASE_URL ??
+ ''
+ ).trim();
+ if (configured) return configured.replace(/\/+$/, '');
+
+ const host = String(req.get('x-forwarded-host') || req.get('host') || '').split(',')[0].trim();
+ if (host) {
+ let proto = String(req.get('x-forwarded-proto') || 'https').split(',')[0].trim() || 'https';
+ if (proto === 'http' && !/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i.test(host)) {
+ proto = 'https';
+ }
+ return `${proto}://${host}`.replace(/\/+$/, '');
+ }
+
+ return DEFAULT_PUBLIC_REPORT_BASE_URL;
+}
+
+function absolutePublicUrl(req: Request, value: string | undefined): string | undefined {
+ if (!value) return undefined;
+ if (/^https?:\/\//i.test(value)) return value;
+ if (!value.startsWith('/')) return value;
+ return `${resolvePublicBaseUrl(req)}${value}`;
+}
+
+function absoluteLogoUri(req: Request, remoteLogoUri: string, symbol: string): string {
+ const localLogoURI = resolveLocalLogoUri(remoteLogoUri, symbol);
+ return absolutePublicUrl(req, localLogoURI) ?? remoteLogoUri;
+}
+
+function absoluteReportLogoUri(remoteLogoUri: string, symbol: string): string {
+ const localLogoURI = resolveLocalLogoUri(remoteLogoUri, symbol);
+ if (localLogoURI?.startsWith('/')) return `${DEFAULT_PUBLIC_REPORT_BASE_URL}${localLogoURI}`;
+ return localLogoURI ?? remoteLogoUri;
+}
+
/** Build token entries with DB market/pool data for a chain */
async function buildTokenReport(chainId: number) {
const canonical = getCanonicalTokensByChain(chainId);
@@ -51,6 +575,8 @@ async function buildTokenReport(chainId: number) {
deploymentStatus?: string;
preferredForX402?: boolean;
liquiditySourceSymbol?: string;
+ logoURI?: string;
+ originalLogoURI?: string;
market?: {
priceUsd?: number;
volume24h: number;
@@ -60,14 +586,13 @@ async function buildTokenReport(chainId: number) {
liquidityUsd: number;
lastUpdated: string;
};
- pools: Array<{
- poolAddress: string;
- dex: string;
- token0: string;
- token1: string;
- tvl: number;
- volume24h: number;
- }>;
+ totalSupply?: string;
+ totalSupplyRaw?: string;
+ circulatingSupply?: string;
+ circulatingSupplyFormula?: string;
+ supplyProofProvenance?: SupplyProofEnrichment['supplyProofProvenance'];
+ trackerCaveats?: string[];
+ pools: ReportPoolEntry[];
fromDb: boolean;
}> = [];
@@ -98,6 +623,58 @@ async function buildTokenReport(chainId: number) {
const fallbackPriceUsd = getCanonicalPriceUsd(chainId, address);
+ const market = marketData
+ ? {
+ priceUsd: marketData.priceUsd ?? fallbackPriceUsd,
+ volume24h: marketData.volume24h,
+ volume7d: marketData.volume7d,
+ volume30d: marketData.volume30d,
+ marketCapUsd: marketData.marketCapUsd,
+ liquidityUsd: marketData.liquidityUsd,
+ lastUpdated: marketData.lastUpdated?.toISOString() ?? '',
+ }
+ : fallbackPriceUsd !== undefined
+ ? {
+ priceUsd: fallbackPriceUsd,
+ volume24h: 0,
+ volume7d: 0,
+ volume30d: 0,
+ liquidityUsd: 0,
+ lastUpdated: `${getCanonicalPriceSnapshotGeneratedAt()}T00:00:00.000Z`,
+ }
+ : undefined;
+ const registryFamily = getTokenRegistryFamily(spec);
+ const originalLogoURI = getLogoUriForSpec(spec);
+ const logoURI = absoluteReportLogoUri(originalLogoURI, spec.symbol);
+ const supplyProof = buildSupplyProofEnrichment(chainId, address, spec.symbol, spec.type, registryFamily, market?.priceUsd);
+ if (supplyProof?.marketCapUsd !== undefined && market) {
+ market.marketCapUsd = supplyProof.marketCapUsd;
+ }
+
+ const dbPoolEntries: ReportPoolEntry[] = resolvedPools.map((p) => ({
+ poolAddress: p.poolAddress,
+ dex: p.dex,
+ token0: p.token0.address,
+ token1: p.token1.address,
+ token0Symbol: p.token0.symbol,
+ token1Symbol: p.token1.symbol,
+ tvl: p.tvl,
+ volume24h: p.volume24h,
+ source: 'indexed-db',
+ status: 'indexed',
+ statusReason: 'Pool is present in the token-aggregation indexed pool repository.',
+ }));
+ const liveUniswapV2PoolEntries = buildLiveUniswapV2FallbackPoolsForToken(chainId, address, dbPoolEntries);
+ const gruV2FallbackPoolEntries = await buildGruV2FallbackPoolsForToken(chainId, address, [
+ ...dbPoolEntries,
+ ...liveUniswapV2PoolEntries,
+ ]);
+ const reportPools = [
+ ...dbPoolEntries,
+ ...liveUniswapV2PoolEntries,
+ ...gruV2FallbackPoolEntries,
+ ];
+
out.push({
chainId,
address: address.toLowerCase(),
@@ -106,42 +683,22 @@ async function buildTokenReport(chainId: number) {
type: spec.type,
decimals: spec.decimals,
currencyCode: spec.currencyCode,
- registryFamily: getTokenRegistryFamily(spec),
+ registryFamily,
familySymbol: spec.familySymbol,
deploymentVersion: spec.deploymentVersion,
deploymentStatus: spec.deploymentStatus,
preferredForX402: spec.preferredForX402,
liquiditySourceSymbol: spec.liquiditySourceSymbol,
- market: marketData
- ? {
- priceUsd: marketData.priceUsd ?? fallbackPriceUsd,
- volume24h: marketData.volume24h,
- volume7d: marketData.volume7d,
- volume30d: marketData.volume30d,
- marketCapUsd: marketData.marketCapUsd,
- liquidityUsd: marketData.liquidityUsd,
- lastUpdated: marketData.lastUpdated?.toISOString() ?? '',
- }
- : fallbackPriceUsd !== undefined
- ? {
- priceUsd: fallbackPriceUsd,
- volume24h: 0,
- volume7d: 0,
- volume30d: 0,
- liquidityUsd: 0,
- lastUpdated: `${getCanonicalPriceSnapshotGeneratedAt()}T00:00:00.000Z`,
- }
- : undefined,
- pools: resolvedPools.map((p) => ({
- poolAddress: p.poolAddress,
- dex: p.dex,
- token0: p.token0.address,
- token1: p.token1.address,
- token0Symbol: p.token0.symbol,
- token1Symbol: p.token1.symbol,
- tvl: p.tvl,
- volume24h: p.volume24h,
- })),
+ logoURI,
+ originalLogoURI,
+ market,
+ totalSupply: supplyProof?.totalSupply,
+ totalSupplyRaw: supplyProof?.totalSupplyRaw,
+ circulatingSupply: supplyProof?.circulatingSupply,
+ circulatingSupplyFormula: supplyProof?.circulatingSupplyFormula,
+ supplyProofProvenance: supplyProof?.supplyProofProvenance,
+ trackerCaveats: supplyProof?.trackerCaveats,
+ pools: reportPools,
fromDb: !!dbToken,
});
}
@@ -167,15 +724,42 @@ function describeToken(spec: { currencyCode?: string; registryFamily?: string })
function buildGruTransportOverview() {
const gruTransportMetadata = getGruTransportMetadata();
- if (!gruTransportMetadata) return undefined;
+ const deploymentStatus = loadDeploymentStatusFile()?.data;
+
+ if (!gruTransportMetadata && !deploymentStatus) return undefined;
+
+ const activeTransportPairs = getActiveTransportPairs();
+ const fallbackGasAssetFamilies = deploymentStatus ? buildGasAssetFamiliesFromDeploymentStatus(deploymentStatus) : [];
+ const fallbackRuntimePairs = deploymentStatus ? buildGasRuntimePairsFromDeploymentStatus(deploymentStatus) : [];
+ const gasAssetFamilies = gruTransportMetadata?.gasAssetFamilies ?? fallbackGasAssetFamilies;
+ const gasRedeemGroups = gruTransportMetadata?.gasRedeemGroups ?? [];
+ const gasProtocolExposure = gruTransportMetadata?.gasProtocolExposure ?? [];
+ const runtimePairs = activeTransportPairs.length > 0 ? activeTransportPairs : fallbackRuntimePairs;
return {
- system: gruTransportMetadata.system,
- summary: gruTransportMetadata.counts,
- gasAssetFamilies: gruTransportMetadata.gasAssetFamilies ?? [],
- gasRedeemGroups: gruTransportMetadata.gasRedeemGroups ?? [],
- gasProtocolExposure: gruTransportMetadata.gasProtocolExposure ?? [],
- activeTransportPairs: getActiveTransportPairs().map((pair) => ({
+ system: gruTransportMetadata?.system ?? {
+ name: 'GRU Monetary Transport Layer',
+ shortName: 'GRU Transport',
+ canonicalChainId: 138,
+ canonicalChainName: 'DeFi Oracle Meta Chain 138',
+ transportClass: 'deployment-status-fallback',
+ },
+ summary: gruTransportMetadata?.counts ?? {
+ enabledCanonicalTokens: 0,
+ enabledDestinationChains: new Set(runtimePairs.map((pair) => pair.destinationChainId)).size,
+ approvedBridgePeers: 0,
+ transportPairs: runtimePairs.length,
+ gasAssetFamilies: gasAssetFamilies.length,
+ gasRedeemGroups: gasRedeemGroups.length,
+ gasProtocolExposure: gasProtocolExposure.length,
+ gasTransportPairs: runtimePairs.length,
+ runtimeReadyTransportPairs: runtimePairs.filter((pair) => pair.runtimeReady === true).length,
+ publicPools: 0,
+ },
+ gasAssetFamilies,
+ gasRedeemGroups,
+ gasProtocolExposure,
+ activeTransportPairs: runtimePairs.map((pair) => ({
key: pair.key,
canonicalSymbol: pair.canonicalSymbol,
mirroredSymbol: pair.mirroredSymbol,
@@ -200,6 +784,173 @@ function buildGruTransportOverview() {
};
}
+function gasFamilyDefaults(familyKey: string, mirroredSymbol: string) {
+ const byFamily: Record = {
+ eth_mainnet: {
+ canonicalSymbol138: 'cETH',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'eth_mainnet',
+ },
+ eth_l2: {
+ canonicalSymbol138: 'cETHL2',
+ assetClass: 'gas_native',
+ backingMode: 'hybrid_cap',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'eth_l2',
+ },
+ bnb: {
+ canonicalSymbol138: 'cBNB',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'bnb',
+ },
+ pol: {
+ canonicalSymbol138: 'cPOL',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'pol',
+ },
+ avax: {
+ canonicalSymbol138: 'cAVAX',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'avax',
+ },
+ cro: {
+ canonicalSymbol138: 'cCRO',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'cro',
+ },
+ xdai: {
+ canonicalSymbol138: 'cXDAI',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'xdai',
+ },
+ celo: {
+ canonicalSymbol138: 'cCELO',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'celo',
+ },
+ wemix: {
+ canonicalSymbol138: 'cWEMIX',
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: 'wemix',
+ },
+ };
+
+ return byFamily[familyKey] ?? {
+ canonicalSymbol138: mirroredSymbol.replace(/^cW/, 'c'),
+ assetClass: 'gas_native',
+ backingMode: 'strict_escrow',
+ redeemPolicy: 'same_family_only',
+ laneGroup: familyKey,
+ };
+}
+
+function buildGasAssetFamiliesFromDeploymentStatus(data: DeploymentStatusFile): GruTransportGasAssetFamily[] {
+ const byFamily = new Map();
+
+ for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
+ const chainId = Number(chainIdText);
+ for (const pool of chain.gasPmmPools ?? []) {
+ const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
+ const mirroredSymbol = typeof pool.base === 'string' ? pool.base : '';
+ if (!familyKey || !mirroredSymbol) continue;
+ const defaults = gasFamilyDefaults(familyKey, mirroredSymbol);
+ const existing = byFamily.get(familyKey) ?? {
+ familyKey,
+ active: true,
+ status: 'deployment_status_fallback',
+ canonicalSymbol138: defaults.canonicalSymbol138,
+ mirroredSymbol,
+ assetClass: defaults.assetClass,
+ originChains: [],
+ laneGroup: defaults.laneGroup,
+ backingMode: defaults.backingMode,
+ redeemPolicy: defaults.redeemPolicy,
+ wrappedNativeQuoteSymbol: 'WETH',
+ stableQuoteSymbol: 'USDC',
+ referenceVenue: 'deployment-status',
+ };
+ if (!existing.originChains.includes(chainId)) {
+ existing.originChains.push(chainId);
+ }
+ if (typeof pool.quote === 'string' && ['WETH', 'WBNB', 'WPOL', 'WAVAX', 'WCRO', 'WXDAI', 'CELO', 'WEMIX'].includes(pool.quote)) {
+ existing.wrappedNativeQuoteSymbol = pool.quote;
+ }
+ if (typeof pool.quote === 'string' && ['USDC', 'USDT', 'DAI'].includes(pool.quote)) {
+ existing.stableQuoteSymbol = pool.quote;
+ }
+ byFamily.set(familyKey, existing);
+ }
+ }
+
+ return Array.from(byFamily.values()).sort((a, b) => a.familyKey.localeCompare(b.familyKey));
+}
+
+function buildGasRuntimePairsFromDeploymentStatus(data: DeploymentStatusFile): GruTransportPair[] {
+ const familiesByKey = new Map(buildGasAssetFamiliesFromDeploymentStatus(data).map((family) => [family.familyKey, family]));
+ const pairs: GruTransportPair[] = [];
+
+ for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
+ const destinationChainId = Number(chainIdText);
+ const seen = new Set();
+ for (const pool of chain.gasPmmPools ?? []) {
+ const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
+ const mirroredSymbol = typeof pool.base === 'string' ? pool.base : '';
+ if (!familyKey || !mirroredSymbol || seen.has(familyKey)) continue;
+ seen.add(familyKey);
+ const family = familiesByKey.get(familyKey);
+ const canonicalSymbol = family?.canonicalSymbol138 ?? gasFamilyDefaults(familyKey, mirroredSymbol).canonicalSymbol138;
+ pairs.push({
+ key: `138-${destinationChainId}-${canonicalSymbol}-${mirroredSymbol}`,
+ canonicalChainId: 138,
+ destinationChainId,
+ destinationChainName: chain.name ?? `Chain ${chainIdText}`,
+ active: true,
+ status: 'deployment_status_fallback',
+ canonicalSymbol,
+ mirroredSymbol,
+ mappingKey: `${canonicalSymbol}:${destinationChainId}`,
+ peerKey: `chain-${destinationChainId}`,
+ assetClass: 'gas_native',
+ familyKey,
+ laneGroup: family?.laneGroup,
+ backingMode: family?.backingMode,
+ redeemPolicy: family?.redeemPolicy,
+ wrappedNativeQuoteSymbol: family?.wrappedNativeQuoteSymbol,
+ stableQuoteSymbol: family?.stableQuoteSymbol,
+ mirrorDeploymentAddress: chain.gasMirrors?.[mirroredSymbol],
+ mirrorDeployed: !!chain.gasMirrors?.[mirroredSymbol],
+ canonicalEnabled: true,
+ destinationEnabled: true,
+ bridgeAvailable: chain.bridgeAvailable === true,
+ bridgePeerConfigured: chain.bridgeAvailable === true,
+ runtimeBridgeReady: chain.bridgeAvailable === true,
+ runtimeReady: chain.bridgeAvailable === true && !!chain.gasMirrors?.[mirroredSymbol],
+ eligible: true,
+ runtimeMissingRequirements: chain.bridgeAvailable === true ? [] : ['bridgeAvailable is not true in deployment-status'],
+ eligibilityBlockers: [],
+ });
+ }
+ }
+
+ return pairs.sort((a, b) => a.destinationChainId - b.destinationChainId || a.familyKey!.localeCompare(b.familyKey!));
+}
+
function buildCanonicalCwFallback(chainIdFilter?: number | null): CwRegistryChain[] {
const grouped = new Map();
@@ -230,6 +981,42 @@ function buildCanonicalCwFallback(chainIdFilter?: number | null): CwRegistryChai
.sort((a, b) => a.chainId - b.chainId);
}
+/** GET /report/assets/* — packaged report assets such as controlled token logos. */
+router.get(/^\/assets\/(.+)$/, (req: Request, res: Response) => {
+ const assetPath = String(req.params[0] ?? '');
+ const publicRoot = path.resolve(__dirname, '../../../public');
+ const resolved = path.resolve(publicRoot, assetPath);
+
+ if (!resolved.startsWith(publicRoot) || !existsSync(resolved)) {
+ res.status(404).json({ error: 'Asset not found' });
+ return;
+ }
+
+ res.set('Cache-Control', 'public, max-age=86400, immutable');
+ res.sendFile(resolved);
+});
+
+/** GET /report/logo/:symbol — extensionless public logo route for proxies that block static file extensions. */
+router.get('/logo/:symbol', (req: Request, res: Response) => {
+ const rawSymbol = String(req.params.symbol ?? '').trim();
+ const publicRoot = path.resolve(__dirname, '../../../public/token-logos');
+ const gruSymbol = rawSymbol.replace(/^cW(?=USD|EUR|GBP|AUD|JPY|CHF|CAD|XAU)/, 'c');
+ const candidates = [
+ rawSymbol === 'cWBTC' ? path.join(publicRoot, 'trustwallet/btc-logo.png') : '',
+ rawSymbol.startsWith('ipfs-') ? path.join(publicRoot, 'ipfs', rawSymbol.replace(/^ipfs-/, '')) : '',
+ path.join(publicRoot, 'gru', `${gruSymbol}.svg`),
+ ].filter(Boolean);
+
+ const resolved = candidates.find((candidate) => existsSync(candidate));
+ if (!resolved) {
+ res.status(404).json({ error: 'Logo not found' });
+ return;
+ }
+
+ res.set('Cache-Control', 'public, max-age=86400, immutable');
+ res.sendFile(resolved);
+});
+
/** GET /report/cross-chain — cross-chain pools, bridge volume, atomic swaps (Chain 138, ALL Mainnet) */
router.get(
'/cross-chain',
@@ -338,7 +1125,15 @@ router.get(
name: t.name,
asset_platform_id: chainId === 138 ? 'defi-oracle-meta' : chainId === 651940 ? 'all-mainnet' : `chain-${chainId}`,
decimals: t.decimals,
+ logo_uri: t.logoURI,
+ original_logo_uri: t.originalLogoURI,
description: describeToken(t),
+ total_supply: t.totalSupply ? Number(t.totalSupply) : undefined,
+ total_supply_raw: t.totalSupplyRaw,
+ circulating_supply: t.circulatingSupply ? Number(t.circulatingSupply) : undefined,
+ circulating_supply_formula: t.circulatingSupplyFormula,
+ supply_proof_provenance: t.supplyProofProvenance,
+ tracker_caveats: t.trackerCaveats,
market_data: t.market
? {
current_price: { usd: t.market.priceUsd },
@@ -353,6 +1148,14 @@ router.get(
dex_id: p.dex,
tvl_usd: p.tvl,
volume_24h_usd: p.volume24h,
+ source: p.source,
+ status: p.status,
+ status_reason: p.statusReason,
+ role: p.role,
+ section: p.section,
+ public_routing_enabled: p.publicRoutingEnabled,
+ base_symbol: p.token0Symbol,
+ quote_symbol: p.token1Symbol,
})),
}));
@@ -395,6 +1198,14 @@ router.get(
symbol: t.symbol,
name: t.name,
decimals: t.decimals,
+ logo_url: t.logoURI,
+ original_logo_url: t.originalLogoURI,
+ total_supply: t.totalSupply ? Number(t.totalSupply) : undefined,
+ total_supply_raw: t.totalSupplyRaw,
+ circulating_supply: t.circulatingSupply ? Number(t.circulatingSupply) : undefined,
+ circulating_supply_formula: t.circulatingSupplyFormula,
+ supply_proof_provenance: t.supplyProofProvenance,
+ tracker_caveats: t.trackerCaveats,
volume_24h: t.market?.volume24h,
market_cap: t.market?.marketCapUsd,
liquidity_usd: t.market?.liquidityUsd ?? t.pools.reduce((s, p) => s + p.tvl, 0),
@@ -405,6 +1216,12 @@ router.get(
quote: p.token0 === t.address ? p.token1 : p.token0,
liquidity_usd: p.tvl,
volume_24h_usd: p.volume24h,
+ source: p.source,
+ status: p.status,
+ status_reason: p.statusReason,
+ role: p.role,
+ section: p.section,
+ public_routing_enabled: p.publicRoutingEnabled,
})),
}));
@@ -456,12 +1273,16 @@ router.get(
if (!isNaN(chainIdFilter as number)) {
tokens = tokens.filter((t) => t.chainId === chainIdFilter);
}
+ const normalizedTokens = tokens.map((token) => ({
+ ...token,
+ logoURI: absolutePublicUrl(req, typeof token.logoURI === 'string' ? token.logoURI : undefined) ?? token.logoURI,
+ }));
return res.json({
name: data.name ?? 'Token List',
version: data.version ?? '1.0.0',
timestamp: data.timestamp ?? new Date().toISOString(),
- logoURI: data.logoURI,
- tokens,
+ logoURI: absolutePublicUrl(req, data.logoURI) ?? data.logoURI,
+ tokens: normalizedTokens,
});
} catch (err) {
logger.error('TOKEN_LIST_JSON_URL fetch failed, using built-in token list:', err);
@@ -481,6 +1302,7 @@ router.get(
decimals: number;
type: string;
logoURI: string;
+ originalLogoURI?: string;
registryFamily?: string;
familySymbol?: string;
deploymentVersion?: string;
@@ -493,6 +1315,7 @@ router.get(
for (const spec of specs) {
const address = spec.addresses[chainId];
if (address) {
+ const originalLogoURI = getLogoUriForSpec(spec);
list.push({
chainId,
address: address.toLowerCase(),
@@ -500,7 +1323,8 @@ router.get(
name: spec.name,
decimals: spec.decimals,
type: spec.type,
- logoURI: getLogoUriForSpec(spec),
+ logoURI: absoluteLogoUri(req, originalLogoURI, spec.symbol),
+ originalLogoURI,
registryFamily: getTokenRegistryFamily(spec),
familySymbol: spec.familySymbol,
deploymentVersion: spec.deploymentVersion,
@@ -515,7 +1339,7 @@ router.get(
name: 'GRU Canonical Token List',
version: '1.0.0',
timestamp: new Date().toISOString(),
- logoURI: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png',
+ logoURI: absolutePublicUrl(req, DBIS_CHAIN_138_LOGO_PATH),
tokens: list,
});
} catch (error) {
@@ -586,6 +1410,150 @@ router.get('/gru-v2-pmm-pools', async (req: Request, res: Response) => {
}
});
+/** GET /report/adoption-readiness — summary gates for institutional and tracker/listing readiness. */
+router.get('/adoption-readiness', async (_req: Request, res: Response) => {
+ try {
+ const chainIds = getSupportedChainIds();
+ const tokensByChain: Record>> = {};
+ for (const chainId of chainIds) {
+ tokensByChain[chainId] = await buildTokenReport(chainId);
+ }
+
+ const allTokens = Object.values(tokensByChain).flat();
+ const candidates = allTokens.filter((token) =>
+ isGruSupplyTrackedCandidate(token.symbol, token.type, token.registryFamily)
+ );
+ const nonReportablePlaceholder = candidates.filter(
+ (token) => token.supplyProofProvenance?.source === 'deterministic-placeholder-address'
+ );
+ const reportableCandidates = candidates.filter(
+ (token) => token.supplyProofProvenance?.source !== 'deterministic-placeholder-address'
+ );
+ const proved = reportableCandidates.filter((token) => token.supplyProofProvenance && token.supplyProofProvenance.source !== 'missing-supply-proof');
+ const proofRequired = reportableCandidates.filter((token) => token.supplyProofProvenance?.source === 'missing-supply-proof');
+ const silent = reportableCandidates.filter((token) => !token.supplyProofProvenance);
+ const externalOfficialQuoteLiquidity = reportableCandidates.filter(hasExternalOfficialQuoteLiquidity);
+ const liquidityPositive = reportableCandidates.filter(
+ (token) => (token.market?.liquidityUsd ?? 0) > 0 || token.pools.some((pool) => pool.tvl > 0) || hasExternalOfficialQuoteLiquidity(token)
+ );
+ const poolIndexed = reportableCandidates.filter((token) => token.pools.length > 0);
+ const liquidityMissing = reportableCandidates.filter(
+ (token) => (token.market?.liquidityUsd ?? 0) <= 0 && !token.pools.some((pool) => pool.tvl > 0) && !hasExternalOfficialQuoteLiquidity(token)
+ );
+ const liquidityMissingWithPools = liquidityMissing.filter((token) => token.pools.length > 0);
+ const liquidityMissingWithoutPools = liquidityMissing.filter((token) => token.pools.length === 0);
+ const gruPools = getGruV2DeploymentPoolRows();
+ const groupTokensByChain = (tokens: typeof candidates) =>
+ tokens
+ .reduce>((groups, token) => {
+ let group = groups.find((row) => row.chainId === token.chainId);
+ if (!group) {
+ group = { chainId: token.chainId, count: 0, symbols: [] };
+ groups.push(group);
+ }
+ group.count += 1;
+ group.symbols.push(token.symbol);
+ return groups;
+ }, [])
+ .map((group) => ({
+ ...group,
+ symbols: Array.from(new Set(group.symbols)).sort(),
+ }))
+ .sort((a, b) => a.chainId - b.chainId);
+
+ res.set('Cache-Control', 'public, max-age=0, must-revalidate');
+ res.json({
+ generatedAt: new Date().toISOString(),
+ scope: 'gru-c-and-cw-assets',
+ counts: {
+ candidates: candidates.length,
+ reportableCandidates: reportableCandidates.length,
+ nonReportablePlaceholder: nonReportablePlaceholder.length,
+ proved: proved.length,
+ proofRequired: proofRequired.length,
+ silent: silent.length,
+ poolIndexed: poolIndexed.length,
+ liquidityPositive: liquidityPositive.length,
+ liquidityMissing: liquidityMissing.length,
+ liquidityMissingWithPools: liquidityMissingWithPools.length,
+ liquidityMissingWithoutPools: liquidityMissingWithoutPools.length,
+ externalOfficialQuoteLiquidity: externalOfficialQuoteLiquidity.length,
+ gruV2Pools: gruPools.length,
+ gruV2PoolsWithStatus: gruPools.filter((pool) => !!pool.status).length,
+ },
+ institutional: {
+ score: Math.round(
+ 100 *
+ ((silent.length === 0 ? 0.25 : 0) +
+ (proved.length / Math.max(reportableCandidates.length, 1)) * 0.35 +
+ (poolIndexed.length / Math.max(reportableCandidates.length, 1)) * 0.2 +
+ (gruPools.length > 0 && gruPools.every((pool) => !!pool.status) ? 0.2 : 0))
+ ),
+ blockers: [
+ proofRequired.length > 0 ? `${proofRequired.length} assets still require supply-proof artifacts.` : null,
+ silent.length > 0 ? `${silent.length} assets have no proof state.` : null,
+ gruPools.some((pool) => !pool.status) ? 'One or more GRU v2 pools lacks lifecycle status.' : null,
+ ].filter(Boolean),
+ },
+ cryptoListing: {
+ score: Math.round(
+ 100 *
+ ((proved.length / Math.max(reportableCandidates.length, 1)) * 0.45 +
+ (liquidityPositive.length / Math.max(reportableCandidates.length, 1)) * 0.35 +
+ (poolIndexed.length / Math.max(reportableCandidates.length, 1)) * 0.2)
+ ),
+ blockers: [
+ proofRequired.length > 0 ? 'Most c*/cW* assets are proof-gated for supply and market-cap claims.' : null,
+ liquidityPositive.length < reportableCandidates.length ? 'Not every reportable c*/cW* asset has positive indexed liquidity in the report API.' : null,
+ ].filter(Boolean),
+ },
+ blockerInventory: {
+ proofRequiredByChain: groupTokensByChain(proofRequired),
+ liquidityMissingByChain: groupTokensByChain(liquidityMissing),
+ liquidityMissingWithPoolsByChain: groupTokensByChain(liquidityMissingWithPools),
+ liquidityMissingWithoutPoolsByChain: groupTokensByChain(liquidityMissingWithoutPools),
+ liquidityMissingDetails: liquidityMissing
+ .map((token) => ({
+ chainId: token.chainId,
+ symbol: token.symbol,
+ address: token.address,
+ poolCount: token.pools.length,
+ zeroTvlPoolCount: token.pools.filter((pool) => pool.tvl <= 0).length,
+ marketLiquidityUsd: token.market?.liquidityUsd ?? 0,
+ category: token.pools.length > 0 ? 'configured_or_indexed_pools_zero_tvl' : 'no_visible_pool_binding',
+ }))
+ .sort((a, b) => a.chainId - b.chainId || a.symbol.localeCompare(b.symbol)),
+ externalOfficialQuoteLiquidityByChain: groupTokensByChain(externalOfficialQuoteLiquidity),
+ nonReportablePlaceholderByChain: groupTokensByChain(nonReportablePlaceholder),
+ gruV2PoolsMissingStatus: gruPools
+ .filter((pool) => !pool.status)
+ .map((pool) => ({
+ chainId: pool.chainId,
+ poolAddress: pool.poolAddress,
+ baseSymbol: pool.baseSymbol,
+ quoteSymbol: pool.quoteSymbol,
+ })),
+ notes: [
+ 'proof_required means the report intentionally withholds totalSupply, circulatingSupply, and marketCap claims for that asset.',
+ 'nonReportablePlaceholder means the configured address is intentionally visible for roadmap traceability but is not a deployed ERC-20 proof surface.',
+ 'liquidityMissing means no positive indexed liquidity is currently visible through the report API for that asset.',
+ 'External official quote mirrors (public-chain USDC/USDT and ALL AUSDC/AUSDT) are not GRU pool blockers when their canonical token binding is proved; their public venue liquidity is maintained by the issuer/external market and should not be confused with repo-native cW*/Chain 138 liquidity.',
+ 'A configured pool or registry entry is not the same thing as tracker-grade supply proof or live positive liquidity.',
+ ],
+ },
+ proofRequiredSample: proofRequired.slice(0, 20).map((token) => ({
+ chainId: token.chainId,
+ symbol: token.symbol,
+ address: token.address,
+ status: token.supplyProofProvenance?.status,
+ })),
+ });
+ } catch (error) {
+ logger.error('Error building report/adoption-readiness:', error);
+ res.status(500).json({ error: 'Internal server error' });
+ }
+});
+
/** GET /report/gas-registry — live gas-family rollout registry from deployment-status.json plus GRU transport metadata. */
router.get('/gas-registry', async (req: Request, res: Response) => {
try {
@@ -593,7 +1561,9 @@ router.get('/gas-registry', async (req: Request, res: Response) => {
const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
const fileBackedRegistry = loadDeploymentStatusFile();
const gruTransport = buildGruTransportOverview();
- const runtimeGasPairs = getActiveTransportPairs()
+ const loaderRuntimePairs = getActiveTransportPairs();
+ const fallbackRuntimePairs = fileBackedRegistry ? buildGasRuntimePairsFromDeploymentStatus(fileBackedRegistry.data) : [];
+ const runtimeGasPairs = (loaderRuntimePairs.length > 0 ? loaderRuntimePairs : fallbackRuntimePairs)
.filter((pair) => pair.assetClass === 'gas_native')
.map((pair) => ({
key: pair.key,
diff --git a/services/token-aggregation/src/api/server.ts b/services/token-aggregation/src/api/server.ts
index b9f3aa2..ee70ff3 100644
--- a/services/token-aggregation/src/api/server.ts
+++ b/services/token-aggregation/src/api/server.ts
@@ -1,4 +1,5 @@
import express, { Express, Request, Response, NextFunction } from 'express';
+import { Server } from 'http';
import path from 'path';
import { readFileSync, existsSync } from 'fs';
import cors from 'cors';
@@ -48,6 +49,7 @@ export class ApiServer {
private indexerEnabled: boolean;
private indexer: MultiChainIndexer | null;
private omnlPoller: OmnlEventPoller | null;
+ private server: Server | null;
private resolveTrustProxySetting(): boolean | number | string {
const raw = (process.env.EXPRESS_TRUST_PROXY ?? process.env.TRUST_PROXY ?? '1').trim();
@@ -65,6 +67,7 @@ export class ApiServer {
this.indexerEnabled = this.resolveFeatureFlag('ENABLE_INDEXER', true);
this.indexer = this.indexerEnabled ? new MultiChainIndexer() : null;
this.omnlPoller = this.resolveFeatureFlag('ENABLE_OMNL_EVENT_POLLER', false) ? new OmnlEventPoller() : null;
+ this.server = null;
this.setupMiddleware();
this.setupRoutes();
@@ -106,6 +109,14 @@ export class ApiServer {
});
next();
});
+
+ const publicPath = path.join(__dirname, '../../public');
+ if (existsSync(publicPath)) {
+ this.app.use('/static', express.static(publicPath, {
+ immutable: true,
+ maxAge: '1d',
+ }));
+ }
}
private setupRoutes(): void {
@@ -151,6 +162,39 @@ export class ApiServer {
res.type('html').send(readFileSync(dashboardPath, 'utf8'));
});
+ // Public API catalog (register before routers so GET /api/v1 is not swallowed by middleware-only mounts)
+ const sendApiV1Catalog = (_req: Request, res: Response) => {
+ res.json({
+ service: 'token-aggregation',
+ version: '1.0.0',
+ note: 'Prefix paths with your public API origin (e.g. https://explorer.d-bis.org).',
+ paths: {
+ catalog: '/api/v1',
+ health: '/health',
+ chains: '/api/v1/chains',
+ networks: '/api/v1/networks',
+ config: '/api/v1/config',
+ tokens: '/api/v1/tokens',
+ tokenDetail: '/api/v1/tokens/{address}',
+ quote: '/api/v1/quote',
+ bridgeRoutes: '/api/v1/bridge/routes',
+ bridgeStatus: '/api/v1/bridge/status',
+ bridgeMetrics: '/api/v1/bridge/metrics',
+ bridgePreflight: '/api/v1/bridge/preflight',
+ tokenMappingPairs: '/api/v1/token-mapping/pairs',
+ tokenMappingResolve: '/api/v1/token-mapping/resolve',
+ reportTokenList: '/api/v1/report/token-list',
+ routesTree: '/api/v1/routes/tree',
+ plannerProvidersCapabilities: '/api/v2/providers/capabilities',
+ plannerRoutesPlan: '/api/v2/routes/plan',
+ plannerIntentsPlan: '/api/v2/intents/plan',
+ plannerInternalExecutionPlan: '/api/v2/routes/internal-execution-plan',
+ },
+ });
+ };
+ this.app.get('/api/v1', sendApiV1Catalog);
+ this.app.get('/api/v1/', sendApiV1Catalog);
+
// API routes
this.app.use('/api/v1', tokenRoutes);
this.app.use('/api/v1', configRoutes);
@@ -206,21 +250,25 @@ export class ApiServer {
async start(): Promise {
try {
+ // Start server
+ this.server = this.app.listen(this.port, () => {
+ logger.info(`Token Aggregation Service listening on port ${this.port}`);
+ logger.info(`Health check: http://localhost:${this.port}/health`);
+ logger.info(`API: http://localhost:${this.port}/api/v1`);
+ });
+
if (this.indexer) {
- await this.indexer.initialize();
- await this.indexer.startAll();
+ this.indexer
+ .initialize()
+ .then(() => this.indexer?.startAll())
+ .catch((error) => {
+ logger.error('Token aggregation indexer failed after API startup:', error);
+ });
} else {
logger.info('Token aggregation indexer disabled by ENABLE_INDEXER flag');
}
this.omnlPoller?.start();
-
- // Start server
- this.app.listen(this.port, () => {
- logger.info(`Token Aggregation Service listening on port ${this.port}`);
- logger.info(`Health check: http://localhost:${this.port}/health`);
- logger.info(`API: http://localhost:${this.port}/api/v1`);
- });
} catch (error) {
logger.error('Failed to start server:', error);
process.exit(1);
@@ -230,6 +278,18 @@ export class ApiServer {
async stop(): Promise {
this.omnlPoller?.stop();
this.indexer?.stopAll();
+ if (this.server) {
+ await new Promise((resolve, reject) => {
+ this.server?.close((error) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ resolve();
+ });
+ });
+ this.server = null;
+ }
logger.info('Server stopped');
}
}
diff --git a/services/token-aggregation/src/config/canonical-tokens.test.ts b/services/token-aggregation/src/config/canonical-tokens.test.ts
index 60f8c59..bbe8858 100644
--- a/services/token-aggregation/src/config/canonical-tokens.test.ts
+++ b/services/token-aggregation/src/config/canonical-tokens.test.ts
@@ -44,6 +44,41 @@ describe('canonical cW token catalog', () => {
expect(getCanonicalTokenByAddress(56, '0xC2FA05F12a75Ac84ea778AF9D6935cA807275E55')?.symbol).toBe('cWUSDW');
});
+ it('keeps Cronos and the broader wrapped fiat/commodity family in the canonical cW mesh', () => {
+ const cronosCwUsdc = getCanonicalTokenBySymbol(25, 'cWUSDC');
+ expect(cronosCwUsdc).toMatchObject({
+ symbol: 'cWUSDC',
+ type: 'w',
+ currencyCode: 'USD',
+ });
+ expect(cronosCwUsdc?.addresses[25]).toBe('0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec');
+ expect(getCanonicalTokenByAddress(25, '0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec')?.symbol).toBe('cWUSDC');
+
+ const expected = [
+ ['cWEURC', 'EUR', '0x7574d37F42528B47c88962931e48FC61608a4050'],
+ ['cWEURT', 'EUR', '0x9f833b4f1012F52eb3317b09922a79c6EdFca77D'],
+ ['cWGBPC', 'GBP', '0xe5c65A76A541368d3061fe9E7A2140cABB903dbF'],
+ ['cWGBPT', 'GBP', '0xBb58fa16bAc8E789f09C14243adEE6480D8213A2'],
+ ['cWAUDC', 'AUD', '0xff3084410A732231472Ee9f93F5855dA89CC5254'],
+ ['cWJPYC', 'JPY', '0x52aD62B8bD01154e2A4E067F8Dc4144C9988d203'],
+ ['cWCHFC', 'CHF', '0xB55F49D6316322d5caA96D34C6e4b1003BD3E670'],
+ ['cWCADC', 'CAD', '0x32aD687F24F77bF8C86605c202c829163Ac5Ab36'],
+ ['cWXAUC', 'XAU', '0xf1B771c95573113E993374c0c7cB2dc1a7908B12'],
+ ['cWXAUT', 'XAU', '0xD517C0cF7013f988946A468c880Cc9F8e2A4BCbE'],
+ ] as const;
+
+ for (const [symbol, currencyCode, cronosAddress] of expected) {
+ const token = getCanonicalTokenBySymbol(25, symbol);
+ expect(token).toMatchObject({
+ symbol,
+ type: 'w',
+ currencyCode,
+ });
+ expect(token?.addresses[25]).toBe(cronosAddress);
+ expect(getCanonicalTokenByAddress(25, cronosAddress)?.symbol).toBe(symbol);
+ }
+ });
+
it('surfaces cUSDW on Chain 138 as the repo-native USDW hub asset', () => {
const cusdw = getCanonicalTokenBySymbol(138, 'cUSDW');
expect(cusdw).toMatchObject({
diff --git a/services/token-aggregation/src/config/canonical-tokens.ts b/services/token-aggregation/src/config/canonical-tokens.ts
index 09f755e..ddde2cd 100644
--- a/services/token-aggregation/src/config/canonical-tokens.ts
+++ b/services/token-aggregation/src/config/canonical-tokens.ts
@@ -70,7 +70,7 @@ const LEGACY_CHAIN_ENV_SUFFIX: Partial> = {
};
/** L2/mainnet chain IDs for cUSDT/cUSDC multichain (env: CUSDT_ADDRESS_56, CUSDC_ADDRESS_137, etc.) */
const L2_CHAIN_IDS = [1, 56, 137, 10, 42161, 8453, 43114, 25, 100, 42220, 1111] as const;
-const GRU_CW_CHAIN_IDS = [1, 56, 137, 10, 42161, 8453, 43114, 100, 42220] as const;
+const GRU_CW_CHAIN_IDS = [1, 10, 25, 56, 100, 137, 8453, 42161, 42220, 43114] as const;
const BTC_CW_CHAIN_IDS = [1, 10, 25, 56, 100, 137, 42161, 42220, 43114, 8453, 1111] as const;
const ETH_MAINNET_CW_CHAIN_IDS = [1] as const;
const ETH_L2_CW_CHAIN_IDS = [10, 42161, 8453] as const;
@@ -165,33 +165,156 @@ const FALLBACK_ADDRESSES: Record>> = {
cWAUSDT: {
[56]: '0xe1a51Bc037a79AB36767561B147eb41780124934',
[137]: '0xf12e262F85107df26741726b074606CaFa24AAe7',
- [43114]: '0xff3084410A732231472Ee9f93F5855dA89CC5254',
[42220]: '0xC158b6cD3A3088C52F797D41f5Aa02825361629e',
+ [43114]: '0xff3084410A732231472Ee9f93F5855dA89CC5254',
},
cWUSDC: {
[1]: '0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a',
- [56]: '0x5355148C4740fcc3D7a96F05EdD89AB14851206b',
- [137]: '0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4',
- [100]: '0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4',
[10]: '0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105',
- [42161]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
+ [25]: '0x932566E5bB6BEBF6B035B94f3DE1f75f126304Ec',
+ [56]: '0x5355148C4740fcc3D7a96F05EdD89AB14851206b',
+ [100]: '0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4',
+ [137]: '0xd6969bC19b53f866C64f2148aE271B2Dae0C58E4',
[8453]: '0x377a5FaA3162b3Fc6f4e267301A3c817bAd18105',
- [43114]: '0x0C242b513008Cd49C89078F5aFb237A3112251EB',
+ [42161]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
[42220]: '0x4C38F9A5ed68A04cd28a72E8c68C459Ec34576f3',
+ [43114]: '0x0C242b513008Cd49C89078F5aFb237A3112251EB',
},
cWUSDT: {
[1]: '0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE',
- [56]: '0x9a1D0dBEE997929ED02fD19E0E199704d20914dB',
- [137]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
- [100]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
[10]: '0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6',
- [42161]: '0x73ADaF7dBa95221c080db5631466d2bC54f6a76B',
+ [25]: '0x72948a7a813B60b37Cd0c920C4657DbFF54312b8',
+ [56]: '0x9a1D0dBEE997929ED02fD19E0E199704d20914dB',
+ [100]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
+ [137]: '0x0cb0192C056aa425C557BdeAD8E56C7eEabf7acF',
[8453]: '0x04B2AE3c3bb3d70Df506FAd8717b0FBFC78ED7E6',
- [43114]: '0x8142BA530B08f3950128601F00DaaA678213DFdf',
+ [42161]: '0x73ADaF7dBa95221c080db5631466d2bC54f6a76B',
[42220]: '0x73376eB92c16977B126dB9112936A20Fa0De3442',
+ [43114]: '0x8142BA530B08f3950128601F00DaaA678213DFdf',
+ },
+ cWEURC: {
+ [1]: '0xD4aEAa8cD3fB41Dc8437FaC7639B6d91B60A5e8d',
+ [10]: '0x4ab39b5bab7b463435209a9039bd40cf241f5a82',
+ [25]: '0x7574d37F42528B47c88962931e48FC61608a4050',
+ [56]: '0x50b073d0D1D2f002745cb9FC28a057d5be84911c',
+ [100]: '0x25603ae4bff0b71d637b3573d1b6657f5f6d17ef',
+ [137]: '0x3CD9ee18db7ad13616FCC1c83bC6098e03968E66',
+ [8453]: '0xcb145ba9a370681e3545f60e55621ebf218b1031',
+ [42161]: '0x2a0023ad5ce1ac6072b454575996dffb1bb11b16',
+ [42220]: '0xb6D2f38b9015F32ccE8818509c712264E7fceeD3',
+ [43114]: '0x84353ed1f0c7a703a17abad19b0db15bc9a5e3e5',
+ },
+ cWEURT: {
+ [1]: '0x855d74FFB6CF75721a9bAbc8B2ed35c8119241dC',
+ [10]: '0x6f521cd9fcf7884cd4e9486c7790e818638e09dd',
+ [25]: '0x9f833b4f1012F52eb3317b09922a79c6EdFca77D',
+ [56]: '0x1ED9E491A5eCd53BeF21962A5FCE24880264F63f',
+ [100]: '0x8e54c52d34a684e22865ac9f2d7c27c30561a7b9',
+ [137]: '0xBeF5A0Bcc0E77740c910f197138cdD90F98d2427',
+ [8453]: '0x73e0cf8bf861d376b3a4c87c136f975027f045ff',
+ [42161]: '0x22b98130ab4d9c355512b25ade4c35e75a4e7e89',
+ [42220]: '0x7e6fB8D80f81430e560F8232b2A4fd06249d74ce',
+ [43114]: '0xfc7d256e48253f7a7e08f0e55b9ff7039eb2524c',
+ },
+ cWGBPC: {
+ [1]: '0xc074007dc0bfb384b1cf6426a56287ed23fe4d52',
+ [10]: '0x3f8c409c6072a2b6a4ff17071927ba70f80c725f',
+ [25]: '0xe5c65A76A541368d3061fe9E7A2140cABB903dbF',
+ [56]: '0x8b6EE72001cAFcb21D56a6c4686D6Db951d499A6',
+ [100]: '0x4d9bc6c74ba65e37c4139f0aec9fc5ddff28dcc4',
+ [137]: '0x948690147D2e50ffe50C5d38C14125aD6a9FA036',
+ [8453]: '0x2a0023ad5ce1ac6072b454575996dffb1bb11b16',
+ [42161]: '0xa846aead3071df1b6439d5d813156ace7c2c1da1',
+ [42220]: '0xE37c332a88f112F9e039C5d92D821402A89c7052',
+ [43114]: '0xbdf0c4ea1d81e8e769b0f41389a2c733e3ff723e',
+ },
+ cWGBPT: {
+ [1]: '0x1dDF9970F01c76A692Fdba2706203E6f16e0C46F',
+ [10]: '0x456373d095d6b9260f01709f93fccf1d8aa14d11',
+ [25]: '0xBb58fa16bAc8E789f09C14243adEE6480D8213A2',
+ [56]: '0xA6eFb8783C8ad2740ec880e46D4f7E608E893B1B',
+ [100]: '0x9f6d2578003fe04e58a9819a4943732f2a203a61',
+ [137]: '0x58a8D8F78F1B65c06dAd7542eC46b299629A60dd',
+ [8453]: '0x22b98130ab4d9c355512b25ade4c35e75a4e7e89',
+ [42161]: '0x29828e9ab2057cd3df3c9211455ae1f76e53d2af',
+ [42220]: '0x1dBa81f91f1BeC47FFf60eC3e7DeD780ad9968E3',
+ [43114]: '0x4611d3424e059392a52b957e508273bc761c80f2',
+ },
+ cWAUDC: {
+ [1]: '0x5020Db641B3Fc0dAbBc0c688C845bc4E3699f35F',
+ [10]: '0x25603ae4bff0b71d637b3573d1b6657f5f6d17ef',
+ [25]: '0xff3084410A732231472Ee9f93F5855dA89CC5254',
+ [56]: '0x7062f35567BBAb4d98dc33af03B0d14Df42294D5',
+ [100]: '0xddc4063f770f7c49d00b5a10fb552e922aa39b2c',
+ [137]: '0xFb4B6Cc81211F7d886950158294A44C312abCA29',
+ [8453]: '0xa846aead3071df1b6439d5d813156ace7c2c1da1',
+ [42161]: '0xc1535e88578d984f12eab55863376b8d8b9fb05a',
+ [42220]: '0x2d3a2ED4Ca4d69912d217c305EE921609F7906A8',
+ [43114]: '0x04e1e22b0d41e99f4275bd40a50480219bc9a223',
+ },
+ cWJPYC: {
+ [1]: '0x07EEd0D7dD40984e47B9D3a3bdded1c536435582',
+ [10]: '0x8e54c52d34a684e22865ac9f2d7c27c30561a7b9',
+ [25]: '0x52aD62B8bD01154e2A4E067F8Dc4144C9988d203',
+ [56]: '0x5fbCE65524211BC1bFb0309fd9EE09E786c6D097',
+ [100]: '0x145e8e8c49b6a021969dd9d2c01c8fea44374f61',
+ [137]: '0xf9f5D0ACD71C76F9476F10B3F3d3E201F0883C68',
+ [8453]: '0x29828e9ab2057cd3df3c9211455ae1f76e53d2af',
+ [42161]: '0xdc383c489533a4dd9a6bd3007386e25d5078b878',
+ [42220]: '0x0b39F47D2E68aB0eB18d4b637Bbd1dD8E97cFbB5',
+ [43114]: '0x3714b1a312e0916c7dcdc4edf480fc0339e59a59',
+ },
+ cWCHFC: {
+ [1]: '0x0F91C5E6Ddd46403746aAC970D05d70FFe404780',
+ [10]: '0x4d9bc6c74ba65e37c4139f0aec9fc5ddff28dcc4',
+ [25]: '0xB55F49D6316322d5caA96D34C6e4b1003BD3E670',
+ [56]: '0xD9f8710caeeBA3b3D423D7D14a918701426B5ef3',
+ [100]: '0x46d90d7947f1139477c206c39268923b99cf09e4',
+ [137]: '0xeE17bB0322383fecCA2784fbE2d4CD7d02b1905B',
+ [8453]: '0xc1535e88578d984f12eab55863376b8d8b9fb05a',
+ [42161]: '0x7e4b4682453bcce19ec903fb69153d3031986bc4',
+ [42220]: '0x8142BA530B08f3950128601F00DaaA678213DFdf',
+ [43114]: '0xc2fa05f12a75ac84ea778af9d6935ca807275e55',
+ },
+ cWCADC: {
+ [1]: '0x209FE32fe7B541751D190ae4e50cd005DcF8EDb4',
+ [10]: '0x9f6d2578003fe04e58a9819a4943732f2a203a61',
+ [25]: '0x32aD687F24F77bF8C86605c202c829163Ac5Ab36',
+ [56]: '0x9AE7a6B311584D60Fa93f973950d609061875775',
+ [100]: '0xa7133c78e0ec74503a5941bcbd44257615b6b4f6',
+ [137]: '0xc9750828124D4c10e7a6f4B655cA8487bD3842EB',
+ [8453]: '0xdc383c489533a4dd9a6bd3007386e25d5078b878',
+ [42161]: '0xcc6ae6016d564e9ab82aaff44d65e05a9b18951c',
+ [42220]: '0x0C242b513008Cd49C89078F5aFb237A3112251EB',
+ [43114]: '0x1872e033b30f3ce0498847926857433e0146394e',
+ },
+ cWXAUC: {
+ [1]: '0x572Be0fa8CA0534d642A567CEDb398B771D8a715',
+ [10]: '0xddc4063f770f7c49d00b5a10fb552e922aa39b2c',
+ [25]: '0xf1B771c95573113E993374c0c7cB2dc1a7908B12',
+ [56]: '0xCB145bA9A370681e3545F60e55621eBf218B1031',
+ [100]: '0x23873b85cfeb343eb952618e8c9e9bfb7f6a0d45',
+ [137]: '0x328Cd365Bb35524297E68ED28c6fF2C9557d1363',
+ [8453]: '0x7e4b4682453bcce19ec903fb69153d3031986bc4',
+ [42161]: '0xa7762b63c4871581885ad17c5714ebb286a7480b',
+ [42220]: '0x61D642979eD75c1325f35b9275C5A7FE97F22451',
+ [43114]: '0x4f95297c23d9f4a1032b1c6a2e553225cb175bee',
+ },
+ cWXAUT: {
+ [1]: '0xACE1DBF857549a11aF1322e1f91F2F64b029c906',
+ [10]: '0x145e8e8c49b6a021969dd9d2c01c8fea44374f61',
+ [25]: '0xD517C0cF7013f988946A468c880Cc9F8e2A4BCbE',
+ [56]: '0x73E0CF8BF861D376B3a4C87c136F975027f045ff',
+ [100]: '0xc6189d404dc60cae7b48e2190e44770a03193e5f',
+ [137]: '0x9e6044d730d4183bF7a666293d257d035Fba6d44',
+ [8453]: '0xcc6ae6016d564e9ab82aaff44d65e05a9b18951c',
+ [42161]: '0x66568899ffe8f00b25dc470e878b65a478994e76',
+ [42220]: '0x30751782486eed825187C1EAe5DE4b4baD428AaE',
+ [43114]: '0xd2b4dbf2f6bd6704e066d752eec61fb0be953fd3',
},
cWUSDW: {
[56]: '0xC2FA05F12a75Ac84ea778AF9D6935cA807275E55',
+ [42220]: '0x176a1b6Aa59F24B3aa65F2b697AB262Bca9093B5',
[43114]: '0xcfdCe5E660FC2C8052BDfa7aEa1865DD753411Ae',
},
cWBTC: {
@@ -236,7 +359,7 @@ const FALLBACK_ADDRESSES: Record>> = {
cWWEMIX: {
[1111]: '0xc111000000000000000000000000000000000457',
},
- // Compliant Fiat on Chain 138 — from DeployCompliantFiatTokens (2026-02-27)
+ // Compliant Fiat on Chain 138 - from DeployCompliantFiatTokens (2026-02-27)
cEURC: { [CHAIN_138]: '0x8085961F9cF02b4d800A3c6d386D31da4B34266a' },
cEURT: { [CHAIN_138]: '0xdf4b71c61E5912712C1Bdd451416B9aC26949d72' },
cGBPC: { [CHAIN_138]: '0x003960f16D9d34F2e98d62723B6721Fb92074aD2' },
@@ -247,9 +370,10 @@ const FALLBACK_ADDRESSES: Record>> = {
cCADC: { [CHAIN_138]: '0x54dBd40cF05e15906A2C21f600937e96787f5679' },
cXAUC: { [CHAIN_138]: '0x290E52a8819A4fbD0714E517225429aA2B70EC6b' },
cXAUT: { [CHAIN_138]: '0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E' },
+ LINK: { [CHAIN_138]: '0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03' },
WETH: { [CHAIN_138]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },
WETH10: { [CHAIN_138]: '0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f' },
- // ISO-4217W on Cronos (25) — from DeployISO4217WSystem
+ // ISO-4217W on Cronos (25) - from DeployISO4217WSystem
USDW: { [CHAIN_25]: '0x948690147D2e50ffe50C5d38C14125aD6a9FA036' },
EURW: { [CHAIN_25]: '0x58a8D8F78F1B65c06dAd7542eC46b299629A60dd' },
GBPW: { [CHAIN_25]: '0xFb4B6Cc81211F7d886950158294A44C312abCA29' },
@@ -451,6 +575,16 @@ export const CANONICAL_TOKENS: CanonicalTokenSpec[] = [
{ symbol: 'cWAUSDT', name: 'Alltra USD Token (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'USD', description: 'Public-network mirrored transport form for the live Chain 138 cAUSDT surface.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWAUSDT', id)])) } },
{ symbol: 'cWUSDC', name: 'USD Coin (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'USD', description: 'Public-network mirrored transport form of canonical Chain 138 cUSDC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWUSDC', id)])) } },
{ symbol: 'cWUSDT', name: 'Tether USD (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'USD', description: 'Public-network mirrored transport form of canonical Chain 138 cUSDT.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWUSDT', id)])) } },
+ { symbol: 'cWEURC', name: 'Euro Coin (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'EUR', description: 'Public-network mirrored transport form of canonical Chain 138 cEURC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWEURC', id)])) } },
+ { symbol: 'cWEURT', name: 'Tether EUR (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'EUR', description: 'Public-network mirrored transport form of canonical Chain 138 cEURT.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWEURT', id)])) } },
+ { symbol: 'cWGBPC', name: 'Pound Sterling (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'GBP', description: 'Public-network mirrored transport form of canonical Chain 138 cGBPC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWGBPC', id)])) } },
+ { symbol: 'cWGBPT', name: 'Tether GBP (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'GBP', description: 'Public-network mirrored transport form of canonical Chain 138 cGBPT.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWGBPT', id)])) } },
+ { symbol: 'cWAUDC', name: 'Australian Dollar (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'AUD', description: 'Public-network mirrored transport form of canonical Chain 138 cAUDC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWAUDC', id)])) } },
+ { symbol: 'cWJPYC', name: 'Japanese Yen (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'JPY', description: 'Public-network mirrored transport form of canonical Chain 138 cJPYC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWJPYC', id)])) } },
+ { symbol: 'cWCHFC', name: 'Swiss Franc (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'CHF', description: 'Public-network mirrored transport form of canonical Chain 138 cCHFC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWCHFC', id)])) } },
+ { symbol: 'cWCADC', name: 'Canadian Dollar (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'CAD', description: 'Public-network mirrored transport form of canonical Chain 138 cCADC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWCADC', id)])) } },
+ { symbol: 'cWXAUC', name: 'Gold (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'XAU', registryFamily: 'commodity', description: 'Public-network mirrored transport form of canonical Chain 138 cXAUC.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWXAUC', id)])) } },
+ { symbol: 'cWXAUT', name: 'Tether XAU (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'XAU', registryFamily: 'commodity', description: 'Public-network mirrored transport form of canonical Chain 138 cXAUT.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWXAUT', id)])) } },
{ symbol: 'cWUSDW', name: 'USD W (Compliant Wrapped ISO-4217 M1)', type: 'w', decimals: 6, currencyCode: 'USD', description: 'Public-network mirrored transport form of canonical Chain 138 cUSDW.', addresses: { ...Object.fromEntries(GRU_CW_CHAIN_IDS.map((id) => [id, addr('cWUSDW', id)])) } },
{
symbol: 'WETH',
@@ -472,6 +606,16 @@ export const CANONICAL_TOKENS: CanonicalTokenSpec[] = [
description: 'Chain 138 WETH10 pilot wrapped ETH surface used by DODO v3 routing and flash-capable paths.',
addresses: { [CHAIN_138]: addr('WETH10', CHAIN_138) || '' },
},
+ {
+ symbol: 'LINK',
+ name: 'Chainlink Token',
+ type: 'base',
+ decimals: 18,
+ currencyCode: 'LINK',
+ registryFamily: 'unclassified',
+ description: 'Chain 138 LINK token used for CCIP and oracle fee accounting.',
+ addresses: { [CHAIN_138]: addr('LINK', CHAIN_138) || '' },
+ },
{
symbol: 'cWBTC',
name: 'Bitcoin (Compliant Wrapped Monetary Unit)',
@@ -730,7 +874,18 @@ export function resolveCanonicalQuoteAddress(chainId: number, address: string):
const IPFS_GATEWAY = 'https://ipfs.io/ipfs';
const GRU_LOGO_BASE =
'https://raw.githubusercontent.com/Order-of-Hospitallers/proxmox-cp/main/token-lists/logos/gru';
-const ETH_LOGO = `${IPFS_GATEWAY}/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong`;
+const ETH_LOGO = '/api/v1/report/logo/ETH';
+const LINK_LOGO = '/api/v1/report/logo/LINK';
+const GAS_NATIVE_LOGO_BY_CODE: Record = {
+ ETH: ETH_LOGO,
+ BNB: '/api/v1/report/logo/BNB',
+ POL: '/api/v1/report/logo/POL',
+ AVAX: '/api/v1/report/logo/AVAX',
+ CRO: '/api/v1/report/logo/CRO',
+ XDAI: '/api/v1/report/logo/XDAI',
+ CELO: '/api/v1/report/logo/CELO',
+ WEMIX: '/api/v1/report/logo/WEMIX',
+};
const USDC_LOGO = `${GRU_LOGO_BASE}/cUSDC.svg`;
const USDT_LOGO = `${GRU_LOGO_BASE}/cUSDT.svg`;
const BTC_LOGO = 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/bitcoin/info/logo.png';
@@ -750,8 +905,10 @@ const LOGO_BY_SYMBOL: Record = {
cWUSDC: USDC_LOGO,
cWUSDT: USDT_LOGO,
cWUSDW: USDC_LOGO,
+ cWEMIX: GAS_NATIVE_LOGO_BY_CODE.WEMIX,
WETH: ETH_LOGO,
WETH10: ETH_LOGO,
+ LINK: LINK_LOGO,
cEURC: `${GRU_LOGO_BASE}/cEURC.svg`,
cEURT: `${GRU_LOGO_BASE}/cEURT.svg`,
cGBPC: `${GRU_LOGO_BASE}/cGBPC.svg`,
@@ -781,6 +938,15 @@ export function getLogoUriForSpec(spec: CanonicalTokenSpec): string {
if (spec.logoUrl) return spec.logoUrl;
const bySymbol = LOGO_BY_SYMBOL[spec.symbol];
if (bySymbol) return bySymbol;
+ const gasLogo = spec.registryFamily === 'gas_native' && spec.currencyCode
+ ? GAS_NATIVE_LOGO_BY_CODE[spec.currencyCode.toUpperCase()]
+ : undefined;
+ if (gasLogo) return gasLogo;
+ if (spec.symbol.startsWith('cW')) {
+ const hubSymbol = `c${spec.symbol.slice(2)}`;
+ const hubSpec = CANONICAL_TOKENS.find((t) => t.symbol === hubSymbol);
+ if (hubSpec && hubSpec.symbol !== spec.symbol) return getLogoUriForSpec(hubSpec);
+ }
if (spec.symbol.startsWith('ac')) return getLogoUriForSpec(CANONICAL_TOKENS.find((t) => t.symbol === spec.symbol.replace('ac', 'c')) || spec);
if (spec.symbol.startsWith('vdc') || spec.symbol.startsWith('sdc')) {
const base = spec.symbol.replace(/^(vd|sd)c/, 'c');
diff --git a/services/token-aggregation/src/config/chains.ts b/services/token-aggregation/src/config/chains.ts
index c82958c..be12534 100644
--- a/services/token-aggregation/src/config/chains.ts
+++ b/services/token-aggregation/src/config/chains.ts
@@ -81,7 +81,7 @@ export const CHAIN_CONFIGS: Record = {
137: {
chainId: 137,
name: 'Polygon',
- rpcUrl: process.env.CHAIN_137_RPC_URL || 'https://polygon-rpc.com',
+ rpcUrl: process.env.CHAIN_137_RPC_URL || 'https://polygon-bor-rpc.publicnode.com',
explorerUrl: 'https://polygonscan.com',
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
blockTime: 2,
diff --git a/services/token-aggregation/src/config/deployment-status.ts b/services/token-aggregation/src/config/deployment-status.ts
index 3de3873..5d2d905 100644
--- a/services/token-aggregation/src/config/deployment-status.ts
+++ b/services/token-aggregation/src/config/deployment-status.ts
@@ -69,6 +69,9 @@ function buildDeploymentStatusCandidates(): string[] {
process.env.DEPLOYMENT_STATUS_JSON_PATH,
process.env.CW_REGISTRY_JSON_PATH,
process.env.CROSS_CHAIN_PMM_DEPLOYMENT_STATUS_PATH,
+ process.env.PROXMOX_REPO_ROOT
+ ? path.resolve(process.env.PROXMOX_REPO_ROOT, 'cross-chain-pmm-lps/config/deployment-status.json')
+ : undefined,
path.resolve(process.cwd(), 'cross-chain-pmm-lps/config/deployment-status.json'),
path.resolve(process.cwd(), '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
path.resolve(process.cwd(), '..', '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
diff --git a/services/token-aggregation/src/config/gru-v2-deployment-pools.ts b/services/token-aggregation/src/config/gru-v2-deployment-pools.ts
index 95ed486..def187a 100644
--- a/services/token-aggregation/src/config/gru-v2-deployment-pools.ts
+++ b/services/token-aggregation/src/config/gru-v2-deployment-pools.ts
@@ -7,6 +7,8 @@ export interface GruV2DeploymentPoolRow {
chainId: number;
chainName: string;
section: GruV2PmmSection;
+ status: 'live' | 'routing_enabled' | 'configured' | 'proof_required';
+ statusReason: string;
baseSymbol: string;
quoteSymbol: string;
baseAddress: string;
@@ -101,6 +103,16 @@ export function buildGruV2PoolRegistryFromDeploymentData(data: DeploymentStatusF
chainId,
chainName,
section,
+ status:
+ pool.publicRoutingEnabled === true
+ ? 'routing_enabled'
+ : poolAddress.startsWith('0x')
+ ? 'live'
+ : 'configured',
+ statusReason:
+ pool.publicRoutingEnabled === true
+ ? 'Pool address is configured in deployment-status and public routing is enabled.'
+ : 'Pool address is configured in deployment-status; routing enablement is not asserted.',
baseSymbol,
quoteSymbol,
baseAddress,
diff --git a/services/token-aggregation/src/config/networks.ts b/services/token-aggregation/src/config/networks.ts
index e1d52ce..8967f35 100644
--- a/services/token-aggregation/src/config/networks.ts
+++ b/services/token-aggregation/src/config/networks.ts
@@ -34,8 +34,9 @@ export const NETWORKS: NetworkEntry[] = [
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
blockExplorerUrls: ['https://explorer.d-bis.org'],
iconUrls: [
+ 'https://explorer.d-bis.org/api/v1/report/logo/chain-138',
+ 'https://explorer.d-bis.org/token-icons/chain-138.png',
'https://explorer.d-bis.org/favicon.ico',
- 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png',
],
oracles: [
{ name: 'ETH/USD', address: '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6', decimals: 8 },
@@ -87,7 +88,13 @@ export const NETWORKS: NetworkEntry[] = [
chainId: '0x89',
chainIdDecimal: 137,
chainName: 'Polygon',
- rpcUrls: ['https://polygon-rpc.com', 'https://rpc.ankr.com/polygon'],
+ rpcUrls: [
+ 'https://polygon-bor-rpc.publicnode.com',
+ 'https://1rpc.io/matic',
+ 'https://polygon.drpc.org',
+ 'https://polygon-rpc.com',
+ 'https://rpc.ankr.com/polygon',
+ ],
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
blockExplorerUrls: ['https://polygonscan.com'],
iconUrls: ['https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/info/logo.png'],
diff --git a/services/token-aggregation/src/database/client.ts b/services/token-aggregation/src/database/client.ts
index 7844e97..a9b5e7c 100644
--- a/services/token-aggregation/src/database/client.ts
+++ b/services/token-aggregation/src/database/client.ts
@@ -26,6 +26,7 @@ export function getDatabasePool(): Pool {
connectionString: process.env.DATABASE_URL,
min: parseInt(process.env.DATABASE_POOL_MIN || '2', 10),
max: parseInt(process.env.DATABASE_POOL_MAX || '10', 10),
+ connectionTimeoutMillis: parseInt(process.env.DATABASE_CONNECTION_TIMEOUT_MS || '3000', 10),
};
// If connectionString is not provided, use individual config
diff --git a/services/token-aggregation/src/database/repositories/market-data-repo.ts b/services/token-aggregation/src/database/repositories/market-data-repo.ts
index 917c700..37c697d 100644
--- a/services/token-aggregation/src/database/repositories/market-data-repo.ts
+++ b/services/token-aggregation/src/database/repositories/market-data-repo.ts
@@ -19,7 +19,16 @@ export class MarketDataRepository {
return code === '42P01' || (message.includes('relation "') && message.includes('" does not exist'));
}
+ private shouldUseReadFallback(error: unknown): boolean {
+ if (this.isMissingRelationError(error)) return true;
+ if (String(process.env.TOKEN_AGGREGATION_DB_READ_FALLBACK ?? '1').toLowerCase() === '0') return false;
+ const message = (error as { message?: string })?.message || '';
+ const code = (error as { code?: string })?.code || '';
+ return ['ETIMEDOUT', 'ECONNREFUSED', 'ENOTFOUND'].includes(code) || /timeout|connect/i.test(message);
+ }
+
async getMarketData(chainId: number, tokenAddress: string): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return null;
try {
const result = await this.pool.query(
`SELECT chain_id, token_address, price_usd, price_change_24h, volume_24h, volume_7d, volume_30d,
@@ -49,7 +58,7 @@ export class MarketDataRepository {
lastUpdated: row.last_updated,
};
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return null;
}
throw error;
@@ -99,6 +108,7 @@ export class MarketDataRepository {
}
async getTopTokensByVolume(chainId: number, limit: number = 50): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return [];
try {
const result = await this.pool.query(
`SELECT chain_id, token_address, price_usd, price_change_24h, volume_24h, volume_7d, volume_30d,
@@ -125,7 +135,7 @@ export class MarketDataRepository {
lastUpdated: row.last_updated,
}));
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return [];
}
throw error;
@@ -133,6 +143,7 @@ export class MarketDataRepository {
}
async getTopTokensByLiquidity(chainId: number, limit: number = 50): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return [];
try {
const result = await this.pool.query(
`SELECT chain_id, token_address, price_usd, price_change_24h, volume_24h, volume_7d, volume_30d,
@@ -159,7 +170,7 @@ export class MarketDataRepository {
lastUpdated: row.last_updated,
}));
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return [];
}
throw error;
diff --git a/services/token-aggregation/src/database/repositories/pool-repo.ts b/services/token-aggregation/src/database/repositories/pool-repo.ts
index 4c5a28b..e4143c2 100644
--- a/services/token-aggregation/src/database/repositories/pool-repo.ts
+++ b/services/token-aggregation/src/database/repositories/pool-repo.ts
@@ -53,7 +53,16 @@ export class PoolRepository {
return code === '42P01' || message.includes('relation "') && message.includes('" does not exist');
}
+ private shouldUseReadFallback(error: unknown): boolean {
+ if (this.isMissingRelationError(error)) return true;
+ if (String(process.env.TOKEN_AGGREGATION_DB_READ_FALLBACK ?? '1').toLowerCase() === '0') return false;
+ const message = (error as { message?: string })?.message || '';
+ const code = (error as { code?: string })?.code || '';
+ return ['ETIMEDOUT', 'ECONNREFUSED', 'ENOTFOUND'].includes(code) || /timeout|connect/i.test(message);
+ }
+
async getPool(chainId: number, poolAddress: string): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return null;
try {
const result = await this.pool.query(
`SELECT id, chain_id, pool_address, token0_address, token1_address, dex_type,
@@ -70,7 +79,7 @@ export class PoolRepository {
return this.mapRowToPool(result.rows[0]);
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return null;
}
throw error;
@@ -78,6 +87,7 @@ export class PoolRepository {
}
async getPoolsByChain(chainId: number, limit: number = 500): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return [];
try {
const result = await this.pool.query(
`SELECT id, chain_id, pool_address, token0_address, token1_address, dex_type,
@@ -91,7 +101,7 @@ export class PoolRepository {
);
return result.rows.map((row) => this.mapRowToPool(row));
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return [];
}
throw error;
@@ -99,6 +109,7 @@ export class PoolRepository {
}
async getPoolsByToken(chainId: number, tokenAddress: string): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return [];
try {
const result = await this.pool.query(
`SELECT id, chain_id, pool_address, token0_address, token1_address, dex_type,
@@ -112,7 +123,7 @@ export class PoolRepository {
return result.rows.map((row) => this.mapRowToPool(row));
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return [];
}
throw error;
diff --git a/services/token-aggregation/src/database/repositories/token-repo.ts b/services/token-aggregation/src/database/repositories/token-repo.ts
index be4b039..71f5c02 100644
--- a/services/token-aggregation/src/database/repositories/token-repo.ts
+++ b/services/token-aggregation/src/database/repositories/token-repo.ts
@@ -46,7 +46,16 @@ export class TokenRepository {
return code === '42P01' || (message.includes('relation "') && message.includes('" does not exist'));
}
+ private shouldUseReadFallback(error: unknown): boolean {
+ if (this.isMissingRelationError(error)) return true;
+ if (String(process.env.TOKEN_AGGREGATION_DB_READ_FALLBACK ?? '1').toLowerCase() === '0') return false;
+ const message = (error as { message?: string })?.message || '';
+ const code = (error as { code?: string })?.code || '';
+ return ['ETIMEDOUT', 'ECONNREFUSED', 'ENOTFOUND'].includes(code) || /timeout|connect/i.test(message);
+ }
+
async getToken(chainId: number, address: string): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return null;
try {
const result = await this.pool.query(
`SELECT chain_id, address, name, symbol, decimals, total_supply, logo_url, website_url, description, verified
@@ -73,7 +82,7 @@ export class TokenRepository {
verified: row.verified,
};
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return null;
}
throw error;
@@ -81,6 +90,7 @@ export class TokenRepository {
}
async getTokens(chainId: number, limit: number = 50, offset: number = 0): Promise {
+ if (String(process.env.TOKEN_AGGREGATION_SKIP_DB_READS ?? '0').toLowerCase() === '1') return [];
try {
const result = await this.pool.query(
`SELECT chain_id, address, name, symbol, decimals, total_supply, logo_url, website_url, description, verified
@@ -104,7 +114,7 @@ export class TokenRepository {
verified: row.verified,
}));
} catch (error) {
- if (this.isMissingRelationError(error)) {
+ if (this.shouldUseReadFallback(error)) {
return [];
}
throw error;
diff --git a/services/token-aggregation/src/services/pmm-onchain-quote.ts b/services/token-aggregation/src/services/pmm-onchain-quote.ts
index ff20b15..a1c2b5f 100644
--- a/services/token-aggregation/src/services/pmm-onchain-quote.ts
+++ b/services/token-aggregation/src/services/pmm-onchain-quote.ts
@@ -3,6 +3,7 @@ import { Contract, JsonRpcProvider } from 'ethers';
const POOL_ABI = [
'function _BASE_TOKEN_() view returns (address)',
'function _QUOTE_TOKEN_() view returns (address)',
+ 'function getVaultReserve() view returns (uint256,uint256)',
'function querySellBase(address,uint256) view returns (uint256,uint256)',
'function querySellQuote(address,uint256) view returns (uint256,uint256)',
];
@@ -40,6 +41,25 @@ export async function pmmQuoteAmountOutFromChain(params: {
}
}
+/** Best-effort reserve read for DODO-style PMM/DVM pools. */
+export async function pmmVaultReserveFromChain(params: {
+ rpcUrl: string;
+ poolAddress: string;
+}): Promise<{ baseReserveRaw: bigint; quoteReserveRaw: bigint } | null> {
+ const { rpcUrl, poolAddress } = params;
+ try {
+ const provider = new JsonRpcProvider(rpcUrl);
+ const pool = new Contract(poolAddress, POOL_ABI, provider);
+ const [baseReserve, quoteReserve] = await pool.getVaultReserve();
+ return {
+ baseReserveRaw: BigInt(baseReserve.toString()),
+ quoteReserveRaw: BigInt(quoteReserve.toString()),
+ };
+ } catch {
+ return null;
+ }
+}
+
/** RPC for PMM eth_call quotes on Chain 138 (optional; unset = skip on-chain override). */
export function resolvePmmQuoteRpcUrl(): string {
return (
diff --git a/test/flash/DBISEngineXIndexedLiquidityVault.t.sol b/test/flash/DBISEngineXIndexedLiquidityVault.t.sol
new file mode 100644
index 0000000..30d4db7
--- /dev/null
+++ b/test/flash/DBISEngineXIndexedLiquidityVault.t.sol
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {Test} from "forge-std/Test.sol";
+import {DBISEngineXIndexedLiquidityVault} from "../../contracts/flash/DBISEngineXIndexedLiquidityVault.sol";
+import {MockMintableToken} from "../dbis/MockMintableToken.sol";
+
+contract MockEngineXUniswapV3Pool {
+ address public immutable token0;
+ address public immutable token1;
+ uint24 public immutable fee;
+ uint160 public sqrtPriceX96;
+ int24 public tick;
+ uint128 public liquidity;
+
+ constructor(address token0_, address token1_, uint24 fee_) {
+ token0 = token0_;
+ token1 = token1_;
+ fee = fee_;
+ }
+
+ function setSlot0(uint160 sqrtPriceX96_, int24 tick_) external {
+ sqrtPriceX96 = sqrtPriceX96_;
+ tick = tick_;
+ }
+
+ function setLiquidity(uint128 liquidity_) external {
+ liquidity = liquidity_;
+ }
+
+ function slot0() external view returns (uint160, int24, uint16, uint16, uint16, uint8, bool) {
+ return (sqrtPriceX96, tick, 0, 0, 0, 0, true);
+ }
+}
+
+contract DBISEngineXIndexedLiquidityVaultTest is Test {
+ MockMintableToken internal cwusdc;
+ MockMintableToken internal usdc;
+ MockEngineXUniswapV3Pool internal pool;
+ DBISEngineXIndexedLiquidityVault internal vault;
+
+ address internal constant RECIPIENT = address(0xD00D);
+ uint160 internal constant ONE_TO_ONE_SQRT_PRICE_X96 = 79_228_162_514_264_337_593_543_950_336;
+ bytes32 internal constant PROOF_ID = bytes32("indexed-proof");
+ bytes32 internal constant SWAP_TX = bytes32(uint256(0xA1));
+ bytes32 internal constant LIQUIDITY_TX = bytes32(uint256(0xB1));
+ bytes32 internal constant ISO_HASH = bytes32(uint256(0x1001));
+ bytes32 internal constant AUDIT_HASH = bytes32(uint256(0x1002));
+ bytes32 internal constant PEG_HASH = bytes32(uint256(0x1003));
+
+ function setUp() public {
+ cwusdc = new MockMintableToken("Wrapped cWUSDC", "cWUSDC", 6, address(this));
+ usdc = new MockMintableToken("USD Coin", "USDC", 6, address(this));
+ pool = new MockEngineXUniswapV3Pool(address(cwusdc), address(usdc), 100);
+ pool.setSlot0(ONE_TO_ONE_SQRT_PRICE_X96, 0);
+ pool.setLiquidity(1_000_000);
+ cwusdc.mint(address(pool), 100_000_000);
+ usdc.mint(address(pool), 100_000_000);
+
+ vault = new DBISEngineXIndexedLiquidityVault(
+ address(cwusdc), address(usdc), address(pool), address(this), 100, 1_000, 1_000_000
+ );
+ }
+
+ function testRecordIndexedProofAnchorsPublicPoolState() public {
+ DBISEngineXIndexedLiquidityVault.IndexedProof memory proof = _proof(PROOF_ID);
+
+ (uint160 sqrtPriceX96, int24 tick, uint128 liquidity) = vault.recordIndexedProof(proof);
+
+ assertEq(sqrtPriceX96, ONE_TO_ONE_SQRT_PRICE_X96, "sqrt price");
+ assertEq(tick, 0, "tick");
+ assertEq(liquidity, 1_000_000, "liquidity");
+ assertTrue(vault.usedProofIds(PROOF_ID), "proof consumed");
+ }
+
+ function testRejectsDuplicateProofId() public {
+ vault.recordIndexedProof(_proof(PROOF_ID));
+
+ vm.expectRevert(bytes("proof used"));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+ }
+
+ function testRejectsTickDrift() public {
+ pool.setSlot0(ONE_TO_ONE_SQRT_PRICE_X96, 101);
+
+ vm.expectRevert(bytes("tick drift too high"));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+ }
+
+ function testRejectsInsufficientLiquidity() public {
+ pool.setLiquidity(999);
+
+ vm.expectRevert(bytes("insufficient liquidity"));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+ }
+
+ function testRejectsOversizedProofAmount() public {
+ DBISEngineXIndexedLiquidityVault.IndexedProof memory proof = _proof(PROOF_ID);
+ proof.exactOutputAmount = 1_000_001;
+
+ vm.expectRevert(bytes("proof amount too high"));
+ vault.recordIndexedProof(proof);
+ }
+
+ function testOperatorAllowlist() public {
+ vault.setOperatorAllowlistEnabled(true);
+
+ vm.expectRevert(bytes("operator not approved"));
+ vm.prank(address(0xBEEF));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+
+ vault.setOperatorApproved(address(0xBEEF), true);
+ vm.prank(address(0xBEEF));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+ assertTrue(vault.usedProofIds(PROOF_ID), "proof consumed");
+ }
+
+ function testPauseBlocksProofs() public {
+ vault.pause();
+
+ vm.expectRevert(bytes("paused"));
+ vault.recordIndexedProof(_proof(PROOF_ID));
+ }
+
+ function _proof(bytes32 proofId) internal pure returns (DBISEngineXIndexedLiquidityVault.IndexedProof memory) {
+ return DBISEngineXIndexedLiquidityVault.IndexedProof({
+ proofId: proofId,
+ publicSwapTxHash: SWAP_TX,
+ liquidityTxHash: LIQUIDITY_TX,
+ outputRecipient: RECIPIENT,
+ exactOutputAmount: 100_000,
+ iso20022DocumentHash: ISO_HASH,
+ auditEnvelopeHash: AUDIT_HASH,
+ pegProofHash: PEG_HASH
+ });
+ }
+}
diff --git a/test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol b/test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol
new file mode 100644
index 0000000..9874b7a
--- /dev/null
+++ b/test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import {Test} from "forge-std/Test.sol";
+import {DBISEngineXSingleSidedDodoCwusdcVault} from
+ "../../contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol";
+import {MockMintableToken} from "../dbis/MockMintableToken.sol";
+
+contract MockDodoPool {
+ using SafeERC20 for IERC20;
+
+ address public immutable base;
+ address public immutable quote;
+ uint256 public baseReserve;
+ uint256 public quoteReserve;
+
+ constructor(address base_, address quote_) {
+ base = base_;
+ quote = quote_;
+ }
+
+ function _BASE_TOKEN_() external view returns (address) {
+ return base;
+ }
+
+ function _QUOTE_TOKEN_() external view returns (address) {
+ return quote;
+ }
+
+ function querySellBase(address, uint256 payBaseAmount) external pure returns (uint256 receiveQuoteAmount, uint256 mtFee) {
+ return (payBaseAmount, 0);
+ }
+
+ function querySellQuote(address, uint256 payQuoteAmount) external pure returns (uint256 receiveBaseAmount, uint256 mtFee) {
+ return (payQuoteAmount, 0);
+ }
+
+ function getVaultReserve() external view returns (uint256, uint256) {
+ return (baseReserve, quoteReserve);
+ }
+
+ function buyShares(address) external returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare) {
+ uint256 baseBalance = IERC20(base).balanceOf(address(this));
+ uint256 quoteBalance = IERC20(quote).balanceOf(address(this));
+ baseShare = baseBalance - baseReserve;
+ quoteShare = quoteBalance - quoteReserve;
+ baseReserve = baseBalance;
+ quoteReserve = quoteBalance;
+ lpShare = baseShare < quoteShare ? baseShare : quoteShare;
+ }
+}
+
+contract MockDodoIntegration {
+ using SafeERC20 for IERC20;
+
+ function addLiquidity(address pool, uint256 baseAmount, uint256 quoteAmount)
+ external
+ returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare)
+ {
+ require(baseAmount > 0 && quoteAmount > 0, "zero amount");
+ address base = MockDodoPool(pool)._BASE_TOKEN_();
+ address quote = MockDodoPool(pool)._QUOTE_TOKEN_();
+ IERC20(base).safeTransferFrom(msg.sender, pool, baseAmount);
+ IERC20(quote).safeTransferFrom(msg.sender, pool, quoteAmount);
+ return MockDodoPool(pool).buyShares(msg.sender);
+ }
+}
+
+contract DBISEngineXSingleSidedDodoCwusdcVaultTest is Test {
+ MockMintableToken internal cwusdc;
+ MockMintableToken internal weth;
+ MockDodoIntegration internal integration;
+ MockDodoPool internal pool;
+ DBISEngineXSingleSidedDodoCwusdcVault internal vault;
+
+ address internal constant FUNDER = address(0xF00D);
+ address internal constant OWNER = address(0xA11CE);
+
+ function setUp() public {
+ cwusdc = new MockMintableToken("Wrapped cWUSDC", "cWUSDC", 6, address(this));
+ weth = new MockMintableToken("Wrapped Ether", "WETH", 18, address(this));
+ integration = new MockDodoIntegration();
+ pool = new MockDodoPool(address(cwusdc), address(weth));
+ vault = new DBISEngineXSingleSidedDodoCwusdcVault(address(cwusdc), address(weth), address(integration), OWNER);
+
+ cwusdc.mint(FUNDER, 100_000_000);
+ weth.mint(FUNDER, 1 ether);
+ vm.startPrank(FUNDER);
+ cwusdc.approve(address(vault), type(uint256).max);
+ weth.approve(address(vault), type(uint256).max);
+ vm.stopPrank();
+ }
+
+ function testAcceptsSingleSidedCwusdcAsInventoryButNotExecutable() public {
+ vm.prank(FUNDER);
+ vault.depositCwusdc(10_000_000);
+
+ (
+ uint256 cwusdcBalance,
+ uint256 quoteBalance,
+ uint256 cwusdcInventory,
+ uint256 quoteInventory,
+ bool solvent,
+ bool executable
+ ) = vault.solvencyState();
+
+ assertEq(cwusdcBalance, 10_000_000, "cw balance");
+ assertEq(quoteBalance, 0, "quote balance");
+ assertEq(cwusdcInventory, 10_000_000, "cw inventory");
+ assertEq(quoteInventory, 0, "quote inventory");
+ assertTrue(solvent, "single-sided inventory is solvent");
+ assertFalse(executable, "single-sided inventory is not executable DODO liquidity");
+ }
+
+ function testPromoteRequiresTwoSidedInventory() public {
+ vm.prank(OWNER);
+ vault.setDodoPool(address(pool));
+ vm.prank(OWNER);
+ vault.setCanary(1_000, 1_000, 1_000, 1_000);
+
+ vm.prank(FUNDER);
+ vault.depositCwusdc(10_000_000);
+
+ vm.expectRevert(bytes("two-sided required"));
+ vm.prank(OWNER);
+ vault.promoteToDodo(1_000_000, 0, 0, 0, 0);
+
+ vm.expectRevert(bytes("insufficient quote inventory"));
+ vm.prank(OWNER);
+ vault.promoteToDodo(1_000_000, 1, 0, 0, 0);
+ }
+
+ function testPromotesTwoSidedInventoryAndPassesCanary() public {
+ vm.prank(OWNER);
+ vault.setDodoPool(address(pool));
+ vm.prank(OWNER);
+ vault.setCanary(1_000, 1_000, 1_000, 1_000);
+
+ vm.startPrank(FUNDER);
+ vault.depositCwusdc(10_000_000);
+ vault.depositQuote(1 ether);
+ vm.stopPrank();
+
+ vm.prank(OWNER);
+ (uint256 baseShare, uint256 quoteShare, uint256 lpShare) =
+ vault.promoteToDodo(2_000_000, 2_000_000, 2_000_000, 2_000_000, 2_000_000);
+
+ assertEq(baseShare, 2_000_000, "base share");
+ assertEq(quoteShare, 2_000_000, "quote share");
+ assertEq(lpShare, 2_000_000, "lp share");
+ assertEq(vault.accountedCwusdcInventory(), 8_000_000, "remaining cw inventory");
+ assertEq(vault.accountedQuoteInventory(), 1 ether - 2_000_000, "remaining quote inventory");
+ assertEq(cwusdc.balanceOf(address(pool)), 2_000_000, "pool cw balance");
+ assertEq(weth.balanceOf(address(pool)), 2_000_000, "pool quote balance");
+ assertTrue(vault.canaryPasses(), "canary passes");
+ }
+
+ function testCannotRescueAccountedInventory() public {
+ vm.prank(FUNDER);
+ vault.depositCwusdc(10_000_000);
+
+ vm.expectRevert(bytes("cwusdc insolvent"));
+ vm.prank(OWNER);
+ vault.rescueUnaccountedToken(address(cwusdc), OWNER, 1);
+ }
+
+ function testOwnerCanWithdrawAccountedInventory() public {
+ vm.prank(FUNDER);
+ vault.depositCwusdc(10_000_000);
+
+ vm.prank(OWNER);
+ vault.withdrawCwusdcInventory(OWNER, 4_000_000);
+
+ assertEq(vault.accountedCwusdcInventory(), 6_000_000, "inventory decremented");
+ assertEq(cwusdc.balanceOf(OWNER), 4_000_000, "owner received");
+ }
+}
diff --git a/test/flash/DBISEngineXVirtualBatchVault.t.sol b/test/flash/DBISEngineXVirtualBatchVault.t.sol
index 9079a3d..60bbf5d 100644
--- a/test/flash/DBISEngineXVirtualBatchVault.t.sol
+++ b/test/flash/DBISEngineXVirtualBatchVault.t.sol
@@ -2,9 +2,32 @@
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
+import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import {DBISEngineXFlashProofBorrower} from "../../contracts/flash/DBISEngineXFlashProofBorrower.sol";
import {DBISEngineXVirtualBatchVault} from "../../contracts/flash/DBISEngineXVirtualBatchVault.sol";
import {MockMintableToken} from "../dbis/MockMintableToken.sol";
+contract EngineXFlashBorrower is IERC3156FlashBorrower {
+ bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
+ bool public repay = true;
+
+ function setRepay(bool repay_) external {
+ repay = repay_;
+ }
+
+ function onFlashLoan(address, address token, uint256 amount, uint256 fee, bytes calldata)
+ external
+ override
+ returns (bytes32)
+ {
+ if (repay) {
+ IERC20(token).transfer(msg.sender, amount + fee);
+ }
+ return _RETURN_VALUE;
+ }
+}
+
contract DBISEngineXVirtualBatchVaultTest is Test {
MockMintableToken internal cwusdc;
MockMintableToken internal usdc;
@@ -138,15 +161,7 @@ contract DBISEngineXVirtualBatchVaultTest is Test {
);
vm.prank(USER);
vault.runVirtualProofExactOutTo(
- proofId,
- LENDER_USDC,
- 3,
- OUTPUT_RECIPIENT,
- exactOutput,
- ROUNDING_RECEIVER,
- ISO_HASH,
- AUDIT_HASH,
- PEG_HASH
+ proofId, LENDER_USDC, 3, OUTPUT_RECIPIENT, exactOutput, ROUNDING_RECEIVER, ISO_HASH, AUDIT_HASH, PEG_HASH
);
assertEq(vault.poolCwusdcReserve(), LIVE_POOL_RESERVE, "pool cWUSDC reserve should not drift");
@@ -243,4 +258,154 @@ contract DBISEngineXVirtualBatchVaultTest is Test {
PEG_HASH
);
}
+
+ function testWithdrawPoolLiquidityUpdatesAccountingAndPreservesMaintainedPool() public {
+ uint256 withdrawAmount = 10_000_000;
+ uint256 ownerCwusdcBefore = cwusdc.balanceOf(address(this));
+ uint256 ownerUsdcBefore = usdc.balanceOf(address(this));
+
+ vault.withdrawPoolLiquidity(address(this), withdrawAmount, withdrawAmount);
+
+ assertEq(vault.poolCwusdcReserve(), LIVE_POOL_RESERVE - withdrawAmount, "pool cWUSDC accounting");
+ assertEq(vault.poolUsdcReserve(), LIVE_POOL_RESERVE - withdrawAmount, "pool USDC accounting");
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC, "lender accounting unchanged");
+ assertEq(cwusdc.balanceOf(address(this)), ownerCwusdcBefore + withdrawAmount, "owner cWUSDC received");
+ assertEq(usdc.balanceOf(address(this)), ownerUsdcBefore + withdrawAmount, "owner USDC received");
+ }
+
+ function testWithdrawPoolLiquidityRejectsBreakingMaintainedPool() public {
+ vm.expectRevert(bytes("would break maintained pool"));
+ vault.withdrawPoolLiquidity(address(this), 1, 0);
+ }
+
+ function testWithdrawLenderUsdcUpdatesAccounting() public {
+ uint256 withdrawAmount = 1_000_000;
+ uint256 ownerUsdcBefore = usdc.balanceOf(address(this));
+
+ vault.withdrawLenderUsdc(address(this), withdrawAmount);
+
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC - withdrawAmount, "lender accounting");
+ assertEq(vault.poolUsdcReserve(), LIVE_POOL_RESERVE, "pool accounting unchanged");
+ assertEq(usdc.balanceOf(address(this)), ownerUsdcBefore + withdrawAmount, "owner USDC received");
+ }
+
+ function testGenericWithdrawCannotTouchAccountedBalances() public {
+ vm.expectRevert(bytes("accounting undercollateralized"));
+ vault.withdraw(address(usdc), address(this), 1);
+ }
+
+ function testGenericWithdrawCanRescueUnaccountedTokens() public {
+ uint256 dust = 123;
+ usdc.mint(address(vault), dust);
+ uint256 ownerUsdcBefore = usdc.balanceOf(address(this));
+
+ vault.withdraw(address(usdc), address(this), dust);
+
+ assertEq(vault.poolUsdcReserve(), LIVE_POOL_RESERVE, "pool accounting unchanged");
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC, "lender accounting unchanged");
+ assertEq(usdc.balanceOf(address(this)), ownerUsdcBefore + dust, "owner receives unaccounted dust");
+ }
+
+ function testFlashLoanUsesLenderBucketAndCollectsFee() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ uint256 amount = 1_000_000;
+ uint256 fee = vault.flashFee(address(usdc), amount);
+ usdc.mint(address(borrower), fee);
+
+ vm.prank(USER);
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), amount, "");
+
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC + fee, "fee stays in lender bucket");
+ assertEq(vault.totalFlashFeesCollectedUsdc(), fee, "fee accounting");
+ assertEq(usdc.balanceOf(address(vault)), LIVE_POOL_RESERVE + LENDER_USDC + fee, "USDC backing");
+ }
+
+ function testFlashLoanCanPullRepaymentByAllowance() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ uint256 amount = 1_000_000;
+ uint256 fee = vault.flashFee(address(usdc), amount);
+ usdc.mint(address(borrower), fee);
+
+ borrower.setRepay(false);
+ vm.prank(address(borrower));
+ usdc.approve(address(vault), type(uint256).max);
+
+ vm.prank(USER);
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), amount, "");
+
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC + fee, "fee stays in lender bucket");
+ assertEq(vault.totalFlashFeesCollectedUsdc(), fee, "fee accounting");
+ }
+
+ function testFlashLoanRejectsBorrowingPoolUsdc() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+
+ vm.expectRevert(bytes("insufficient lender usdc"));
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), LENDER_USDC + 1, "");
+ }
+
+ function testFlashLoanRejectsUnsupportedToken() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+
+ vm.expectRevert(bytes("unsupported flash token"));
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(cwusdc), 1, "");
+ }
+
+ function testFlashLoanCanBeCapped() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ vault.setMaxFlashLoanAmount(999_999);
+
+ vm.expectRevert(bytes("flash amount too high"));
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), 1_000_000, "");
+ }
+
+ function testFlashLoanAllowlistRejectsUnapprovedBorrower() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ vault.setFlashBorrowerAllowlistEnabled(true);
+
+ vm.expectRevert(bytes("flash borrower not approved"));
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), 1, "");
+ }
+
+ function testFlashLoanAllowlistAllowsApprovedBorrower() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ uint256 amount = 1_000_000;
+ uint256 fee = vault.flashFee(address(usdc), amount);
+ usdc.mint(address(borrower), fee);
+
+ vault.setFlashBorrowerAllowlistEnabled(true);
+ vault.setFlashBorrowerApproved(address(borrower), true);
+
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), amount, "");
+
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC + fee, "fee stays in lender bucket");
+ }
+
+ function testPauseBlocksProofsAndFlashLoans() public {
+ EngineXFlashBorrower borrower = new EngineXFlashBorrower();
+ vault.pause();
+
+ vm.expectRevert(bytes("paused"));
+ vm.prank(USER);
+ vault.runVirtualProof(bytes32("proof-paused"), LENDER_USDC, 1);
+
+ vm.expectRevert(bytes("paused"));
+ vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(usdc), 1, "");
+
+ assertEq(vault.maxFlashLoan(address(usdc)), 0, "paused max flash");
+ }
+
+ function testEngineXFlashProofBorrowerRunsProofFlash() public {
+ DBISEngineXFlashProofBorrower borrower =
+ new DBISEngineXFlashProofBorrower(address(vault), address(usdc), address(this));
+ uint256 amount = 1_000_000;
+ uint256 fee = vault.flashFee(address(usdc), amount);
+ bytes32 proofId = bytes32("flash-proof");
+ usdc.mint(address(borrower), fee);
+
+ borrower.runFlashProof(amount, proofId, ISO_HASH, AUDIT_HASH, PEG_HASH);
+
+ assertTrue(borrower.usedProofIds(proofId), "proof consumed");
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC + fee, "fee stays in lender bucket");
+ }
}
diff --git a/test/flash/DBISEngineXXautUsdcBorrowVault.t.sol b/test/flash/DBISEngineXXautUsdcBorrowVault.t.sol
new file mode 100644
index 0000000..920073f
--- /dev/null
+++ b/test/flash/DBISEngineXXautUsdcBorrowVault.t.sol
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import {Test} from "forge-std/Test.sol";
+import {DBISEngineXXautUsdcBorrowVault} from "../../contracts/flash/DBISEngineXXautUsdcBorrowVault.sol";
+import {MockMintableToken} from "../dbis/MockMintableToken.sol";
+
+contract DBISEngineXXautUsdcBorrowVaultTest is Test {
+ MockMintableToken internal xaut;
+ MockMintableToken internal usdc;
+ MockMintableToken internal cwusdc;
+ DBISEngineXXautUsdcBorrowVault internal vault;
+
+ address internal constant BORROWER = address(0xB0B);
+ address internal constant LENDER = address(0x1EAD);
+ address internal constant LIQUIDATOR = address(0xA11CE);
+ bytes32 internal constant PRICE_SOURCE_HASH = bytes32(uint256(0x5052494345));
+ bytes32 internal constant SWAP_TX = bytes32(uint256(0x51574150));
+ bytes32 internal constant ISO_HASH = bytes32(uint256(0x150));
+ bytes32 internal constant AUDIT_HASH = bytes32(uint256(0xA0017));
+ bytes32 internal constant PEG_HASH = bytes32(uint256(0x9E6));
+
+ uint256 internal constant XAUT_PRICE6 = 3_226_640_000;
+ uint256 internal constant LTV_BPS = 7_500;
+ uint256 internal constant LIQUIDATION_THRESHOLD_BPS = 8_000;
+ uint256 internal constant MIN_HEALTH_FACTOR_BPS = 11_000;
+ uint256 internal constant LIQUIDATION_BONUS_BPS = 500;
+ uint256 internal constant LENDER_USDC = 5_000_000_000;
+
+ event CwusdcSourcedRepay(
+ address indexed account,
+ address indexed payer,
+ uint256 amount,
+ bytes32 indexed publicSwapTxHash,
+ bytes32 iso20022DocumentHash,
+ bytes32 auditEnvelopeHash,
+ bytes32 pegProofHash
+ );
+
+ function setUp() public {
+ xaut = new MockMintableToken("Tether Gold", "XAUt", 6, address(this));
+ usdc = new MockMintableToken("USD Coin", "USDC", 6, address(this));
+ cwusdc = new MockMintableToken("Wrapped cWUSDC", "cWUSDC", 6, address(this));
+
+ vault = new DBISEngineXXautUsdcBorrowVault(
+ address(xaut),
+ address(usdc),
+ address(cwusdc),
+ address(this),
+ XAUT_PRICE6,
+ LTV_BPS,
+ LIQUIDATION_THRESHOLD_BPS,
+ MIN_HEALTH_FACTOR_BPS,
+ LIQUIDATION_BONUS_BPS,
+ 0,
+ PRICE_SOURCE_HASH
+ );
+
+ usdc.mint(LENDER, LENDER_USDC);
+ vm.startPrank(LENDER);
+ usdc.approve(address(vault), type(uint256).max);
+ vault.fundLender(LENDER_USDC);
+ vm.stopPrank();
+
+ xaut.mint(BORROWER, 1_000_000);
+ usdc.mint(BORROWER, 1_000_000_000);
+ vm.startPrank(BORROWER);
+ xaut.approve(address(vault), type(uint256).max);
+ usdc.approve(address(vault), type(uint256).max);
+ vm.stopPrank();
+
+ usdc.mint(LIQUIDATOR, 1_000_000_000);
+ vm.prank(LIQUIDATOR);
+ usdc.approve(address(vault), type(uint256).max);
+ }
+
+ function testBorrowRepayAndWithdrawCollateral() public {
+ vm.startPrank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+ assertEq(vault.collateralValueUsd6(BORROWER), XAUT_PRICE6, "1 XAUt value");
+
+ vault.borrowUsdc(2_000_000_000, BORROWER);
+ assertEq(usdc.balanceOf(BORROWER), 3_000_000_000, "borrowed USDC");
+ assertEq(vault.lenderUsdcAvailable(), 3_000_000_000, "lender bucket lent out");
+ assertEq(vault.healthFactorBps(BORROWER), 12_906, "health factor");
+
+ vault.repayUsdc(2_000_000_000);
+ vault.withdrawCollateral(1_000_000, BORROWER);
+ vm.stopPrank();
+
+ (uint256 collateral, uint256 debt) = vault.positions(BORROWER);
+ assertEq(collateral, 0, "collateral closed");
+ assertEq(debt, 0, "debt closed");
+ assertEq(xaut.balanceOf(BORROWER), 1_000_000, "xaut returned");
+ assertEq(vault.lenderUsdcAvailable(), LENDER_USDC, "lender restored");
+ }
+
+ function testBorrowRejectsDebtAboveEffectiveCollateralLimit() public {
+ vm.startPrank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+
+ vm.expectRevert(bytes("exceeds collateral"));
+ vault.borrowUsdc(2_400_000_000, BORROWER);
+ vm.stopPrank();
+ }
+
+ function testBorrowRejectsGlobalBorrowCap() public {
+ vault.setRiskParams(LTV_BPS, LIQUIDATION_THRESHOLD_BPS, MIN_HEALTH_FACTOR_BPS, LIQUIDATION_BONUS_BPS, 1_000_000_000);
+
+ vm.startPrank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+
+ vm.expectRevert(bytes("max borrow exceeded"));
+ vault.borrowUsdc(1_000_000_001, BORROWER);
+ vm.stopPrank();
+ }
+
+ function testRepayFromCwusdcProofStillSettlesInUsdcAndAnchorsHashes() public {
+ vm.startPrank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+ vault.borrowUsdc(1_000_000_000, BORROWER);
+
+ vm.expectEmit(true, true, true, true, address(vault));
+ emit CwusdcSourcedRepay(BORROWER, BORROWER, 250_000_000, SWAP_TX, ISO_HASH, AUDIT_HASH, PEG_HASH);
+ vault.repayUsdcFromCwusdcProof(250_000_000, SWAP_TX, ISO_HASH, AUDIT_HASH, PEG_HASH);
+ vm.stopPrank();
+
+ (, uint256 debt) = vault.positions(BORROWER);
+ assertEq(debt, 750_000_000, "debt reduced");
+ assertEq(vault.totalCwusdcProofRepayUsdc(), 250_000_000, "proof-sourced repay counter");
+ }
+
+ function testLiquidationAfterPriceDrop() public {
+ vm.startPrank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+ vault.borrowUsdc(2_000_000_000, BORROWER);
+ vm.stopPrank();
+
+ vault.setXautUsdPrice6(2_000_000_000, bytes32(uint256(0x44524f50)));
+ assertEq(vault.healthFactorBps(BORROWER), 8_000, "unhealthy after price drop");
+
+ vm.prank(LIQUIDATOR);
+ uint256 seized = vault.liquidate(BORROWER, 100_000_000);
+
+ assertEq(seized, 52_500, "5 percent bonus on 0.05 XAUt");
+ assertEq(xaut.balanceOf(LIQUIDATOR), 52_500, "liquidator receives XAUt");
+ (, uint256 debt) = vault.positions(BORROWER);
+ assertEq(debt, 1_900_000_000, "debt after partial liquidation");
+ }
+
+ function testOwnerCanWithdrawOnlyUnborrowedLenderUsdc() public {
+ vm.prank(BORROWER);
+ vault.supplyCollateral(1_000_000);
+ vm.prank(BORROWER);
+ vault.borrowUsdc(500_000_000, BORROWER);
+
+ vault.withdrawLenderUsdc(address(this), 4_500_000_000);
+ assertEq(vault.lenderUsdcAvailable(), 0, "available bucket withdrawn");
+
+ vm.expectRevert(bytes("insufficient lender usdc"));
+ vault.withdrawLenderUsdc(address(this), 1);
+ }
+
+ function testPauseBlocksMutableUserFlows() public {
+ vault.pause();
+
+ vm.expectRevert(bytes("paused"));
+ vm.prank(BORROWER);
+ vault.supplyCollateral(1);
+
+ vault.unpause();
+ vm.prank(BORROWER);
+ vault.supplyCollateral(1);
+ }
+}