ChainWithdrawalDriver.js
1 import { logger } from "@/middleware/logging.js"; 2 import { toBigInt, scaleAmount, unscaleAmount } from "@/utils/cryptoUtils.js"; 3 4 export class ChainWithdrawalDriver { 5 constructor(config) { 6 this.config = config; 7 this.currency = config.currency; 8 this.chain = config.chain; 9 this.networkId = config.networkId; 10 } 11 12 normalizePrivateKey(privateKey) { 13 if (!privateKey || typeof privateKey !== "string") { 14 throw new Error("Invalid private key format"); 15 } 16 17 const normalized = privateKey.trim().toLowerCase(); 18 19 if (normalized.startsWith("0x")) { 20 return normalized; 21 } 22 23 if (normalized.startsWith("[") && normalized.endsWith("]")) { 24 try { 25 const parsed = JSON.parse(normalized); 26 if (Array.isArray(parsed) && parsed.every(b => typeof b === "number" && b >= 0 && b <= 255)) { 27 return "0x" + Buffer.from(parsed).toString("hex"); 28 } 29 } catch { 30 throw new Error("Invalid array private key format"); 31 } 32 } 33 34 if (/^[0-9a-f]+$/i.test(normalized)) { 35 return "0x" + normalized; 36 } 37 38 throw new Error("Unsupported private key format"); 39 } 40 41 async lock(_amount, _playerAddress) { 42 throw new Error("lock method must be implemented by subclass"); 43 } 44 45 async release(_lockId, _toAddress, _amount) { 46 throw new Error("release method must be implemented by subclass"); 47 } 48 49 async refund(_lockId, _toAddress, _amount) { 50 throw new Error("refund method must be implemented by subclass"); 51 } 52 53 async getBalance(_address) { 54 throw new Error("getBalance method must be implemented by subclass"); 55 } 56 57 validateAddress(_address) { 58 throw new Error("validateAddress method must be implemented by subclass"); 59 } 60 61 async verifyTransaction(_txHash, _expectedSender, _expectedAmount) { 62 throw new Error("verifyTransaction method must be implemented by subclass"); 63 } 64 65 async fetchGasFee() { 66 throw new Error("fetchGasFee method must be implemented by subclass"); 67 } 68 69 toSmallestUnit(amount) { 70 try { 71 const scaled = scaleAmount(amount, this.config.decimals); 72 return scaled.toString(); 73 } catch (error) { 74 logger.error("Failed to convert to smallest unit:", error); 75 throw new Error(`Invalid amount: ${amount}`); 76 } 77 } 78 79 fromSmallestUnit(amountSmallest) { 80 try { 81 const { quotient, remainder } = unscaleAmount(toBigInt(amountSmallest), this.config.decimals); 82 83 if (remainder === 0n) { 84 return quotient.toString(); 85 } 86 87 const remainderStr = remainder.toString().padStart(this.config.decimals, "0"); 88 const truncated = remainderStr.substring(0, 8); 89 const result = `${quotient}.${truncated}`; 90 91 return result.replace(/\.?0+$/, ""); 92 } catch (error) { 93 logger.error("Failed to convert from smallest unit:", error); 94 throw new Error(`Invalid amount: ${amountSmallest}`); 95 } 96 } 97 }