Skip to content
Open
Show file tree
Hide file tree
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
71 changes: 37 additions & 34 deletions .github/workflows/check-contributor-format.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Check Contributor Format
name: Check Contributor Format

on:
pull_request:
Expand Down Expand Up @@ -27,27 +27,32 @@ jobs:
const fs = require('fs');
const { execSync } = require('child_process');

const prTitle = context.payload.pull_request.title;
const prTitle = context.payload.pull_request.title;
const prNumber = context.payload.pull_request.number;
const prAuthor = context.payload.pull_request.user.login;

// ── Read file ──────────────────────────────────────────────────
// Read file
const content = fs.readFileSync('CONTRIBUTORS.md', 'utf8');

// ── Get diff (lines added by this PR) ──────────────────────────
// Get diff (lines added by this PR)
let addedLines = '';
try {
execSync(`git fetch origin ${process.env.BASE_REF}`, { stdio: 'pipe' });
const raw = execSync(
`git diff origin/${process.env.BASE_REF} -- CONTRIBUTORS.md`,
{ stdio: 'pipe' }
).toString();
addedLines = raw.split('\n').filter(l => l.startsWith('+')).join('\n');
} catch (_) { /* fall back to whole-file check */ }
addedLines = raw
.split('\n')
.filter((line) => line.startsWith('+') && !line.startsWith('+++'))
.join('\n');
} catch (_) {
// fall back to whole-file check
}

const errors = [];

// ── 1. PR title ────────────────────────────────────────────────
// 1. PR title
const titleOk = /^docs:\s+add\s+.+\s+to\s+contributors$/i.test(prTitle);
if (!titleOk) {
errors.push(
Expand All @@ -56,7 +61,7 @@ jobs:
);
}

// ── 2. CONTRIBUTORS-START / END markers present ────────────────
// 2. CONTRIBUTORS-START / END markers present
const sectionMatch = content.match(
/<!-- CONTRIBUTORS-START -->([\s\S]*?)<!-- CONTRIBUTORS-END -->/
);
Expand All @@ -68,7 +73,7 @@ jobs:
} else {
const section = sectionMatch[1];

// ── 3. No placeholder values left ─────────────────────────
// 3. No placeholder values left
if (section.includes('YOUR_GITHUB_USERNAME')) {
errors.push(
'**Placeholder not replaced**: Replace `YOUR_GITHUB_USERNAME` ' +
Expand All @@ -78,22 +83,22 @@ jobs:
if (section.includes('YOUR_X_HANDLE')) {
errors.push(
'**Placeholder not replaced**: Replace `YOUR_X_HANDLE` with your ' +
'X / Twitter handle, or remove the X badge if you don\'t have one.'
'X / Twitter handle, or remove the X badge if you do not have one.'
);
}
if (section.includes('>Your Name<')) {
errors.push(
'**Placeholder not replaced**: Replace `Your Name` with your actual name.'
);
}
if (/RoleProject 1/.test(section)) {
if (/Role\s+[-—]\s+Project\s+1/.test(section)) {
errors.push(
'**Placeholder not replaced**: Replace `Role Project 1, Project 2` ' +
'**Placeholder not replaced**: Replace `Role - Project 1, Project 2` ' +
'with your real role and project names.'
);
}

// ── 4. At least one div block present ─────────────────────
// 4. At least one div block present
const divBlocks = section.match(
/<div[^>]*style="display:inline-block[^"]*"[\s\S]*?<\/div>/g
) || [];
Expand All @@ -105,8 +110,8 @@ jobs:
);
}

// ── 5. Structural checks on the added lines ────────────────
const checkIn = addedLines || section; // fallback to whole section
// 5. Structural checks on the added lines
const checkIn = addedLines || section;

if (!/https:\/\/github\.com\/[A-Za-z0-9_-]+/.test(checkIn)) {
errors.push(
Expand All @@ -130,33 +135,33 @@ jobs:
'**Missing GitHub badge**: Include the GitHub badge from the template.'
);
}
if (!/<sub>[A-Za-z][\w\s]*.+<\/sub>/.test(checkIn)) {
if (!/<sub>[A-Za-z][\w\s]*\s[-—]\s.+<\/sub>/.test(checkIn)) {
errors.push(
'**Missing role/projects line**: Include a role line such as ' +
'`<sub>Researcher StellarPay, LumenSwap</sub>`.'
'`<sub>Researcher - StellarPay, LumenSwap</sub>`.'
);
}
}

// ── Post or update PR comment ──────────────────────────────────
// Post or update PR comment
const MARKER = '<!-- contributor-format-check -->';

const allComments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
repo: context.repo.repo,
issue_number: prNumber,
});
const existing = allComments.data.find(c => c.body.includes(MARKER));
const existing = allComments.data.find((comment) => comment.body.includes(MARKER));

