-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Refactor #64
Changes from all commits
383db47
db3b072
f1fcbb2
d6fd425
1cee260
1b5440f
22a375f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { CrossmintApi } from '../transport/CrossmintApi'; | ||
| import { API_VERSIONS } from '../utils/constants'; | ||
| import { ApiResponse } from '../transport/types'; | ||
|
|
||
| export class WalletApi { | ||
| constructor(private api: CrossmintApi) {} | ||
|
|
||
| async getBalance( | ||
| walletLocator: string, | ||
| chains: string, | ||
| tkn: string, | ||
| ): Promise<ApiResponse> { | ||
| const endpoint = `wallets/${walletLocator}/balances?chains=${encodeURIComponent(chains)}&tokens=${encodeURIComponent(tkn)}`; | ||
| return await this.api.get(endpoint, API_VERSIONS.WALLETS); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,126 @@ | ||||||||
| import { IChainProvider } from './IChainProvider'; | ||||||||
| import { SolanaProvider } from './solana/SolanaProvider'; | ||||||||
|
|
||||||||
| /** | ||||||||
| * Chain Factory | ||||||||
| * | ||||||||
| * Factory for creating blockchain provider instances. | ||||||||
| * This is where new blockchain support is added. | ||||||||
| * | ||||||||
| * To add a new blockchain: | ||||||||
| * 1. Create a new provider class implementing IChainProvider | ||||||||
| * 2. Add a case in createProvider() method | ||||||||
| * 3. That's it! The new chain will automatically appear in all node properties | ||||||||
| */ | ||||||||
| export class ChainFactory { | ||||||||
| /** | ||||||||
| * Creates a chain provider instance for the specified blockchain type | ||||||||
| * | ||||||||
| * @param chainType - The blockchain type (e.g., 'solana', 'evm', 'bitcoin') | ||||||||
| * @returns The chain provider instance or undefined if not supported | ||||||||
| */ | ||||||||
| static createProvider(chainType: string): IChainProvider | undefined { | ||||||||
| switch (chainType.toLowerCase()) { | ||||||||
| case 'solana': | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use
Suggested change
Context Used: Rule from Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: shared/chains/ChainFactory.ts
Line: 24:24
Comment:
Use `CHAIN_TYPES.SOLANA` constant from `constants.ts` instead of string literal for consistency and type safety.
```suggestion
case CHAIN_TYPES.SOLANA:
```
**Context Used:** Rule from `dashboard` - Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... ([source](https://app.greptile.com/review/custom-context?memory=0e60096d-0843-4800-801b-f8a78b766fbc))
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
||||||||
| return new SolanaProvider(); | ||||||||
| // Future chains can be added here: | ||||||||
| // case 'evm': | ||||||||
| // return new EVMProvider(); | ||||||||
| // case 'bitcoin': | ||||||||
| // return new BitcoinProvider(); | ||||||||
| default: | ||||||||
| return undefined; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Get all supported chain types | ||||||||
| * | ||||||||
| * @returns Array of supported chain type identifiers | ||||||||
| */ | ||||||||
| static getSupportedChainTypes(): string[] { | ||||||||
| return ['solana']; | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using Context Used: Rule from Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: shared/chains/ChainFactory.ts
Line: 42:42
Comment:
Consider using `CHAIN_TYPES.SOLANA` constant from `constants.ts` instead of string literal
**Context Used:** Rule from `dashboard` - Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... ([source](https://app.greptile.com/review/custom-context?memory=0e60096d-0843-4800-801b-f8a78b766fbc))
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use
Suggested change
Context Used: Rule from Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: shared/chains/ChainFactory.ts
Line: 41:42
Comment:
Use `CHAIN_TYPES.SOLANA` constant instead of string literal.
```suggestion
return [CHAIN_TYPES.SOLANA];
```
**Context Used:** Rule from `dashboard` - Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... ([source](https://app.greptile.com/review/custom-context?memory=0e60096d-0843-4800-801b-f8a78b766fbc))
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
||||||||
| // When adding new chains, add them here: | ||||||||
| // return ['solana', 'evm', 'bitcoin']; | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Get all available chain providers | ||||||||
| * | ||||||||
| * @returns Array of all chain provider instances | ||||||||
| */ | ||||||||
| static getAllProviders(): IChainProvider[] { | ||||||||
| return this.getSupportedChainTypes() | ||||||||
| .map(chainType => this.createProvider(chainType)) | ||||||||
| .filter((provider): provider is IChainProvider => provider !== undefined); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Check if a chain type is supported | ||||||||
| * | ||||||||
| * @param chainType - The chain type to check | ||||||||
| * @returns True if the chain is supported | ||||||||
| */ | ||||||||
| static isSupported(chainType: string): boolean { | ||||||||
| return this.getSupportedChainTypes().includes(chainType.toLowerCase()); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Get chain options for n8n node properties | ||||||||
| * | ||||||||
| * @returns Array of options suitable for n8n node properties | ||||||||
| */ | ||||||||
| static getChainOptions(): Array<{ name: string; value: string; description: string }> { | ||||||||
| return this.getAllProviders().map(provider => ({ | ||||||||
| name: provider.displayName, | ||||||||
| value: provider.chainType, | ||||||||
| description: `${provider.displayName} blockchain`, | ||||||||
| })); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Get network options for a specific chain | ||||||||
| * | ||||||||
| * @param chainType - The chain type | ||||||||
| * @returns Array of network options or empty array if chain not found | ||||||||
| */ | ||||||||
| static getNetworkOptions(chainType: string): Array<{ name: string; value: string; description: string }> { | ||||||||
| const provider = this.createProvider(chainType); | ||||||||
| if (!provider) { | ||||||||
| return []; | ||||||||
| } | ||||||||
|
|
||||||||
| return provider.getNetworks().map(network => ({ | ||||||||
| name: network.name, | ||||||||
| value: network.id, | ||||||||
| description: network.isTestnet ? 'Testnet' : 'Mainnet', | ||||||||
| })); | ||||||||
| } | ||||||||
|
|
||||||||
| /** | ||||||||
| * Get the chainType from a network identifier | ||||||||
| * e.g., "solana-devnet" -> "solana", "ethereum" -> "evm", "polygon" -> "evm" | ||||||||
| * | ||||||||
| * @param network - The network identifier (e.g., "solana", "solana-devnet", "ethereum") | ||||||||
| * @returns The chainType or undefined if not found | ||||||||
| */ | ||||||||
| static getChainTypeFromNetwork(network: string): string | undefined { | ||||||||
| const normalizedNetwork = network.toLowerCase(); | ||||||||
|
|
||||||||
| for (const provider of this.getAllProviders()) { | ||||||||
| const networks = provider.getNetworks(); | ||||||||
| if (networks.some(n => n.id.toLowerCase() === normalizedNetwork)) { | ||||||||
| return provider.chainType; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| // Fallback: check if network starts with a known chainType | ||||||||
| for (const chainType of this.getSupportedChainTypes()) { | ||||||||
| if (normalizedNetwork.startsWith(chainType)) { | ||||||||
| return chainType; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| return undefined; | ||||||||
| } | ||||||||
| } | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| /** | ||
| * Blockchain Provider Interface | ||
| * | ||
| * This interface defines the contract for all blockchain implementations. | ||
| * Each blockchain (Solana, EVM, Bitcoin, etc.) must implement this interface | ||
| * to provide chain-specific functionality. | ||
| */ | ||
|
|
||
| /** | ||
| * Network configuration for a blockchain | ||
| */ | ||
| export interface ChainNetwork { | ||
| /** Network identifier (e.g., 'solana', 'solana-devnet', 'ethereum', 'base') */ | ||
| id: string; | ||
| /** Display name for the network */ | ||
| name: string; | ||
| /** Whether this is a testnet/devnet */ | ||
| isTestnet: boolean; | ||
| /** Optional RPC endpoint */ | ||
| rpcEndpoint?: string; | ||
| } | ||
|
|
||
| /** | ||
| * Transaction data structure for signing | ||
| */ | ||
| export interface TransactionData { | ||
| /** The message/hash to sign */ | ||
| message: string; | ||
| /** Optional transaction ID from the API */ | ||
| transactionId?: string; | ||
| /** Additional chain-specific data */ | ||
| metadata?: Record<string, unknown>; | ||
| } | ||
|
|
||
| /** | ||
| * Signature result from transaction signing | ||
| */ | ||
| export interface SignatureResult { | ||
| /** The signature as a string */ | ||
| signature: string; | ||
| /** The signer's address */ | ||
| signerAddress: string; | ||
| /** Additional metadata about the signature */ | ||
| metadata?: Record<string, unknown>; | ||
| } | ||
|
|
||
| /** | ||
| * Validation result | ||
| */ | ||
| export interface ValidationResult { | ||
| valid: boolean; | ||
| error?: string; | ||
| } | ||
|
|
||
| /** | ||
| * Chain Provider Interface | ||
| * | ||
| * Implement this interface to add support for a new blockchain. | ||
| */ | ||
| export interface IChainProvider { | ||
| /** | ||
| * Unique identifier for this chain type (e.g., 'solana', 'evm', 'bitcoin') | ||
| */ | ||
| readonly chainType: string; | ||
|
|
||
| /** | ||
| * Display name for the chain (e.g., 'Solana', 'Ethereum Virtual Machine') | ||
| */ | ||
| readonly displayName: string; | ||
|
|
||
| /** | ||
| * Get all available networks for this chain | ||
| * @returns Array of available networks | ||
| */ | ||
| getNetworks(): ChainNetwork[]; | ||
|
|
||
| /** | ||
| * Get a specific network by its ID | ||
| * @param networkId - The network identifier | ||
| * @returns Network configuration or undefined if not found | ||
| */ | ||
| getNetwork(networkId: string): ChainNetwork | undefined; | ||
|
|
||
| /** | ||
| * Validate a wallet address for this chain | ||
| * @param address - The wallet address to validate | ||
| * @returns Validation result with optional error message | ||
| */ | ||
| validateAddress(address: string): ValidationResult; | ||
|
|
||
| /** | ||
| * Validate a private key for this chain | ||
| * @param privateKey - The private key to validate | ||
| * @returns Validation result with optional error message | ||
| */ | ||
| validatePrivateKey(privateKey: string): ValidationResult; | ||
|
|
||
| /** | ||
| * Format an address according to chain conventions | ||
| * (e.g., lowercase for EVM, as-is for Solana) | ||
| * @param address - The address to format | ||
| * @returns Formatted address | ||
| */ | ||
| formatAddress(address: string): string; | ||
|
|
||
| /** | ||
| * Derive the public address from a private key | ||
| * @param privateKey - The private key | ||
| * @returns The public address | ||
| * @throws Error if private key is invalid | ||
| */ | ||
| getAddressFromPrivateKey(privateKey: string): string; | ||
|
|
||
| /** | ||
| * Sign a transaction with a private key | ||
| * @param data - Transaction data to sign | ||
| * @param privateKey - The private key to sign with | ||
| * @returns Signature result | ||
| * @throws Error if signing fails | ||
| */ | ||
| signTransaction(data: TransactionData, privateKey: string): Promise<SignatureResult>; | ||
|
|
||
| /** | ||
| * Get the regex pattern for address validation | ||
| * Used for UI validation in n8n node properties | ||
| * @returns Regex pattern as string | ||
| */ | ||
| getAddressValidationRegex(): string; | ||
|
|
||
| /** | ||
| * Get the validation error message for addresses | ||
| * @returns Error message shown to users | ||
| */ | ||
| getAddressValidationError(): string; | ||
|
|
||
| /** | ||
| * Get example address for placeholder text | ||
| * @returns Example address string | ||
| */ | ||
| getExampleAddress(): string; | ||
| } | ||
|
|
||
| /** | ||
| * Base class for chain providers with common functionality | ||
| */ | ||
| export abstract class BaseChainProvider implements IChainProvider { | ||
| abstract readonly chainType: string; | ||
| abstract readonly displayName: string; | ||
|
|
||
| abstract getNetworks(): ChainNetwork[]; | ||
| abstract getNetwork(networkId: string): ChainNetwork | undefined; | ||
| abstract validateAddress(address: string): ValidationResult; | ||
| abstract validatePrivateKey(privateKey: string): ValidationResult; | ||
| abstract formatAddress(address: string): string; | ||
| abstract getAddressFromPrivateKey(privateKey: string): string; | ||
| abstract signTransaction(data: TransactionData, privateKey: string): Promise<SignatureResult>; | ||
| abstract getAddressValidationRegex(): string; | ||
| abstract getAddressValidationError(): string; | ||
| abstract getExampleAddress(): string; | ||
|
|
||
| /** | ||
| * Helper method to create a validation result | ||
| */ | ||
| protected createValidationResult(valid: boolean, error?: string): ValidationResult { | ||
| return { valid, error }; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /** | ||
| * Blockchain Abstraction Layer | ||
| * | ||
| * Central exports for all blockchain-related functionality. | ||
| */ | ||
|
|
||
| export * from './IChainProvider'; | ||
| export * from './ChainFactory'; | ||
| export * from './solana/SolanaProvider'; | ||
|
|
||
| // Future exports: | ||
| // export * from './evm/EVMProvider'; | ||
| // export * from './bitcoin/BitcoinProvider'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using
CHAIN_TYPES.SOLANAconstant fromconstants.tsinstead of string literal for consistency with custom rule 0e60096dContext Used: Rule from
dashboard- Use enum constants (e.g., Chain.SOLANA, Chain.BASE) instead of string literals when comparing chain ... (source)Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI