- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
254 lines
9.8 KiB
TypeScript
254 lines
9.8 KiB
TypeScript
/**
|
|
* Token mapping API: exposes config/token-mapping-loader (multichain) when run from monorepo.
|
|
* GET /api/v1/token-mapping?fromChain=138&toChain=651940
|
|
* GET /api/v1/token-mapping/pairs
|
|
*/
|
|
|
|
import { Router, Request, Response } from 'express';
|
|
import { cacheMiddleware } from '../middleware/cache';
|
|
import { loadTokenMappingLoader } from '../../config/repo-config-loader';
|
|
|
|
const router: Router = Router();
|
|
|
|
function loadMultichainLoader(): {
|
|
getTokenMappingForPair: (from: number, to: number) => { tokens: unknown[]; addressMapFromTo: Record<string, string>; addressMapToFrom: Record<string, string> } | null;
|
|
getAllMultichainPairs: () => Array<{ fromChainId: number; toChainId: number; notes?: string }>;
|
|
getMappedAddress: (from: number, to: number, addr: string) => string | undefined;
|
|
getGruTransportMetadata?: () => {
|
|
system: Record<string, unknown> | null;
|
|
terminology: Record<string, string>;
|
|
enabledCanonicalTokens: Array<Record<string, unknown>>;
|
|
enabledDestinationChains: Array<Record<string, unknown>>;
|
|
counts: Record<string, number>;
|
|
} | null;
|
|
getActiveTransportPairs?: () => Array<Record<string, unknown>>;
|
|
getActiveTransportPair?: (from: number, to: number, criteria?: Record<string, unknown>) => Record<string, unknown> | null;
|
|
getActivePublicPools?: () => Array<Record<string, unknown>>;
|
|
getEnabledCanonicalToken?: (identifier: string) => Record<string, unknown> | null;
|
|
isGasRedemptionPathAllowed?: (from: number, to: number, identifier: string) => boolean;
|
|
} | null {
|
|
const loader = loadTokenMappingLoader<{
|
|
getTokenMappingForPair: (from: number, to: number) => { tokens: unknown[]; addressMapFromTo: Record<string, string>; addressMapToFrom: Record<string, string> } | null;
|
|
getAllMultichainPairs: () => Array<{ fromChainId: number; toChainId: number; notes?: string }>;
|
|
getMappedAddress: (from: number, to: number, addr: string) => string | undefined;
|
|
getGruTransportMetadata?: () => {
|
|
system: Record<string, unknown> | null;
|
|
terminology: Record<string, string>;
|
|
enabledCanonicalTokens: Array<Record<string, unknown>>;
|
|
enabledDestinationChains: Array<Record<string, unknown>>;
|
|
counts: Record<string, number>;
|
|
} | null;
|
|
getActiveTransportPairs?: () => Array<Record<string, unknown>>;
|
|
getActiveTransportPair?: (from: number, to: number, criteria?: Record<string, unknown>) => Record<string, unknown> | null;
|
|
getActivePublicPools?: () => Array<Record<string, unknown>>;
|
|
getEnabledCanonicalToken?: (identifier: string) => Record<string, unknown> | null;
|
|
isGasRedemptionPathAllowed?: (from: number, to: number, identifier: string) => boolean;
|
|
}>();
|
|
if (loader) {
|
|
return loader;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/token-mapping?fromChain=138&toChain=651940
|
|
* Returns token mapping for a chain pair (from config/token-mapping-multichain.json).
|
|
*/
|
|
router.get(
|
|
'/',
|
|
cacheMiddleware(5 * 60 * 1000),
|
|
(req: Request, res: Response) => {
|
|
const fromChain = parseInt(String(req.query.fromChain ?? req.query.from), 10);
|
|
const toChain = parseInt(String(req.query.toChain ?? req.query.to), 10);
|
|
if (!Number.isFinite(fromChain) || !Number.isFinite(toChain)) {
|
|
return res.status(400).json({
|
|
error: 'Missing or invalid fromChain and toChain query params',
|
|
example: '/api/v1/token-mapping?fromChain=138&toChain=651940',
|
|
});
|
|
}
|
|
|
|
const loader = loadMultichainLoader();
|
|
if (!loader) {
|
|
return res.status(503).json({
|
|
error: 'Token mapping config not available (run from monorepo with config/token-mapping-multichain.json)',
|
|
});
|
|
}
|
|
|
|
const result = loader.getTokenMappingForPair(fromChain, toChain);
|
|
if (!result) {
|
|
return res.status(404).json({
|
|
error: 'No token mapping for this chain pair',
|
|
fromChain,
|
|
toChain,
|
|
});
|
|
}
|
|
|
|
const activePairs = loader.getActiveTransportPairs
|
|
? loader
|
|
.getActiveTransportPairs()
|
|
.filter((pair) => {
|
|
const canonicalChainId = Number(pair.canonicalChainId);
|
|
const destinationChainId = Number(pair.destinationChainId);
|
|
return (
|
|
(canonicalChainId === fromChain && destinationChainId === toChain) ||
|
|
(canonicalChainId === toChain && destinationChainId === fromChain)
|
|
);
|
|
})
|
|
: [];
|
|
|
|
return res.json({
|
|
fromChainId: fromChain,
|
|
toChainId: toChain,
|
|
tokens: result.tokens,
|
|
addressMapFromTo: result.addressMapFromTo,
|
|
addressMapToFrom: result.addressMapToFrom,
|
|
gruTransport: loader.getGruTransportMetadata
|
|
? {
|
|
system: loader.getGruTransportMetadata()?.system ?? null,
|
|
activePairs,
|
|
}
|
|
: undefined,
|
|
});
|
|
}
|
|
);
|
|
|
|
/**
|
|
* GET /api/v1/token-mapping/pairs
|
|
* Returns all defined chain pairs.
|
|
*/
|
|
router.get(
|
|
'/pairs',
|
|
cacheMiddleware(5 * 60 * 1000),
|
|
(req: Request, res: Response) => {
|
|
const loader = loadMultichainLoader();
|
|
if (!loader) {
|
|
return res.status(503).json({
|
|
error: 'Token mapping config not available (run from monorepo)',
|
|
});
|
|
}
|
|
const pairs = loader.getAllMultichainPairs();
|
|
return res.json({
|
|
pairs,
|
|
gruTransport: loader.getGruTransportMetadata
|
|
? {
|
|
system: loader.getGruTransportMetadata()?.system ?? null,
|
|
activePairs: loader.getActiveTransportPairs ? loader.getActiveTransportPairs() : [],
|
|
}
|
|
: undefined,
|
|
});
|
|
}
|
|
);
|
|
|
|
/**
|
|
* GET /api/v1/token-mapping/transport/active
|
|
* Returns the GRU Monetary Transport Layer overlay as seen by token-aggregation.
|
|
*/
|
|
router.get(
|
|
'/transport/active',
|
|
cacheMiddleware(5 * 60 * 1000),
|
|
(_req: Request, res: Response) => {
|
|
const loader = loadMultichainLoader();
|
|
if (!loader || !loader.getGruTransportMetadata || !loader.getActiveTransportPairs) {
|
|
return res.status(503).json({
|
|
error: 'GRU transport config not available (run from monorepo with config/gru-transport-active.json)',
|
|
});
|
|
}
|
|
|
|
return res.json({
|
|
...loader.getGruTransportMetadata(),
|
|
transportPairs: loader.getActiveTransportPairs(),
|
|
publicPools: loader.getActivePublicPools ? loader.getActivePublicPools() : [],
|
|
});
|
|
}
|
|
);
|
|
|
|
/**
|
|
* GET /api/v1/token-mapping/resolve?fromChain=138&toChain=56&address=0x...
|
|
* Returns mapped token address on target chain.
|
|
*/
|
|
router.get(
|
|
'/resolve',
|
|
cacheMiddleware(5 * 60 * 1000),
|
|
(req: Request, res: Response) => {
|
|
const fromChain = parseInt(String(req.query.fromChain ?? req.query.from), 10);
|
|
const toChain = parseInt(String(req.query.toChain ?? req.query.to), 10);
|
|
const address = String(req.query.address ?? req.query.token ?? '').trim();
|
|
if (!Number.isFinite(fromChain) || !Number.isFinite(toChain) || !address) {
|
|
return res.status(400).json({
|
|
error: 'Missing or invalid fromChain, toChain, or address',
|
|
example: '/api/v1/token-mapping/resolve?fromChain=138&toChain=56&address=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
|
});
|
|
}
|
|
|
|
const loader = loadMultichainLoader();
|
|
if (!loader) {
|
|
return res.status(503).json({
|
|
error: 'Token mapping config not available (run from monorepo)',
|
|
});
|
|
}
|
|
|
|
const mapped = loader.getMappedAddress(fromChain, toChain, address);
|
|
const activeTransportPair = loader.getActiveTransportPair
|
|
? loader.getActiveTransportPair(fromChain, toChain, {
|
|
address,
|
|
targetTokenAddress: mapped ?? null,
|
|
})
|
|
: null;
|
|
const canonicalTokenIdentifier =
|
|
(activeTransportPair && typeof activeTransportPair.canonicalSymbol === 'string'
|
|
? activeTransportPair.canonicalSymbol
|
|
: null) ??
|
|
address;
|
|
const canonicalToken = loader.getEnabledCanonicalToken
|
|
? loader.getEnabledCanonicalToken(canonicalTokenIdentifier)
|
|
: null;
|
|
return res.json({
|
|
fromChainId: fromChain,
|
|
toChainId: toChain,
|
|
addressOnSource: address,
|
|
addressOnTarget: mapped ?? null,
|
|
activeTransportEligible: !!activeTransportPair && activeTransportPair.eligible === true,
|
|
gruTransportRuntimeReady: !!activeTransportPair && activeTransportPair.runtimeReady === true,
|
|
gruTransportPairKey:
|
|
activeTransportPair && typeof activeTransportPair.key === 'string' ? activeTransportPair.key : null,
|
|
gruTransportAssetClass:
|
|
activeTransportPair && typeof activeTransportPair.assetClass === 'string'
|
|
? activeTransportPair.assetClass
|
|
: null,
|
|
gruTransportFamilyKey:
|
|
activeTransportPair && typeof activeTransportPair.familyKey === 'string'
|
|
? activeTransportPair.familyKey
|
|
: null,
|
|
gruTransportBackingMode:
|
|
activeTransportPair && typeof activeTransportPair.backingMode === 'string'
|
|
? activeTransportPair.backingMode
|
|
: null,
|
|
gruTransportRedeemPolicy:
|
|
activeTransportPair && typeof activeTransportPair.redeemPolicy === 'string'
|
|
? activeTransportPair.redeemPolicy
|
|
: null,
|
|
gruTransportWrappedNativeQuoteSymbol:
|
|
activeTransportPair && typeof activeTransportPair.wrappedNativeQuoteSymbol === 'string'
|
|
? activeTransportPair.wrappedNativeQuoteSymbol
|
|
: null,
|
|
gruTransportStableQuoteSymbol:
|
|
activeTransportPair && typeof activeTransportPair.stableQuoteSymbol === 'string'
|
|
? activeTransportPair.stableQuoteSymbol
|
|
: null,
|
|
gruTransportReferenceVenue:
|
|
activeTransportPair && typeof activeTransportPair.referenceVenue === 'string'
|
|
? activeTransportPair.referenceVenue
|
|
: null,
|
|
gruGasRedemptionPathAllowed:
|
|
activeTransportPair &&
|
|
typeof activeTransportPair.familyKey === 'string' &&
|
|
loader.isGasRedemptionPathAllowed
|
|
? loader.isGasRedemptionPathAllowed(fromChain, toChain, activeTransportPair.familyKey)
|
|
: null,
|
|
gruTransportCanonicalToken: canonicalToken,
|
|
});
|
|
}
|
|
);
|
|
|
|
export default router;
|