Skip to content
Merged
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
79 changes: 79 additions & 0 deletions auto-qa/tests/flm-config.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const here = dirname(fileURLToPath(import.meta.url));
const root = resolve(here, '../..');
const flmConfig = JSON.parse(await readFile(resolve(root, 'src/config/flm.json'), 'utf8'));

const ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
const optionalAddress = (value) => value === '' || ADDRESS_RE.test(value);
const bySlug = Object.fromEntries(flmConfig.map((config) => [config.slug, config]));

test('FLM config pins Kleros and Gnosis organization routes', () => {
assert.deepEqual(Object.keys(bySlug).sort(), ['gnosis', 'kleros']);

assert.equal(bySlug.kleros.path, '/flm/kleros');
assert.equal(bySlug.kleros.organizationAddress, '0xaab097ead5c2db1ca7b1e5034224a2118edabe36');
assert.equal(bySlug.kleros.companyId, 10);

assert.equal(bySlug.gnosis.path, '/flm/gnosis');
assert.equal(bySlug.gnosis.organizationAddress, '0x3fd2e8e71f75eed4b5c507706c413e33e0661bbf');
assert.equal(bySlug.gnosis.companyId, 9);
});

test('FLM proposal metadata keeps the current official markets discoverable', () => {
assert.equal(bySlug.kleros.activeProposal.label, 'KIP-90');
assert.equal(bySlug.kleros.activeProposal.marketAddress, '0x84412Fe9D088C1D8Dd676a7be9a3d5d0291Ab1Cf');
assert.equal(
bySlug.kleros.activeProposal.snapshotId,
'0xba2749a4f1283da9d1ca925d9f17bf712fa06a23e6a07d759c54340277820932'
);
assert.equal(bySlug.kleros.activeProposal.marketUrl, '/markets/0x84412Fe9D088C1D8Dd676a7be9a3d5d0291Ab1Cf');

assert.equal(bySlug.gnosis.activeProposal.label, 'GIP-151');
assert.equal(bySlug.gnosis.activeProposal.marketAddress, '0xeCe80208CB8376Be311cE0f5Ea4eF73850a0dcF0');
assert.equal(
bySlug.gnosis.activeProposal.snapshotId,
'0x657fbf8892200d24e887c68245cee73b59c466394192be1c10673b39814c74c4'
);
assert.equal(bySlug.gnosis.activeProposal.marketUrl, '/markets/0xeCe80208CB8376Be311cE0f5Ea4eF73850a0dcF0');
});

test('FLM token and contract fields use valid address shapes', () => {
for (const config of flmConfig) {
assert.equal(config.chainId, 100);
assert.match(config.token.address, ADDRESS_RE);
assert.match(config.collateral.address, ADDRESS_RE);
assert.ok(optionalAddress(config.managerAddress), `${config.slug} manager address shape`);
assert.ok(optionalAddress(config.proposalSourceAddress), `${config.slug} proposal source shape`);
assert.ok(optionalAddress(config.activeProposal.proposalMetadataAddress), `${config.slug} metadata shape`);
}
});

test('FLM helpers pin Swapr adapter calldata and manager overloads', async () => {
const utilsSource = await readFile(resolve(root, 'src/utils/flm.js'), 'utf8');
const pageSource = await readFile(resolve(root, 'src/pages/flm/[org].jsx'), 'utf8');

assert.match(
utilsSource,
/tuple\(int24 tickLower,int24 tickUpper,uint256 amount0Min,uint256 amount1Min,uint256 deadline,uint160 sqrtPriceX96\)/
);
assert.match(
utilsSource,
/tuple\(uint256 amount0Min,uint256 amount1Min,uint256 deadline\)/
);
assert.match(pageSource, /depositToSpot\(uint256,uint256,bytes\)/);
assert.match(pageSource, /encodeDualExitParams\(yesExitData, noExitData\)/);
});

test('companies table links configured organizations to their FLM page', async () => {
const hookSource = await readFile(resolve(root, 'src/hooks/useAggregatorCompanies.js'), 'utf8');
const rowSource = await readFile(resolve(root, 'src/components/futarchyFi/companyList/table/OrgRow.jsx'), 'utf8');

assert.match(hookSource, /flmPath: getFlmPathForOrg\(org\.id\)/);
assert.match(rowSource, /href=\{flmPath\}/);
assert.match(rowSource, /event\.stopPropagation\(\)/);
});
21 changes: 20 additions & 1 deletion src/components/futarchyFi/companyList/table/OrgRow.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';

import ChainBadge from '../components/ChainBadge';

/**
Expand All @@ -13,6 +16,7 @@ const OrgRow = ({
activeProposals = 0,
proposalsCount = 0,
chainId = 100, // Default to Gnosis
flmPath = null,
hasActiveMarket = false,
isOwner = false,
onClick,
Expand Down Expand Up @@ -71,9 +75,24 @@ const OrgRow = ({
<td className="py-4 px-4">
<ChainBadge chainId={chainId} size="sm" />
</td>

{/* FLM */}
<td className="py-4 px-4">
{flmPath ? (
<Link
href={flmPath}
onClick={(event) => event.stopPropagation()}
className="inline-flex h-9 items-center gap-1 rounded-lg border border-futarchyGray5 bg-white px-3 text-sm font-semibold text-futarchyGray12 hover:bg-futarchyGray3"
>
<span>FLM</span>
<ArrowTopRightOnSquareIcon className="h-4 w-4" />
</Link>
) : (
<span className="text-sm text-futarchyGray11">-</span>
)}
</td>
</tr>
);
};

