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
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:
@@ -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);
|
||||
});
|
||||
});
|
||||
119
services/token-aggregation/src/api/utils/uniswap-token-list.ts
Normal file
119
services/token-aggregation/src/api/utils/uniswap-token-list.ts
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user