@clayno-club/asset-flow
v0.2.2
Published
Shared asset-flow classification and normalization for supported chains
Readme
@clayno-club/asset-flow
Deterministic asset-flow classification and normalization for supported chains.
This package turns transaction data into a canonical ownership history for a tracked asset. It is designed for consumers that want consistent classification results across services, codebases, or audit pipelines.
Current support:
- Solana NFT asset-flow normalization via the root convenience exports such as
classify,buildFlows, andbuildOwnershipPeriods - Sui Popkins classification via explicit Sui exports such as
classifySuiTransactionForAsset
What it does
- classify supported chain transactions for a tracked asset
- merge raw ownership evidence into enhanced Solana transactions when needed
- normalize an asset's transaction history into canonical flows
- derive ownership periods from normalized flows
- explain why a transaction matched a given classification path
- analyze provided transaction sets for review candidates
What it does not do
- fetch transaction history from RPC providers or indexers
- persist results to a database
- manage queues, jobs, or orchestration
You provide the transaction data. The package provides deterministic interpretation.
Supported entrypoints
- Root exports: Solana-oriented convenience API such as
classify,buildMintHistory,buildFlows,buildValueMovements, andaugmentWithRaw - Sui exports: explicit helpers such as
classifySuiTransactionForAssetand the Sui transaction/type definitions
Install
pnpm add @clayno-club/asset-flowCore API
Classification:
classify(tx, mint)— classify a single transaction for a tracked mintexplain(tx, mint)— same as classify, but returns the full decision trace
Raw augmentation:
shouldFetchRaw(tx, mint)— check if raw data would improve classificationaugmentWithRaw(tx, rawTx)— merge raw ownership evidence into enhanced tx
History normalization:
buildMintHistory(transactions, mint)— full pipeline: flows + value movements + ownership periodsbuildFlows(transactions, mint)— just the canonical flowsbuildValueMovements(transactions, mint)— just the value movementsbuildOwnershipPeriods(flows)— ownership periods from flowsextractMints(tx)— extract candidate mint addresses from a transaction
Example: classify one transaction
import { classify } from "@clayno-club/asset-flow";
const classification = classify(tx, mint);
console.log(classification.family);
console.log(classification.derivedType);
console.log(classification.materialization);Example: augment enhanced data with raw ownership evidence
import { augmentWithRaw } from "@clayno-club/asset-flow";
const augmented = augmentWithRaw(enhancedTx, rawTx);Example: normalize mint history
import { buildMintHistory } from "@clayno-club/asset-flow";
const normalized = buildMintHistory(transactions, mint);
console.log(normalized.flows);
console.log(normalized.valueMovements);
console.log(normalized.periods);Example: explain a classification decision
import { explain } from "@clayno-club/asset-flow";
const explanation = explain(tx, mint);
console.log(explanation.facts);
console.log(explanation.candidates);
console.log(explanation.selection);Concepts
The package is structured around three steps:
Facts Convert transaction evidence into normalized facts about ownership change, settlement, program signals, and participants.
Classification Match the transaction to a known family and determine whether it should materialize as a canonical flow, be skipped as non-settlement activity, or remain review-worthy.
Normalization Turn classified transaction history into canonical mint flows, value movements, and ownership periods.
Output model
The normalized history contains:
flowsCanonical mint ownership events such asMINT,SALE,TRANSFER,SWAP,LIST,DELIST,LOCK,UNLOCK, andFORECLOSUREvalueMovementsAssociated native or fungible settlement movements with roles such asSALE_GROSS,SELLER_PROCEEDS,ROYALTY,MARKETPLACE_FEE,RENT, andSWAP_CONSIDERATIONperiodsReconstructed ownership periods derived from normalized beneficial flows
When to use raw data
Some Solana transactions do not expose enough ownership detail in enhanced/indexed form alone. When raw transaction data is available, use:
shouldFetchRaw()augmentWithRaw()
This lets you keep the classification path deterministic while improving owner resolution for custody-heavy or protocol-heavy transactions.
Review analysis helpers
If you already have a set of transactions and want to identify unresolved patterns:
collectCoverageReviewCandidates()returns review-worthy transactionsclusterCoverageReviewCandidates()groups them into stable fingerprintsscanTransactionsForCoverage()combines both into a mint-scoped summary
These helpers are pure over provided transactions and do not fetch data.
Extending the matcher set
When adding support for a new marketplace or protocol family:
- identify the reusable evidence
- add or reuse fact-building signals
- add a narrow matcher for the family
- register it with the appropriate priority
- add fixture coverage and regression tests
Prefer adding a matcher for a real transaction family over expanding generic fallback behavior.
Testing
Typical verification:
pnpm --filter @clayno-club/asset-flow test
pnpm --filter @clayno-club/asset-flow build
pnpm --filter @clayno-club/asset-flow type-check