export default OrgRow;

Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ const OrganizationsTable = ({
<th className="py-3 px-4 text-xs font-medium text-futarchyGray11 dark:text-futarchyGray112 uppercase tracking-wider">
Chain
</th>
<th className="py-3 px-4 text-xs font-medium text-futarchyGray11 dark:text-futarchyGray112 uppercase tracking-wider">
FLM
</th>
</tr>
</thead>
<tbody className="bg-white dark:bg-transparent divide-y divide-futarchyGray4 dark:divide-futarchyGray112/10">
Expand All @@ -154,6 +157,7 @@ const OrganizationsTable = ({
activeProposals={org.activeProposals || 0}
proposalsCount={org.proposals || org.proposalsCount || 0}
chainId={org.chainId || 100}
flmPath={org.flmPath}
hasActiveMarket={(org.activeProposals || 0) > 0}
isOwner={connectedWallet && org.owner?.toLowerCase() === connectedWallet.toLowerCase()}
onClick={() => onOrgClick?.(org)}
Expand Down
78 changes: 78 additions & 0 deletions src/config/flm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
[
{
"slug": "kleros",
"path": "/flm/kleros",
"organizationName": "Kleros",
"organizationAddress": "0xaab097ead5c2db1ca7b1e5034224a2118edabe36",
"companyId": 10,
"chainId": 100,
"pair": "PNK/sDAI",
"status": "pending-deployment",
"managerAddress": "",
"proposalSourceAddress": "",
"token": {
"symbol": "PNK",
"address": "0x37b60f4E9A31A64cCc0024dce7D0fD07eAA0F7B3",
"decimals": 18
},
"collateral": {
"symbol": "sDAI",
"address": "0xaf204776c7245bF4147c2612BF6e5972Ee483701",
"decimals": 18
},
"activeProposal": {
"label": "KIP-90",
"marketAddress": "0x84412Fe9D088C1D8Dd676a7be9a3d5d0291Ab1Cf",
"questionId": "0xe1ef37c96013f3a1e4a9cb00bdb1bf158ecd91be210077316933bb9d5ba5738d",
"title": "What will the impact on PNK price be if KIP-90 is passed?",
"snapshotId": "0xba2749a4f1283da9d1ca925d9f17bf712fa06a23e6a07d759c54340277820932",
"snapshotUrl": "https://snapshot.org/#/s:kleros.eth/proposal/0xba2749a4f1283da9d1ca925d9f17bf712fa06a23e6a07d759c54340277820932",
"marketUrl": "/markets/0x84412Fe9D088C1D8Dd676a7be9a3d5d0291Ab1Cf",
"closeTimestamp": 1783086060,
"metadataAddress": "",
"proposalMetadataAddress": ""
},
"deployment": {
"repo": "futarchy-fi/futarchy-liquidity-manager",
"config": "config/kleros.kip90.json"
}
},
{
"slug": "gnosis",
"path": "/flm/gnosis",
"organizationName": "Gnosis",
"organizationAddress": "0x3fd2e8e71f75eed4b5c507706c413e33e0661bbf",
"companyId": 9,
"chainId": 100,
"pair": "GNO/sDAI",
"status": "pending-deployment",
"managerAddress": "",
"proposalSourceAddress": "",
"token": {
"symbol": "GNO",
"address": "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb",
"decimals": 18
},
"collateral": {
"symbol": "sDAI",
"address": "0xaf204776c7245bF4147c2612BF6e5972Ee483701",
"decimals": 18
},
"activeProposal": {
"label": "GIP-151",
"marketAddress": "0xeCe80208CB8376Be311cE0f5Ea4eF73850a0dcF0",
"questionId": "",
"title": "What will the impact on GNO price be if GIP-151 is passed?",
"snapshotId": "0x657fbf8892200d24e887c68245cee73b59c466394192be1c10673b39814c74c4",
"snapshotUrl": "https://snapshot.box/#/s:gnosis.eth/proposal/0x657fbf8892200d24e887c68245cee73b59c466394192be1c10673b39814c74c4",
"marketUrl": "/markets/0xeCe80208CB8376Be311cE0f5Ea4eF73850a0dcF0",
"closeTimestamp": 1782486884,
"metadataAddress": "0x590D470C33beC64c16AAD52cC64C232240547702",
"proposalMetadataAddress": "0x590D470C33beC64c16AAD52cC64C232240547702"
},
"deployment": {
"repo": "futarchy-fi/futarchy-liquidity-manager",
"config": "config/gnosis.production.json"
}
}
]
2 changes: 2 additions & 0 deletions src/hooks/useAggregatorCompanies.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { useState, useEffect } from 'react';

import { AGGREGATOR_SUBGRAPH_URL as SUBGRAPH_URL } from '../config/subgraphEndpoints';
import { getFlmPathForOrg } from '../utils/flm';
import { isProposalActive, isProposalArchived } from '../utils/proposalLifecycle';

// Three flat queries — Checkpoint has no auto-generated reverse fields.
Expand Down Expand Up @@ -101,6 +102,7 @@ function transformOrgToCard(org, proposalsForOrg) {
website: meta.website,
twitter: meta.twitter,
metadataURI: org.metadataURI,
flmPath: getFlmPathForOrg(org.id),
// Surface the parsed org metadata so downstream filters can
// check archived/visibility without re-parsing.
_orgMetadata: meta,
Expand Down
Loading
Loading