Spaces:
Sleeping
Sleeping
| """ | |
| Token classification using the Howey Test and other regulatory frameworks. | |
| Determines if a crypto token is a security or utility token. | |
| """ | |
| import logging | |
| from typing import Dict, List, Optional, Tuple | |
| from datetime import datetime | |
| import re | |
| logger = logging.getLogger(__name__) | |
| class HoweyTestAnalyzer: | |
| """ | |
| Analyzes tokens using the SEC's Howey Test. | |
| The Howey Test has 4 prongs: | |
| 1. Investment of money | |
| 2. In a common enterprise | |
| 3. With an expectation of profits | |
| 4. Derived from the efforts of others | |
| If all 4 are met, the token is likely a security. | |
| """ | |
| def __init__(self): | |
| """Initialize Howey Test analyzer.""" | |
| self.test_criteria = { | |
| 'investment_of_money': { | |
| 'keywords': [ | |
| 'purchase', 'buy', 'invest', 'sale', 'ico', 'token sale', | |
| 'presale', 'crowdsale', 'fundraising', 'payment', 'contribute' | |
| ], | |
| 'weight': 0.25 | |
| }, | |
| 'common_enterprise': { | |
| 'keywords': [ | |
| 'pool', 'pooled', 'combined', 'collective', 'together', | |
| 'treasury', 'ecosystem', 'platform', 'network', 'protocol' | |
| ], | |
| 'weight': 0.25 | |
| }, | |
| 'expectation_of_profits': { | |
| 'keywords': [ | |
| 'profit', 'returns', 'gains', 'appreciation', 'yield', | |
| 'rewards', 'earnings', 'income', 'dividend', 'interest', | |
| 'roi', 'return on investment', 'price increase' | |
| ], | |
| 'weight': 0.25 | |
| }, | |
| 'efforts_of_others': { | |
| 'keywords': [ | |
| 'team', 'development', 'management', 'founders', 'developers', | |
| 'operated by', 'managed by', 'governance', 'roadmap', | |
| 'build', 'create', 'maintain', 'improve', 'update' | |
| ], | |
| 'weight': 0.25 | |
| } | |
| } | |
| def analyze_prong(self, text: str, prong_name: str) -> Tuple[bool, float, List[str]]: | |
| """ | |
| Analyze a single Howey Test prong. | |
| Args: | |
| text: Token description/whitepaper text | |
| prong_name: Name of the prong to analyze | |
| Returns: | |
| Tuple of (prong_met, confidence, evidence_keywords) | |
| """ | |
| if prong_name not in self.test_criteria: | |
| raise ValueError(f"Invalid prong: {prong_name}") | |
| criteria = self.test_criteria[prong_name] | |
| keywords = criteria['keywords'] | |
| text_lower = text.lower() | |
| # Find matching keywords | |
| matches = [] | |
| for keyword in keywords: | |
| pattern = r'\b' + re.escape(keyword) + r'\b' | |
| if re.search(pattern, text_lower): | |
| matches.append(keyword) | |
| # Calculate confidence based on match density | |
| match_count = len(matches) | |
| word_count = len(text_lower.split()) | |
| match_density = (match_count / (word_count / 100)) if word_count > 0 else 0 | |
| # Prong is "met" if we have multiple keyword matches | |
| prong_met = match_count >= 2 | |
| confidence = min(match_density / 5, 1.0) # Normalize to 0-1 | |
| return prong_met, confidence, matches | |
| def run_howey_test(self, text: str) -> Dict: | |
| """ | |
| Run full Howey Test analysis on token description. | |
| Args: | |
| text: Token description/whitepaper text | |
| Returns: | |
| Dictionary with test results | |
| """ | |
| results = { | |
| 'prongs': {}, | |
| 'prongs_met': 0, | |
| 'is_security': False, | |
| 'overall_confidence': 0.0, | |
| 'evidence': {}, | |
| 'analysis_timestamp': datetime.now().isoformat() | |
| } | |
| # Analyze each prong | |
| for prong_name in self.test_criteria.keys(): | |
| met, confidence, evidence = self.analyze_prong(text, prong_name) | |
| results['prongs'][prong_name] = { | |
| 'met': met, | |
| 'confidence': confidence, | |
| 'evidence_count': len(evidence) | |
| } | |
| results['evidence'][prong_name] = evidence | |
| if met: | |
| results['prongs_met'] += 1 | |
| # Token is a security if all 4 prongs are met | |
| results['is_security'] = results['prongs_met'] == 4 | |
| # Calculate overall confidence (average of prong confidences) | |
| confidences = [p['confidence'] for p in results['prongs'].values()] | |
| results['overall_confidence'] = sum(confidences) / len(confidences) | |
| # Adjust confidence based on prongs met | |
| if results['prongs_met'] < 4: | |
| # Reduce confidence if not all prongs met | |
| results['overall_confidence'] *= (results['prongs_met'] / 4) | |
| logger.info( | |
| f"Howey Test: {results['prongs_met']}/4 prongs met, " | |
| f"is_security={results['is_security']}, " | |
| f"confidence={results['overall_confidence']:.2f}" | |
| ) | |
| return results | |
| class TokenClassifier: | |
| """ | |
| Comprehensive token classifier using multiple frameworks. | |
| - US: Howey Test | |
| - EU: MiCA classification | |
| - Singapore: DPT classification | |
| """ | |
| def __init__(self): | |
| """Initialize token classifier.""" | |
| self.howey_analyzer = HoweyTestAnalyzer() | |
| logger.info("TokenClassifier initialized") | |
| def classify_us(self, token_description: str) -> Dict: | |
| """ | |
| Classify token under US law (SEC Howey Test). | |
| Args: | |
| token_description: Description of token mechanics | |
| Returns: | |
| Classification result | |
| """ | |
| howey_result = self.howey_analyzer.run_howey_test(token_description) | |
| classification = { | |
| 'jurisdiction': 'us', | |
| 'framework': 'SEC Howey Test', | |
| 'classification': 'security' if howey_result['is_security'] else 'utility', | |
| 'confidence': howey_result['overall_confidence'], | |
| 'howey_test': howey_result, | |
| 'regulatory_implications': self._get_us_implications(howey_result) | |
| } | |
| return classification | |
| def classify_eu(self, token_description: str) -> Dict: | |
| """ | |
| Classify token under EU MiCA framework. | |
| Args: | |
| token_description: Token description | |
| Returns: | |
| Classification result | |
| """ | |
| text_lower = token_description.lower() | |
| # MiCA categories | |
| is_utility_token = any([ | |
| 'access' in text_lower, | |
| 'usage' in text_lower, | |
| 'service' in text_lower, | |
| 'platform access' in text_lower | |
| ]) | |
| is_asset_referenced = any([ | |
| 'backed' in text_lower, | |
| 'pegged' in text_lower, | |
| 'collateralized' in text_lower, | |
| 'reserve' in text_lower | |
| ]) | |
| is_e_money = any([ | |
| 'fiat' in text_lower, | |
| 'currency' in text_lower, | |
| 'stablecoin' in text_lower, | |
| 'payment' in text_lower | |
| ]) | |
| # Determine primary category | |
| if is_e_money: | |
| category = 'e-money token' | |
| elif is_asset_referenced: | |
| category = 'asset-referenced token' | |
| elif is_utility_token: | |
| category = 'utility token' | |
| else: | |
| category = 'crypto-asset' # Default MiCA category | |
| classification = { | |
| 'jurisdiction': 'eu', | |
| 'framework': 'MiCA', | |
| 'classification': category, | |
| 'confidence': 0.6, # Lower confidence for heuristic classification | |
| 'mica_categories': { | |
| 'utility_token': is_utility_token, | |
| 'asset_referenced_token': is_asset_referenced, | |
| 'e_money_token': is_e_money | |
| }, | |
| 'regulatory_implications': self._get_eu_implications(category) | |
| } | |
| return classification | |
| def classify_singapore(self, token_description: str) -> Dict: | |
| """ | |
| Classify token under Singapore MAS framework. | |
| Args: | |
| token_description: Token description | |
| Returns: | |
| Classification result | |
| """ | |
| text_lower = token_description.lower() | |
| # MAS Payment Services Act - Digital Payment Token (DPT) | |
| is_dpt = any([ | |
| 'payment' in text_lower, | |
| 'medium of exchange' in text_lower, | |
| 'store of value' in text_lower, | |
| 'transfer' in text_lower | |
| ]) | |
| is_capital_markets_product = any([ | |
| 'security' in text_lower, | |
| 'investment' in text_lower, | |
| 'profit' in text_lower, | |
| 'return' in text_lower, | |
| 'dividend' in text_lower | |
| ]) | |
| # Determine category | |
| if is_capital_markets_product: | |
| category = 'capital markets product' | |
| elif is_dpt: | |
| category = 'digital payment token' | |
| else: | |
| category = 'unregulated token' | |
| classification = { | |
| 'jurisdiction': 'singapore', | |
| 'framework': 'MAS PSA', | |
| 'classification': category, | |
| 'confidence': 0.6, | |
| 'mas_categories': { | |
| 'digital_payment_token': is_dpt, | |
| 'capital_markets_product': is_capital_markets_product | |
| }, | |
| 'regulatory_implications': self._get_singapore_implications(category) | |
| } | |
| return classification | |
| def classify_all_jurisdictions(self, token_description: str) -> Dict: | |
| """ | |
| Classify token across all supported jurisdictions. | |
| Args: | |
| token_description: Token description/whitepaper text | |
| Returns: | |
| Dictionary of classifications per jurisdiction | |
| """ | |
| return { | |
| 'us': self.classify_us(token_description), | |
| 'eu': self.classify_eu(token_description), | |
| 'singapore': self.classify_singapore(token_description), | |
| 'summary': self._generate_summary(token_description) | |
| } | |
| def _get_us_implications(self, howey_result: Dict) -> List[str]: | |
| """Get regulatory implications for US classification.""" | |
| implications = [] | |
| if howey_result['is_security']: | |
| implications.extend([ | |
| "Token is likely a security under US law", | |
| "Must register with SEC or qualify for exemption", | |
| "Consider Regulation D (private placement) or Regulation A+", | |
| "Must comply with securities laws for trading", | |
| "May need broker-dealer registration for exchanges" | |
| ]) | |
| else: | |
| implications.extend([ | |
| "Token may be a utility token (not a security)", | |
| "Still subject to FinCEN MSB registration if used for payments", | |
| "State money transmitter licenses may be required", | |
| "Consumer protection laws still apply", | |
| "Monitor SEC guidance - classification can change" | |
| ]) | |
| return implications | |
| def _get_eu_implications(self, category: str) -> List[str]: | |
| """Get regulatory implications for EU classification.""" | |
| implications_map = { | |
| 'e-money token': [ | |
| "Subject to strict MiCA e-money token requirements", | |
| "Need authorization as e-money institution", | |
| "Must maintain 1:1 backing with fiat reserves", | |
| "Enhanced consumer protection requirements", | |
| "Effective from June 2024" | |
| ], | |
| 'asset-referenced token': [ | |
| "Subject to MiCA asset-referenced token regime", | |
| "Must maintain reserve of referenced assets", | |
| "Requires authorization from regulator", | |
| "Ongoing reporting and transparency requirements", | |
| "White paper must be approved" | |
| ], | |
| 'utility token': [ | |
| "Lower regulatory burden under MiCA", | |
| "Still requires white paper publication", | |
| "Consumer protection rules apply", | |
| "Marketing restrictions apply", | |
| "Effective from July 2024" | |
| ], | |
| 'crypto-asset': [ | |
| "General MiCA crypto-asset rules apply", | |
| "CASP authorization needed for services", | |
| "White paper required for public offerings", | |
| "AML/CTF compliance mandatory" | |
| ] | |
| } | |
| return implications_map.get(category, ["MiCA framework applies"]) | |
| def _get_singapore_implications(self, category: str) -> List[str]: | |
| """Get regulatory implications for Singapore classification.""" | |
| implications_map = { | |
| 'digital payment token': [ | |
| "Requires DPT service provider license from MAS", | |
| "Must comply with Payment Services Act", | |
| "AML/CFT requirements apply", | |
| "Technology risk management guidelines", | |
| "Fit and proper criteria for operators" | |
| ], | |
| 'capital markets product': [ | |
| "Subject to Securities and Futures Act", | |
| "Requires CMS license from MAS", | |
| "Prospectus or exemption required", | |
| "Ongoing reporting obligations", | |
| "Higher regulatory scrutiny" | |
| ], | |
| 'unregulated token': [ | |
| "May not require MAS licensing", | |
| "Still subject to general laws", | |
| "Monitor for regulatory changes", | |
| "Consumer protection laws apply" | |
| ] | |
| } | |
| return implications_map.get(category, ["Review MAS guidelines"]) | |
| def _generate_summary(self, token_description: str) -> Dict: | |
| """Generate summary across jurisdictions.""" | |
| us_result = self.classify_us(token_description) | |
| eu_result = self.classify_eu(token_description) | |
| sg_result = self.classify_singapore(token_description) | |
| is_security_anywhere = ( | |
| us_result['classification'] == 'security' or | |
| sg_result['classification'] == 'capital markets product' | |
| ) | |
| return { | |
| 'is_security_anywhere': is_security_anywhere, | |
| 'most_restrictive_jurisdiction': 'us' if us_result['classification'] == 'security' else 'eu', | |
| 'classifications': { | |
| 'us': us_result['classification'], | |
| 'eu': eu_result['classification'], | |
| 'singapore': sg_result['classification'] | |
| }, | |
| 'recommendation': ( | |
| "Consult securities lawyer immediately - token appears to be a security" | |
| if is_security_anywhere else | |
| "Token may qualify as utility token, but verify with legal counsel" | |
| ) | |
| } | |
| # Convenience function | |
| def classify_token(token_description: str, jurisdiction: Optional[str] = None) -> Dict: | |
| """ | |
| Quick classify a token. | |
| Args: | |
| token_description: Token description/whitepaper | |
| jurisdiction: Specific jurisdiction ('us', 'eu', 'singapore') or None for all | |
| Returns: | |
| Classification result | |
| """ | |
| classifier = TokenClassifier() | |
| if jurisdiction: | |
| if jurisdiction == 'us': | |
| return classifier.classify_us(token_description) | |
| elif jurisdiction == 'eu': | |
| return classifier.classify_eu(token_description) | |
| elif jurisdiction == 'singapore': | |
| return classifier.classify_singapore(token_description) | |
| else: | |
| raise ValueError(f"Unsupported jurisdiction: {jurisdiction}") | |
| else: | |
| return classifier.classify_all_jurisdictions(token_description) | |
| if __name__ == "__main__": | |
| # Example usage | |
| sample_token = """ | |
| Our governance token allows holders to vote on protocol upgrades and earn | |
| rewards from transaction fees. Tokens are sold in a public sale at $0.50 each. | |
| The development team will use funds to build the platform and market the product. | |
| Early investors expect significant returns as the platform grows and token value | |
| appreciates. The team manages the treasury and executes the roadmap. | |
| """ | |
| print("\n=== Token Classification ===\n") | |
| # Full analysis | |
| results = classify_token(sample_token) | |
| print("US Classification:") | |
| us = results['us'] | |
| print(f" Classification: {us['classification']}") | |
| print(f" Confidence: {us['confidence']:.2f}") | |
| print(f" Howey Test: {us['howey_test']['prongs_met']}/4 prongs met") | |
| print("\nEU Classification:") | |
| eu = results['eu'] | |
| print(f" Classification: {eu['classification']}") | |
| print(f" Confidence: {eu['confidence']:.2f}") | |
| print("\nSingapore Classification:") | |
| sg = results['singapore'] | |
| print(f" Classification: {sg['classification']}") | |
| print(f" Confidence: {sg['confidence']:.2f}") | |
| print("\nSummary:") | |
| summary = results['summary'] | |
| print(f" Is security anywhere: {summary['is_security_anywhere']}") | |
| print(f" Recommendation: {summary['recommendation']}") | |