/ src / services / chains / ChainWithdrawalDriver.js
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  }