fix(token-aggregation): emit Uniswap-schema token lists for mainnet cWUSDC
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m35s
CI/CD Pipeline / Security Scanning (push) Successful in 2m58s
CI/CD Pipeline / Lint and Format (push) Failing after 40s
CI/CD Pipeline / Terraform Validation (push) Failing after 24s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 25s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 56s
Validation / validate-genesis (push) Successful in 32s
Validation / validate-terraform (push) Failing after 30s
Validation / validate-kubernetes (push) Failing after 10s
Validation / validate-smart-contracts (push) Failing after 10s
Validation / validate-security (push) Failing after 1m23s
Validation / validate-documentation (push) Failing after 17s
Verify Deployment / Verify Deployment (push) Failing after 51s

Normalize /report/token-list to checksummed addresses, version objects,
and extensions bag; add pinned static ethereum-mainnet list endpoint.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-06-06 10:54:12 -07:00
parent 0d08d4e11f
commit b6ddd236e2
6 changed files with 486 additions and 69 deletions

View File

@@ -0,0 +1,76 @@
import {
buildUniswapTokenList,
checksumTokenAddress,
normalizeTokenListVersion,
toUniswapTokenListToken,
} from './uniswap-token-list';
describe('uniswap-token-list utils', () => {
it('normalizes string versions into major/minor/patch objects', () => {
expect(normalizeTokenListVersion('1.2.3')).toEqual({ major: 1, minor: 2, patch: 3 });
expect(normalizeTokenListVersion({ major: 4, minor: 5, patch: 6 })).toEqual({
major: 4,
minor: 5,
patch: 6,
});
});
it('moves non-schema token fields into extensions and checksums addresses', () => {
const token = toUniswapTokenListToken(
{
chainId: 1,
address: '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a',
symbol: 'cWUSDC',
name: 'USD Coin (Compliant Wrapped ISO-4217 M1)',
decimals: 6,
logoURI: 'https://d-bis.org/tokens/cwusdc.svg',
type: 'w',
registryFamily: 'iso4217',
originalLogoURI: 'https://example.com/logo.svg',
},
{ displayNames: { cWUSDC: 'Wrapped cUSDC' } }
);
expect(token).toMatchObject({
chainId: 1,
address: '0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a',
symbol: 'cWUSDC',
name: 'Wrapped cUSDC',
decimals: 6,
logoURI: 'https://d-bis.org/tokens/cwusdc.svg',
extensions: {
type: 'w',
registryFamily: 'iso4217',
originalLogoURI: 'https://example.com/logo.svg',
},
});
expect(token).not.toHaveProperty('type');
expect(token).not.toHaveProperty('registryFamily');
expect(checksumTokenAddress('0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a')).toBe(
'0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a'
);
});
it('builds a schema-compliant list envelope', () => {
const list = buildUniswapTokenList({
name: 'DBIS Ethereum Mainnet GRU',
version: '1.1.0',
timestamp: '2026-06-06T00:00:00.000Z',
logoURI: 'https://d-bis.org/tokens/cwusdc.svg',
tokens: [
{
chainId: 1,
address: '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a',
symbol: 'cWUSDC',
name: 'Wrapped cUSDC',
decimals: 6,
logoURI: 'https://d-bis.org/tokens/cwusdc.svg',
},
],
});
expect(list.version).toEqual({ major: 1, minor: 1, patch: 0 });
expect(Array.isArray(list.tokens)).toBe(true);
expect((list.tokens as unknown[]).length).toBe(1);
});
});

View File

@@ -0,0 +1,119 @@
import { getAddress } from 'ethers';
export type UniswapTokenListVersion = {
major: number;
minor: number;
patch: number;
};
const TOKEN_EXTENSION_TOP_LEVEL_KEYS = [
'type',
'originalLogoURI',
'registryFamily',
'familySymbol',
'deploymentVersion',
'deploymentStatus',
'preferredForX402',
] as const;
/** Wallet/registry-facing names for mainnet transport tokens. */
export const MAINNET_PUBLIC_DISPLAY_NAMES: Record<string, string> = {
cWUSDC: 'Wrapped cUSDC',
cWUSDT: 'Wrapped cUSDT',
};
export function normalizeTokenListVersion(version: unknown): UniswapTokenListVersion {
if (version && typeof version === 'object' && 'major' in version) {
const record = version as { major?: unknown; minor?: unknown; patch?: unknown };
return {
major: Number(record.major ?? 1),
minor: Number(record.minor ?? 0),
patch: Number(record.patch ?? 0),
};
}
if (typeof version === 'string' && version.trim() !== '') {
const [major = '1', minor = '0', patch = '0'] = version.trim().split('.');
return {
major: Number.parseInt(major, 10) || 1,
minor: Number.parseInt(minor, 10) || 0,
patch: Number.parseInt(patch, 10) || 0,
};
}
return { major: 1, minor: 0, patch: 0 };
}
export function checksumTokenAddress(address: string): string {
try {
return getAddress(address);
} catch {
return address;
}
}
export function toUniswapTokenListToken(
token: Record<string, unknown>,
options?: { displayNames?: Record<string, string> }
): Record<string, unknown> {
const extensions: Record<string, unknown> = {};
for (const key of TOKEN_EXTENSION_TOP_LEVEL_KEYS) {
if (token[key] !== undefined) {
extensions[key] = token[key];
}
}
if (token.extensions && typeof token.extensions === 'object' && !Array.isArray(token.extensions)) {
Object.assign(extensions, token.extensions as Record<string, unknown>);
}
const symbol = String(token.symbol ?? '');
const displayName = options?.displayNames?.[symbol];
const out: Record<string, unknown> = {
chainId: token.chainId,
address: checksumTokenAddress(String(token.address ?? '')),
name: displayName ?? token.name,
symbol,
decimals: token.decimals,
};
if (token.logoURI) out.logoURI = token.logoURI;
if (Array.isArray(token.tags) && token.tags.length > 0) out.tags = token.tags;
if (Object.keys(extensions).length > 0) out.extensions = extensions;
return out;
}
export function buildUniswapTokenList(params: {
name: string;
version: unknown;
timestamp: string;
logoURI?: string;
tokens: Array<Record<string, unknown>>;
keywords?: string[];
tags?: Record<string, unknown>;
displayNames?: Record<string, string>;
}): Record<string, unknown> {
const body: Record<string, unknown> = {
name: params.name,
timestamp: params.timestamp,
version: normalizeTokenListVersion(params.version),
tokens: params.tokens.map((token) =>
toUniswapTokenListToken(token, { displayNames: params.displayNames })
),
};
if (params.logoURI) body.logoURI = params.logoURI;
if (params.keywords?.length) body.keywords = params.keywords;
if (params.tags && Object.keys(params.tags).length > 0) body.tags = params.tags;
return body;
}
export function resolveTokenListName(chainIds: number[], defaultName: string): string {
if (chainIds.length === 1 && chainIds[0] === 1) {
return 'DBIS Ethereum Mainnet GRU';
}
return defaultName;
}