diff --git a/.github/workflows/check-contributor-format.yml b/.github/workflows/check-contributor-format.yml
index d6951ac..1049e5a 100644
--- a/.github/workflows/check-contributor-format.yml
+++ b/.github/workflows/check-contributor-format.yml
@@ -1,4 +1,4 @@
-name: Check Contributor Format
+name: Check Contributor Format
on:
pull_request:
@@ -27,14 +27,14 @@ 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' });
@@ -42,12 +42,17 @@ jobs:
`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(
@@ -56,7 +61,7 @@ jobs:
);
}
- // ── 2. CONTRIBUTORS-START / END markers present ────────────────
+ // 2. CONTRIBUTORS-START / END markers present
const sectionMatch = content.match(
/([\s\S]*?)/
);
@@ -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` ' +
@@ -78,7 +83,7 @@ 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<')) {
@@ -86,14 +91,14 @@ jobs:
'**Placeholder not replaced**: Replace `Your Name` with your actual name.'
);
}
- if (/Role — Project 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(
/
]*style="display:inline-block[^"]*"[\s\S]*?<\/div>/g
) || [];
@@ -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(
@@ -130,33 +135,33 @@ jobs:
'**Missing GitHub badge**: Include the GitHub badge from the template.'
);
}
- if (!/
[A-Za-z][\w\s]* — .+<\/sub>/.test(checkIn)) {
+ if (!/[A-Za-z][\w\s]*\s[-—]\s.+<\/sub>/.test(checkIn)) {
errors.push(
'**Missing role/projects line**: Include a role line such as ' +
- '`Researcher — StellarPay, LumenSwap`.'
+ '`Researcher - StellarPay, LumenSwap`.'
);
}
}
- // ── Post or update PR comment ──────────────────────────────────
+ // Post or update PR comment
const MARKER = '';
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',
@@ -175,39 +180,37 @@ jobs:
'
',
'
',
'
',
- ' Role — Project 1, Project 2',
+ ' Role - Project 1, Project 2',
' ',
'```',
'',
- '> 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!');
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 6473c27..1c31c99 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -259,6 +259,52 @@ Replace `YOUR_GITHUB_USERNAME`, `Your Name`, `YOUR_X_HANDLE`, and the role/proje
Researcher — KindFi
+
+
+
+
diff --git a/web/research/fxdao.md b/web/research/fxdao.md
new file mode 100644
index 0000000..89ec1a8
--- /dev/null
+++ b/web/research/fxdao.md
@@ -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
diff --git a/web/research/slender.md b/web/research/slender.md
new file mode 100644
index 0000000..3815fd3
--- /dev/null
+++ b/web/research/slender.md
@@ -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