Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions src/adaptors/3jane-lending/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
const sdk = require('@defillama/sdk');
const axios = require('axios');
const utils = require('../utils');

const CHAIN = 'ethereum';
const PROJECT = '3jane-lending';
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';

const USD3 = '0x056B269Eb1f75477a8666ae8C7fE01b64dD55eCc';
const sUSD3 = '0xf689555121e529Ff0463e191F9Bd9d1E496164a7';
const waUSDC = '0xD4fa2D31b7968E448877f69A96DE69f5de8cD23E';
const MORPHO_CREDIT = '0xDe6e08ac208088cc62812Ba30608D852c6B0EcBc';
const MARKET_ID =
'0xc2c3e4b656f4b82649c8adbe82b3284c85cc7dc57c6dc8df6ca3dad7d2740d75';

const SECONDS_PER_DAY = 86400;
const MARKET_ABI =
'function market(bytes32) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)';
const CONVERT_ABI =
'function convertToAssets(uint256 shares) view returns (uint256)';

const getBlock = async (timestamp) => {
const res = await axios.get(
`https://coins.llama.fi/block/${CHAIN}/${timestamp}`
);
Comment on lines +23 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "axios\.get\(" src/adaptors/3jane-lending/index.js -C2

Repository: DefiLlama/yield-server

Length of output: 214


🏁 Script executed:

rg -n "timeout\s*:" src/adaptors/3jane-lending/index.js -C2

Repository: DefiLlama/yield-server

Length of output: 48


🏁 Script executed:

rg -n "axios\\.defaults" src/adaptors/3jane-lending/index.js

Repository: DefiLlama/yield-server

Length of output: 48


🏁 Script executed:

rg -n "axios.*timeout" src/adaptors/ --max-count=5 -l

Repository: DefiLlama/yield-server

Length of output: 134


🏁 Script executed:

rg -n "axios.*timeout" src/adaptors/pancakeswap-amm/index.js -C1

Repository: DefiLlama/yield-server

Length of output: 242


🏁 Script executed:

rg -n "axios.*timeout" src/adaptors/euler-v2/index.js -C1

Repository: DefiLlama/yield-server

Length of output: 257


Add an explicit HTTP timeout for block lookup.

This external request can hang indefinitely and stall the adapter run if the upstream endpoint is slow/unresponsive. Other adapters in the codebase establish timeout configurations as standard practice.

Suggested fix
 const SECONDS_PER_DAY = 86400;
+const HTTP_TIMEOUT_MS = 10_000;
@@
 const getBlock = async (timestamp) => {
   const res = await axios.get(
-    `https://coins.llama.fi/block/${CHAIN}/${timestamp}`
+    `https://coins.llama.fi/block/${CHAIN}/${timestamp}`,
+    { timeout: HTTP_TIMEOUT_MS }
   );
   return res.data.height;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/3jane-lending/index.js` around lines 23 - 25, The axios GET for
the block lookup (`axios.get` using CHAIN and timestamp) can hang; add an
explicit HTTP timeout (e.g., 3–10s) so the request fails fast instead of
blocking the adapter. Update the call that builds the URL for
`https://coins.llama.fi/block/${CHAIN}/${timestamp}` to pass a timeout option
(or use an axios instance with a default timeout) and ensure any surrounding
logic (e.g., the function performing the lookup) handles the timeout rejection
appropriately.

return res.data.height;
};

const apy = async () => {
const now = Math.floor(Date.now() / 1000);
const block7d = await getBlock(now - 7 * SECONDS_PER_DAY);

// --- TVL ---
const [usd3TotalAssets, susd3TotalAssets, usd3Pps] = await Promise.all([
sdk.api.abi.call({
target: USD3,
abi: 'function totalAssets() view returns (uint256)',
chain: CHAIN,
}),
sdk.api.abi.call({
target: sUSD3,
abi: 'function totalAssets() view returns (uint256)',
chain: CHAIN,
}),
sdk.api.abi.call({
target: USD3,
abi: 'function pricePerShare() view returns (uint256)',
chain: CHAIN,
}),
]);

const usd3TvlUsd = Number(usd3TotalAssets.output) / 1e6;
const susd3TvlUsd =
(Number(susd3TotalAssets.output) / 1e6) *
(Number(usd3Pps.output) / 1e6);

// --- APY ---
// USD3 APY = Aave supply rate (from waUSDC rate growth)
// sUSD3 APY = (credit market supply rate + Aave rate) * leverage
// where leverage = USD3 TVL / sUSD3 TVL
// because 100% performance fee redirects all USD3 vault yield to sUSD3

// Fetch waUSDC rates and MorphoCredit market data at current + 7d ago
const [waRateNow, waRate7d, marketNow, market7d] = await Promise.all([
sdk.api.abi.call({
target: waUSDC,
abi: CONVERT_ABI,
params: [1e6],
chain: CHAIN,
}),
sdk.api.abi.call({
target: waUSDC,
abi: CONVERT_ABI,
params: [1e6],
chain: CHAIN,
block: block7d,
}),
sdk.api.abi.call({
target: MORPHO_CREDIT,
abi: MARKET_ABI,
params: [MARKET_ID],
chain: CHAIN,
}),
sdk.api.abi.call({
target: MORPHO_CREDIT,
abi: MARKET_ABI,
params: [MARKET_ID],
chain: CHAIN,
block: block7d,
}),
]);

// Aave APY from waUSDC rate growth (7d)
const aaveApy =
Number(waRate7d.output) > 0
? (Math.pow(Number(waRateNow.output) / Number(waRate7d.output), 365 / 7) - 1) * 100
: null;

// MorphoCredit supply APY from supply share price growth (7d)
// supply share price = totalSupplyAssets / totalSupplyShares
const sharesNow = Number(marketNow.output.totalSupplyShares);
const shares7d = Number(market7d.output.totalSupplyShares);
const sppNow =
sharesNow > 0
? Number(marketNow.output.totalSupplyAssets) / sharesNow
: null;
const spp7d =
shares7d > 0
? Number(market7d.output.totalSupplyAssets) / shares7d
: null;
const creditApy =
Number.isFinite(sppNow) && Number.isFinite(spp7d) && spp7d > 0
? (Math.pow(sppNow / spp7d, 365 / 7) - 1) * 100
: null;

// sUSD3 gets ALL yield (Aave + credit) from USD3 vault via 100% performance fee
// Levered by the ratio of USD3 TVL to sUSD3 TVL
const leverage = susd3TvlUsd > 0 ? usd3TvlUsd / susd3TvlUsd : 0;
const totalVaultYield = (creditApy != null ? creditApy : 0) + (aaveApy != null ? aaveApy : 0);
const susd3Apy = creditApy != null || aaveApy != null ? totalVaultYield * leverage : null;

return [
{
pool: `${USD3}-${CHAIN}`.toLowerCase(),
chain: utils.formatChain(CHAIN),
project: PROJECT,
symbol: 'USD3',
tvlUsd: usd3TvlUsd,
apyBase: aaveApy,
underlyingTokens: [USDC],
url: 'https://app.3jane.xyz/supply',
},
{
pool: `${sUSD3}-${CHAIN}`.toLowerCase(),
chain: utils.formatChain(CHAIN),
project: PROJECT,
symbol: 'sUSD3',
tvlUsd: susd3TvlUsd,
apyBase: susd3Apy,
underlyingTokens: [USD3],
url: 'https://app.3jane.xyz/supply',
},
].filter((p) => utils.keepFinite(p));
};

module.exports = {
timetravel: false,
apy,
url: 'https://app.3jane.xyz/supply',
};
Loading