Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 87 additions & 40 deletions packages/crypto-wallet-core/src/derivation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,72 @@ const derivers: { [chain: string]: IDeriver } = {
};

export class DeriverProxy {
private get(chain) {
/**
* Returns the list of supported chain identifiers.
*
* @returns {string[]} Array of supported chain names (uppercase)
*/
getSupportedChains(): string[] {
return Object.keys(derivers);
}

/**
* Returns whether a given chain is supported by the deriver proxy.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @returns {boolean} True if the chain is supported
*/
isSupported(chain: string): boolean {
if (!chain || typeof chain !== 'string') {
return false;
}
return chain.toUpperCase() in derivers;
}

/**
* Retrieves the deriver implementation for a given chain.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @returns {IDeriver} The deriver instance for the chain
* @throws {Error} If the chain is not provided or not supported
*/
private get(chain: string): IDeriver {
if (!chain || typeof chain !== 'string') {
throw new Error('Chain must be a non-empty string');
}
const normalizedChain = chain.toUpperCase();
return derivers[normalizedChain];
const deriver = derivers[normalizedChain];
if (!deriver) {
throw new Error(`Unsupported chain: ${chain}. Supported chains: ${this.getSupportedChains().join(', ')}`);
}
return deriver;
}

/**
* This is derives addresses using the conventional paths.
* @param chain
* @param network
* @param xpubKey
* @param addressIndex
* @param isChange
* @param addressType
* @returns
* This derives addresses using the conventional paths.
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {string} xpubKey - The extended public key
* @param {number} addressIndex - The address index
* @param {boolean} isChange - Whether this is a change address
* @param {string} [addressType] - Optional address type
* @returns The derived address
*/
deriveAddress(chain, network, xpubKey, addressIndex, isChange, addressType?) {
deriveAddress(chain: string, network: string, xpubKey: string, addressIndex: number, isChange: boolean, addressType?: string) {
return this.get(chain).deriveAddress(network, xpubKey, addressIndex, isChange, addressType);
}

/**
* This derives keys/addresses using the conventional paths.
* @param chain
* @param network
* @param privKey
* @param addressIndex
* @param isChange
* @param addressType
* @returns
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {string} privKey - The private key
* @param {number} addressIndex - The address index
* @param {boolean} isChange - Whether this is a change address
* @param {string} [addressType] - Optional address type
* @returns The derived private key info
*/
derivePrivateKey(chain, network, privKey, addressIndex, isChange, addressType?) {
derivePrivateKey(chain: string, network: string, privKey: string, addressIndex: number, isChange: boolean, addressType?: string) {
return this.get(chain).derivePrivateKey(network, privKey, addressIndex, isChange, addressType);
}

Expand All @@ -65,14 +101,14 @@ export class DeriverProxy {
* This should probably only be used when importing from another wallet
* where known paths are provided with their keys. Most of the BitPay
* codebase uses `deriveAddress()`
* @param chain
* @param network
* @param xpubKey
* @param path
* @param addressType
* @returns
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {string} xpubKey - The extended public key
* @param {string} path - The derivation path
* @param {string} addressType - The address type
* @returns The derived address
*/
deriveAddressWithPath(chain, network, xpubKey, path, addressType) {
deriveAddressWithPath(chain: string, network: string, xpubKey: string, path: string, addressType: string) {
return this.get(chain).deriveAddressWithPath(network, xpubKey, path, addressType);
}

Expand All @@ -81,31 +117,42 @@ export class DeriverProxy {
* This should probably only be used when importing from another wallet
* where known paths are provided with their keys. Most of the BitPay
* codebase uses `derivePrivateKey()`
* @param chain
* @param network
* @param xprivKey
* @param path
* @param addressType
* @returns
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {string} xprivKey - The extended private key
* @param {string} path - The derivation path
* @param {string} addressType - The address type
* @returns The derived private key info
*/
derivePrivateKeyWithPath(chain, network, xprivKey, path, addressType) {
derivePrivateKeyWithPath(chain: string, network: string, xprivKey: string, path: string, addressType: string) {
return this.get(chain).derivePrivateKeyWithPath(network, xprivKey, path, addressType);
}

/**
* This is a simple function for getting an address from a
* given pub key and chain. There is no derivation happening.
* @param chain
* @param network
* @param pubKey
* @param addressType
* @returns
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {string} pubKey - The public key
* @param {string} [addressType] - Optional address type
* @returns The address
*/
getAddress(chain, network, pubKey, addressType?) {
getAddress(chain: string, network: string, pubKey: string, addressType?: string) {
return this.get(chain).getAddress(network, pubKey, addressType);
}

pathFor(chain, network, account = 0) {
/**
* Returns the BIP44 derivation path for a given chain and network.
*
* @param {string} chain - The chain identifier
* @param {string} network - The network name
* @param {number} [account=0] - The account index
* @returns {string} The derivation path
*/
pathFor(chain: string, network: string, account: number = 0): string {
if (!chain || typeof chain !== 'string') {
throw new Error('Chain must be a non-empty string');
}
const normalizedChain = chain.toUpperCase();
const accountStr = `${account}'`;
const chainConfig = Paths[normalizedChain];
Expand Down
58 changes: 49 additions & 9 deletions packages/crypto-wallet-core/src/transactions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,76 @@ const providers = {
};

export class TransactionsProxy {
get({ chain }) {
/**
* Returns the list of supported chain/token identifiers.
*
* @returns {string[]} Array of supported chain names (uppercase)
*/
getSupportedChains(): string[] {
return Object.keys(providers);
}

/**
* Returns whether a given chain is supported by the transactions proxy.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @returns {boolean} True if the chain is supported
*/
isSupported(chain: string): boolean {
if (!chain || typeof chain !== 'string') {
return false;
}
return chain.toUpperCase() in providers;
}

/**
* Retrieves the transaction provider for a given chain.
*
* @param {{ chain: string }} params - Object containing the chain identifier
* @returns The transaction provider for the chain
* @throws {Error} If the chain is not provided or not supported
*/
get(params: { chain: string }) {
if (!params || typeof params !== 'object') {
throw new Error('Params must be an object with a "chain" property');
}
const chain = params.chain;
if (!chain || typeof chain !== 'string') {
throw new Error('Chain must be a non-empty string');
}
const normalizedChain = chain.toUpperCase();
return providers[normalizedChain];
const provider = providers[normalizedChain];
if (!provider) {
throw new Error(`Unsupported chain: ${chain}. Supported chains: ${this.getSupportedChains().join(', ')}`);
}
return provider;
}

create(params) {
create(params: { chain: string; [key: string]: any }) {
return this.get(params).create(params);
}

sign(params): string {
sign(params: { chain: string; [key: string]: any }): string {
return this.get(params).sign(params);
}

getSignature(params): string {
getSignature(params: { chain: string; [key: string]: any }): string {
return this.get(params).getSignature(params);
}

applySignature(params) {
applySignature(params: { chain: string; [key: string]: any }) {
return this.get(params).applySignature(params);
}

getHash(params) {
getHash(params: { chain: string; [key: string]: any }) {
return this.get(params).getHash(params);
}

transformSignatureObject(params) {
transformSignatureObject(params: { chain: string; [key: string]: any }) {
return this.get(params).transformSignatureObject(params);
}

getSighash(params): string {
getSighash(params: { chain: string; [key: string]: any }): string {
return this.get(params).getSighash(params);
}
}
Expand Down
41 changes: 39 additions & 2 deletions packages/crypto-wallet-core/src/validation/eth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,47 @@ export class EthValidation implements IValidation {
}
const address = this.extractAddress(addressUri);
const prefix = this.regex.exec(addressUri);
return !!prefix && utils.isAddress(address);
if (!prefix) {
return false;
}
if (!utils.isAddress(address)) {
return false;
}
// Validate that numeric parameters contain only valid numbers
if (!this.validateUriParams(addressUri)) {
return false;
}
return true;
}

/**
* Validates that URI parameters contain properly formatted numeric values.
* Returns false if any recognized numeric parameter has an invalid (non-numeric) value.
*
* @param {string} uri - The full URI string
* @returns {boolean} True if all numeric params are valid, or no params exist
*/
protected validateUriParams(uri: string): boolean {
const queryIndex = uri.indexOf('?');
if (queryIndex === -1) {
return true;
}
const queryString = uri.substring(queryIndex + 1);
const params = queryString.split('&');
const numericParams = ['value', 'gas', 'gasPrice', 'gasLimit', 'amount'];

for (const param of params) {
const [key, value] = param.split('=');
if (numericParams.includes(key)) {
if (!value || isNaN(Number(value.replace(',', '.')))) {
return false;
}
}
}
return true;
}

protected extractAddress(data) {
protected extractAddress(data: string): string {
const prefix = /^[a-z]+:/i;
const params = /([?&](value|gas|gasPrice|gasLimit)=(\d+([,.]\d+)?))+/i;
return data.replace(prefix, '').replace(params, '');
Expand Down
67 changes: 63 additions & 4 deletions packages/crypto-wallet-core/src/validation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,75 @@ const validation: { [chain: string]: IValidation } = {
};

export class ValidationProxy {
get(chain) {
/**
* Returns the list of supported chain identifiers.
*
* @returns {string[]} Array of supported chain names (uppercase)
*/
getSupportedChains(): string[] {
return Object.keys(validation);
}

/**
* Returns whether a given chain is supported by the validation proxy.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @returns {boolean} True if the chain is supported
*/
isSupported(chain: string): boolean {
if (!chain || typeof chain !== 'string') {
return false;
}
return chain.toUpperCase() in validation;
}

/**
* Retrieves the validation implementation for a given chain.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @returns {IValidation} The validation instance for the chain
* @throws {Error} If the chain is not provided or not supported
*/
get(chain: string): IValidation {
if (!chain || typeof chain !== 'string') {
throw new Error('Chain must be a non-empty string');
}
const normalizedChain = chain.toUpperCase();
return validation[normalizedChain];
const validator = validation[normalizedChain];
if (!validator) {
throw new Error(`Unsupported chain: ${chain}. Supported chains: ${this.getSupportedChains().join(', ')}`);
}
return validator;
}

validateAddress(chain, network, address) {
/**
* Validates an address for the specified chain and network.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @param {string} network - The network (e.g. 'mainnet', 'testnet')
* @param {string} address - The address to validate
* @returns {boolean} True if the address is valid
* @throws {Error} If chain is unsupported or address is not a string
*/
validateAddress(chain: string, network: string, address: string): boolean {
if (!address || typeof address !== 'string') {
return false;
}
return this.get(chain).validateAddress(network, address);
}

validateUri(chain, addressUri) {
/**
* Validates a URI for the specified chain.
*
* @param {string} chain - The chain identifier (case-insensitive)
* @param {string} addressUri - The URI to validate
* @returns {boolean} True if the URI is valid
* @throws {Error} If chain is unsupported
*/
validateUri(chain: string, addressUri: string): boolean {
if (!addressUri || typeof addressUri !== 'string') {
return false;
}
return this.get(chain).validateUri(addressUri);
}
}
Expand Down
Loading