MaplePool (SyrupUSDC / USDC)
Oracle deep-dive for 0x80ac24aa929eaf5013f6436cda2a7ba190f5cc0b — Maple Finance pool share token.
- Source: ERC-4626 Vault (MaplePool)
- Pair: SyrupUSDC / USDC
- Score: 0.2
- Tier: Very High Risk
- Slug:
erc4626_eth_syrupusdc_usdc
Contract Architecture
MaplePool is an ERC-4626 vault where the share token (SyrupUSDC) represents a claim on USDC held in Maple's lending pool. The exchange rate derives from totalAssets() / totalSupply(), where totalAssets() is sourced from the external manager contract.
The pair is SyrupUSDC / USDC (not SyrupUSDC / USD) because the vault's underlying asset is USDC — convertToAssets() returns USDC amounts, not USD.
Risk Dimensions
| Dimension | Observation | Impact |
|---|---|---|
| Governance / admin | Every action gated by checkCall(functionId) -> IPoolManagerLike(manager).canCall(...) | Centralization / policy risk |
| External dependency | totalAssets() and unrealizedLosses() sourced from manager contract | Share price depends on external accounting |
| Redemption mechanics | processRedeem/processWithdraw, escrow params, exit windows via WithdrawalManager | Not "always redeem at NAV" — queue/window/escrow layer |
| Loss socialization | convertToExitAssets/convertToExitShares subtracts unrealizedLosses() | NAV drops on credit losses |
| Transfer restrictions | transfer/transferFrom gated by checkCall | Permissioned transfers (whitelist, KYC, sanctions) |
| Upgradeability | Pool deployer controls implementation | Manager can alter pool behavior |
Code Anchors
Permission gating — every state-mutating function routes through:
modifier checkCall(uint32 functionId) {
require(IPoolManagerLike(manager).canCall(functionId, msg.sender), "NOT_AUTHORIZED");
_;
}
This means deposits, withdrawals, and transfers are all policy-gated. The pool manager contract is the single point of authority.
Manager dependency — totalAssets() delegates to the manager:
function totalAssets() public view returns (uint256) {
return IPoolManagerLike(manager).totalAssets();
}
The share price is entirely determined by what the manager reports. If the manager contract is upgraded or its accounting logic changes, the exchange rate changes with it.
Loss-aware exit math — redemptions subtract unrealized losses:
function convertToExitAssets(uint256 shares_) public view returns (uint256) {
uint256 assets_ = convertToAssets(shares_);
uint256 losses_ = IPoolManagerLike(manager).unrealizedLosses();
return assets_ - (assets_ * losses_ / totalAssets());
}
This means the effective redemption rate is lower than the accounting rate during periods of credit loss. An oracle using convertToAssets() would overstate the realizable value.
Transfer restrictions — transfer and transferFrom are gated:
function transfer(address to_, uint256 amount_) checkCall(TRANSFER) { ... }
Tokens cannot be freely transferred without pool manager approval. This limits secondary market price discovery.
Practical Guidance
As oracle base (score 0.2): Very high risk. The exchange rate is accounting-driven, manager-controlled, and loss-adjusted. Not suitable as a primary oracle reference.
As collateral: Conservative haircuts required. The redemption rate differs from the accounting rate during loss events, and exit is mediated through queues and escrow windows — not instant.
Monitoring triggers:
unrealizedLosses()increasing (credit deterioration)- Manager contract upgrade events
- WithdrawalManager parameter changes (cooldown, window duration)
totalAssets()diverging from expected accrual trajectory