if (errors.length > 0) {
const body = [
MARKER,
'## Contributor Format Check Failed',
'## Contributor Format Check Failed',
'',
`Hi @${prAuthor}! Your \`CONTRIBUTORS.md\` changes don't follow the required format. Please fix the issues below, then push again.`,
`Hi @${prAuthor}! Your \`CONTRIBUTORS.md\` changes do not follow the required format. Please fix the issues below, then push again.`,
'',
'### Issues found',
errors.map(e => `- ${e}`).join('\n'),
errors.map((entry) => `- ${entry}`).join('\n'),
'',
'---',
'### Required format',
Expand All @@ -175,39 +180,37 @@ jobs:
' <a href="https://github.com/YOUR_GITHUB_USERNAME"><img src="https://img.shields.io/badge/-GitHub-181717?logo=github&logoColor=white&style=flat-square" alt="GitHub" /></a>',
' <a href="https://x.com/YOUR_X_HANDLE"><img src="https://img.shields.io/badge/-X-000000?logo=x&logoColor=white&style=flat-square" alt="X" /></a>',
' <br />',
' <sub>Role Project 1, Project 2</sub>',
' <sub>Role - Project 1, Project 2</sub>',
'</div>',
'```',
'',
'> Remove the X badge line if you don\'t have an X account.',
'> Remove the X badge line if you do not have an X account.',
].join('\n');

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body,
});
}

core.setFailed('Contributor format check failed. See the PR comment for details.');

} else {
// All checks passed – update any previous failure comment
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: `${MARKER}\n## Contributor Format Check Passed\n\nGreat job, @${prAuthor}! Your \`CONTRIBUTORS.md\` entry follows the required format.`,
body: `${MARKER}\n## Contributor Format Check Passed\n\nGreat job, @${prAuthor}! Your \`CONTRIBUTORS.md\` entry follows the required format.`,
});
}
console.log('Contributor format check passed!');
Expand Down
46 changes: 46 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,52 @@ Replace `YOUR_GITHUB_USERNAME`, `Your Name`, `YOUR_X_HANDLE`, and the role/proje
<sub>Researcher — KindFi</sub>
</div>

<div
style="display:inline-block;width:130px;vertical-align:top;text-align:center;margin:8px"
>
<a href="https://github.com/Obiajulu-gif">
<img
src="https://github.com/Obiajulu-gif.png"
width="80"
style="border-radius:50%"
alt="Obiajulu-gif"
/>
<br />
<sub><b>Obiajulu-gif</b></sub>
</a>
<br />
<a href="https://github.com/Obiajulu-gif"
><img
src="https://img.shields.io/badge/-GitHub-181717?logo=github&logoColor=white&style=flat-square"
alt="GitHub"
/></a>
<br />
<sub>Researcher — FxDAO</sub>
</div>

<div
style="display:inline-block;width:130px;vertical-align:top;text-align:center;margin:8px"
>
<a href="https://github.com/Obiajulu-gif">
<img
src="https://github.com/Obiajulu-gif.png"
width="80"
style="border-radius:50%"
alt="Obiajulu-gif"
/>
<br />
<sub><b>Obiajulu-gif</b></sub>
</a>
<br />
<a href="https://github.com/Obiajulu-gif"
><img
src="https://img.shields.io/badge/-GitHub-181717?logo=github&logoColor=white&style=flat-square"
alt="GitHub"
/></a>
<br />
<sub>Researcher — Slender</sub>
</div>

<!-- Add your <div> above this line -->
</div>
<!-- CONTRIBUTORS-END -->
Expand Down
25 changes: 25 additions & 0 deletions web/research/fxdao.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# FxDAO

