Skip to content

Commit f31598a

Browse files
committed
PLT-676: replace fuzzy target resolution with curated-fungible-id + address
Symbol-based searchFungibles matching surfaced two failure modes during the polygon dry-run: case-sensitivity smell on `USDC.E` vs `USDC.e`, and broad ambiguity from symbol collisions. The new resolver in cli/utils/trading/consolidate-targets.js accepts only: 1. a curated symbol — looked up in a flat SYMBOL→fungibleId map, with the per-chain impl address fetched live from getFungible. Whatever Zerion lists as USDC's impl on a given chain (bridged USDC.e on some, Circle-native on others) is what the address-based target exclusion catches. 2. a raw contract address — for any token outside the curated set, or to override Zerion's choice. Anything else throws target_token_not_found and names the curated list.
1 parent 9593d44 commit f31598a

4 files changed

Lines changed: 604 additions & 32 deletions

File tree

cli/commands/trading/consolidate.js

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
parseConcurrency,
3939
parseSymbolList,
4040
} from "../../utils/trading/consolidate.js";
41+
import { resolveTargetToken } from "../../utils/trading/consolidate-targets.js";
4142

4243
// Auto-pick the plan-phase quote concurrency from the API-key tier when the
4344
// user didn't pass --concurrency. Paid keys can comfortably handle a small
@@ -135,41 +136,27 @@ export default async function consolidate(args, flags) {
135136

136137
const { walletName, address } = resolveWallet({ ...flags, chain });
137138

138-
// Target token resolution — searchFungibles is fuzzy, so we filter to an
139-
// exact symbol match on the requested chain. Capture the on-chain address
140-
// so filterCandidates can do an address-level target exclusion in addition
141-
// to the symbol check.
142-
const targetUpper = String(toToken).toUpperCase();
143-
let targetFungible;
139+
// Target token resolution. Symbol matching is unreliable (case, bridged
140+
// variants, collisions), so resolveTargetToken accepts only curated symbols
141+
// or raw contract addresses — anything else throws target_token_not_found.
142+
let target;
144143
try {
145-
const response = await api.searchFungibles(toToken, { chainId: chain, limit: 5 });
146-
const results = response.data || [];
147-
targetFungible = results.find((r) => {
148-
const sym = r.attributes?.symbol?.toUpperCase();
149-
const implOnChain = (r.attributes?.implementations || []).some((i) => i.chain_id === chain);
150-
return sym === targetUpper && implOnChain;
151-
}) || null;
144+
target = await resolveTargetToken({
145+
toToken,
146+
chain,
147+
api,
148+
getNativeFungible,
149+
});
152150
} catch (err) {
153-
printError(err.code || "search_error", err.message);
154-
process.exit(1);
155-
}
156-
157-
if (!targetFungible) {
158-
printError(
159-
"target_token_not_found",
160-
`Could not resolve "${toToken}" on chain "${chain}".`,
161-
{
162-
suggestion: `Confirm the symbol with: zerion search ${toToken} --chain ${chain}`,
163-
},
164-
);
151+
printError(err.code || "target_token_not_found", err.message, {
152+
suggestion: err.suggestion,
153+
});
165154
process.exit(1);
166155
}
167156

168-
const targetUsdPrice = Number(targetFungible.attributes?.market_data?.price);
169-
const targetImpl = (targetFungible.attributes?.implementations || []).find(
170-
(i) => i.chain_id === chain,
171-
);
172-
const targetAddress = targetImpl?.address ? String(targetImpl.address).toLowerCase() : null;
157+
const targetUpper = target.symbol;
158+
const targetAddress = target.address;
159+
const targetUsdPrice = target.usdPrice;
173160

174161
// Native gas-token symbol — used by the filter to detect the chain's native
175162
// currency in positions. Catalog lookup can fail (rate limit / no native

0 commit comments

Comments
 (0)