- Category: defi
- Tags: defi, lending, borrowing, stablecoins, cdp, liquidations, soroban, stellar-wave
- Website: https://fxdao.io
- GitHub: https://github.com/FxDAO
- Logo: https://assets.fxdao.io/brand/FxDAO-logo.png
- Stellar account ID: `GC7JVOXZJSHY3GHKWUJWKIUYWEJ4RZABSRQZQ5JBZEC5QUTYBUHVNIKV`
- Stellar contract ID: `CCUN4RXU5VNDHSF4S4RKV4ZJYMX2YWKOH6L4AKEKVNVDQ7HY5QIAO4UB`

FxDAO is a Stellar-native borrowing protocol centered on collateralized debt positions. Users open Vaults, deposit XLM as collateral, and mint synthetic stablecoins such as USDx, EURx, or GBPx against that position. The protocol documents an issuance ceiling of up to 85% of collateral value, which corresponds to an opening collateral ratio of about 115%, while the minimum collateral ratio before liquidation is currently 110%. That structure makes FxDAO closer to a CDP system than to a pooled money market: the user is not borrowing from a shared lender pool with utilization-driven rates, but minting protocol-issued debt against locked collateral.

The supported collateral is currently narrow by design. FxDAO states that Lumens are the only accepted collateral asset today because XLM is the network's native asset, has the deepest liquidity on Stellar, and avoids issuer-side clawback risk. On the debt side, FxDAO publishes classic-asset issuers and Soroban token contracts for FXG, USDx, EURx, and GBPx, plus a Soroban Vaults contract on mainnet. The protocol's official addresses page lists the mainnet protocol manager and admin account as `GC7JVOXZJSHY3GHKWUJWKIUYWEJ4RZABSRQZQ5JBZEC5QUTYBUHVNIKV` and the mainnet Vaults contract as `CCUN4RXU5VNDHSF4S4RKV4ZJYMX2YWKOH6L4AKEKVNVDQ7HY5QIAO4UB`. Both identifiers resolve through Stellar Expert API, which shows the account is active and the Vaults contract was created by that admin account.

FxDAO's pricing model is intentionally simple. Its borrowing cost is a flat 0.25% fee on collateral deposited rather than a floating utilization curve. The main risk controls are overcollateralization, redemptions, and open liquidations. If a vault falls below the minimum collateral ratio, any qualifying liquidator can repay the stablecoin debt and seize the collateral minus the protocol's 0.5% share rate. FxDAO also allows stablecoin holders to redeem against the riskiest vaults, which pressures unhealthy positions before they drift too far from solvency. As of 2026-03-27, DefiLlama reported roughly $977,183 in TVL for FxDAO on Stellar. That TVL is public and DefiLlama states its methodology is the value of XLM locked in the Vaults contract.

## Sources

- FxDAO basics: https://fxdao.io/docs/the-basics
- FxDAO borrowing: https://fxdao.io/docs/borrowing
- FxDAO liquidations: https://fxdao.io/docs/liquidations
- FxDAO official addresses: https://fxdao.io/docs/addresses
- Stellar Expert account API: https://api.stellar.expert/explorer/public/account/GC7JVOXZJSHY3GHKWUJWKIUYWEJ4RZABSRQZQ5JBZEC5QUTYBUHVNIKV
- Stellar Expert contract API: https://api.stellar.expert/explorer/public/contract/CCUN4RXU5VNDHSF4S4RKV4ZJYMX2YWKOH6L4AKEKVNVDQ7HY5QIAO4UB
- DefiLlama protocol API: https://api.llama.fi/protocol/fxdao
35 changes: 35 additions & 0 deletions web/research/slender.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Slender

- Category: defi
- Tags: defi, lending, borrowing, soroban, flash-loans, overcollateralized, stellar-wave
- Website: https://slender.fi/
- GitHub: https://github.com/eq-lab/slender
- Stellar account ID: `GCVWQNFTPVJISL3NM7UWJIESDVL6B73RS6NYV4P3J4WSC7WSTFECEO2J`
- Stellar contract ID: `CCL2KTHYOVMNNOFDT7PEAHACUBYVFLRH2LYWVQB6IPMHHAVUBC7ZUUC2`

Slender is a Stellar Community Fund backed lending and borrowing protocol built on Soroban. The Stellar Community Fund project page classifies Slender as a lending and borrowing project, and the protocol's public app, codebase, and Certora audit all support that classification. Slender uses a pooled money market design rather than isolated vaults. Users deposit supported assets into a reserve, receive yield-bearing sTokens, and can borrow other assets against posted collateral. The debt is overcollateralized and interest-bearing, while reserve liquidity is shared across all suppliers and borrowers. Slender also supports flash loans, which makes it broader than a basic collateralized lending desk.

The currently published mainnet deployment supports three live markets: XLM, XRP, and USDC. Slender's mainnet deployment config shows that all three reserves have borrowing enabled and each reserve has a 90% utilization cap, which is meant to keep part of the pool liquid for withdrawals. The same config publishes reserve-specific collateral discounts and liquidity caps. XLM and XRP are both configured with an 80% discount, while USDC is configured with a 95% discount, making USDC the strongest posted collateral of the three on the live configuration. Penalty order is also set per reserve, so the protocol can liquidate collateral in a deterministic sequence.

Interest rates are utilization driven instead of fixed. Slender's SCF submission says rates move with supply and demand, and the published mainnet config exposes the current parameters directly: `IR_INITIAL_RATE_BPS=200`, `IR_MAX_RATE_BPS=50000`, `IR_ALPHA=143`, and `IR_SCALING_COEFF_BPS=9000`. That means borrowing starts at a relatively low base rate but ramps up sharply as utilization approaches the cap. Mainnet also sets a `FLASH_LOAN_FEE_BPS=9`, so flash borrowers pay 0.09% when the loan is repaid in the same transaction flow. The pool config also includes `USER_ASSET_LIMIT=3`, a one day grace period, and minimum collateral and debt thresholds in the base asset.

Slender's risk model is visible both in configuration and in contract code. Reserve-level controls include `discount`, `liquidity_cap`, `util_cap`, and borrowing enablement. Pool-level controls include `initial_health`, `grace_period`, `timestamp_window`, `flash_loan_fee`, and `liquidation_protocol_fee`. The liquidation function checks whether an account is still a good position; if not, a liquidator can repay debt and seize collateral, with a protocol fee carved out of the liquidated amount. Slender also depends on a SEP-40 oracle feed and uses TWAP-related parameters, timestamp checks, and sanity bounds to reduce manipulation or stale-price risk. I verified the mainnet pool contract, oracle contract, and XLM token contract through the Stellar Expert contract API. The pool contract API also exposes the mainnet creator account above, which I am using as the clearest public on-chain account identifier for this submission. I did not find a separate, protocol-specific public TVL source in the official Slender docs, app, or DefiLlama API on March 27, 2026, so TVL should be treated as not publicly published from the sources below.

## Sources

- Stellar Community Fund project page: https://communityfund.stellar.org/dashboard/submissions/recPZV9KigYc4et2I
- Slender website: https://slender.fi/
- Slender app: https://app.slender.fi/
- Slender GitHub: https://github.com/eq-lab/slender
- Slender README: https://raw.githubusercontent.com/eq-lab/slender/master/README.md
- Mainnet deployment artifacts: https://raw.githubusercontent.com/eq-lab/slender/master/deploy/artifacts/mainnet/.contracts
- Mainnet deployment config: https://raw.githubusercontent.com/eq-lab/slender/master/deploy/scripts/.mainnet.env
- Deployment script: https://raw.githubusercontent.com/eq-lab/slender/master/deploy/scripts/deploy.sh
- Reserve configuration type: https://raw.githubusercontent.com/eq-lab/slender/master/interfaces/pool-interface/src/types/reserve_configuration.rs
- Pool configuration type: https://raw.githubusercontent.com/eq-lab/slender/master/interfaces/pool-interface/src/types/pool_config.rs
- Liquidation implementation: https://raw.githubusercontent.com/eq-lab/slender/master/contracts/pool/src/methods/liquidate.rs
- Certora audit summary: https://www.certora.com/reports/slender
- Stellar Expert pool contract API: https://api.stellar.expert/explorer/public/contract/CCL2KTHYOVMNNOFDT7PEAHACUBYVFLRH2LYWVQB6IPMHHAVUBC7ZUUC2
- Stellar Expert oracle contract API: https://api.stellar.expert/explorer/public/contract/CALI2BYU2JE6WVRUFYTS6MSBNEHGJ35P4AVCZYF3B6QOE3QKOB2PLE6M
- Stellar Expert XLM token contract API: https://api.stellar.expert/explorer/public/contract/CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA
- Stellar Expert account API: https://api.stellar.expert/explorer/public/account/GCVWQNFTPVJISL3NM7UWJIESDVL6B73RS6NYV4P3J4WSC7WSTFECEO2J