UNPKG

@zebec-network/exchange-card-sdk

Version:
219 lines (218 loc) 9.86 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AlgorandService = void 0; const algosdk_1 = __importDefault(require("algosdk")); const algokit_utils_1 = require("@algorandfoundation/algokit-utils"); const client_manager_1 = require("@algorandfoundation/algokit-utils/types/client-manager"); const apiHelpers_1 = require("../helpers/apiHelpers"); const utils_1 = require("../utils"); class AlgorandService { wallet; apiConfig; algodClient; algorandClient; apiService; network; constructor(wallet, apiConfig, sdkOptions) { this.wallet = wallet; this.apiConfig = apiConfig; this.network = sdkOptions?.sandbox ? "testnet" : "mainnet"; this.algodClient = client_manager_1.ClientManager.getAlgodClient(client_manager_1.ClientManager.getAlgoNodeConfig(this.network, "algod")); this.algorandClient = algokit_utils_1.AlgorandClient.fromClients({ algod: this.algodClient, }); this.apiService = new apiHelpers_1.ZebecCardAPIService(apiConfig, sdkOptions?.sandbox || false); } /** * Fetches a quote for Bitcoin transfer. * * @returns {Promise<Quote>} A promise that resolves to a Quote object. */ async fetchQuote(symbol = "ALGO") { const res = await this.apiService.fetchQuote(symbol); return res; } /** * Fetches the Bitcoin vault address. * * @returns {Promise<{ address: string }>} A promise that resolves to the vault address. */ async fetchVault(symbol = "ALGO") { const data = await this.apiService.fetchVault(symbol); return data; } /** * Transfer Algorand currency from one wallet to another * @param config Transfer configuration * @returns Transaction ID if successful */ async transferAlgo(config) { try { const parsedAmount = (0, utils_1.parseAlgo)(config.amount); // Check if sender has sufficient balance const senderBalance = await this.getAccountBalanceInMicroAlgo(this.wallet.address); const minBalance = (0, utils_1.parseAlgo)(0.1); // Minimum account balance if (senderBalance < parsedAmount + minBalance) { throw new Error(`Insufficient balance. Need ${(0, utils_1.formatAlgo)(parsedAmount + minBalance)} ALGO, have ${(0, utils_1.formatAlgo)(senderBalance)} ALGO`); } const vault = await this.fetchVault("ALGO"); const recipientAddress = vault.address; // Validate recipient address if (!algosdk_1.default.isValidAddress(recipientAddress)) { throw new Error("Invalid recipient address"); } // Get suggested transaction parameters const suggestedParams = await this.algodClient.getTransactionParams().do(); // Create payment transaction const paymentTxn = algosdk_1.default.makePaymentTxnWithSuggestedParamsFromObject({ sender: this.wallet.address, receiver: recipientAddress, amount: parsedAmount, note: config.note ? new Uint8Array(Buffer.from(config.note)) : undefined, suggestedParams: suggestedParams, }); // Sign the transaction const txId = await this.wallet.signAndSendTransaction(paymentTxn); return txId; } catch (error) { console.error("Transfer failed:", error); throw error; } } /** * Transfer USDC (ASA token) from wallet to vault * @param config USDC transfer configuration * @returns Transaction ID if successful */ async transferAsset(config) { try { const assetDecimals = await (0, utils_1.getAssetDecimals)(this.algodClient, config.assetId); // const usdcConfig = USDC_ASSET_CONFIG[this.network]; const parsedAmount = (0, utils_1.parseAlgorandAsset)(config.amount, assetDecimals); // Check if sender has sufficient USDC balance const senderAssetBalance = await this.getAssetBalanceInMicroUnit(this.wallet.address, config.assetId); if (senderAssetBalance < parsedAmount) { throw new Error(`Insufficient Asset balance. Need ${(0, utils_1.formatAlgorandAsset)(parsedAmount, assetDecimals)} Asset, have ${(0, utils_1.formatAlgorandAsset)(senderAssetBalance, assetDecimals)} Asset`); } // Check if sender has sufficient ALGO for transaction fees const senderAlgoBalance = await this.getAccountBalanceInMicroAlgo(this.wallet.address); const minAlgoForFees = (0, utils_1.parseAlgo)(0.002); // Minimum ALGO for transaction fees if (senderAlgoBalance < minAlgoForFees) { throw new Error(`Insufficient ALGO for transaction fees. Need at least ${(0, utils_1.formatAlgo)(minAlgoForFees)} ALGO for fees`); } const vault = await this.fetchVault("ALGO-USDC"); const recipientAddress = vault.address; // Validate recipient address if (!algosdk_1.default.isValidAddress(recipientAddress)) { throw new Error("Invalid recipient address"); } // // Check if recipient is opted into USDC asset const recipientOptedIn = await this.isOptedIntoAsset(recipientAddress, config.assetId); if (!recipientOptedIn) { throw new Error("Recipient address is not opted into USDC asset"); } // Get suggested transaction parameters const suggestedParams = await this.algodClient.getTransactionParams().do(); // Create asset transfer transaction const assetTransferTxn = algosdk_1.default.makeAssetTransferTxnWithSuggestedParamsFromObject({ sender: this.wallet.address, receiver: recipientAddress, amount: parsedAmount, assetIndex: config.assetId, note: config.note ? new Uint8Array(Buffer.from(config.note)) : undefined, suggestedParams: suggestedParams, }); // Sign and send the transaction const txId = await this.wallet.signAndSendTransaction(assetTransferTxn); return txId; } catch (error) { console.error("Asset transfer failed:", error); throw error; } } /** * Check if an account is opted into a specific asset * @param address Account address * @param assetId Asset ID to check * @returns Whether the account is opted into the asset */ async isOptedIntoAsset(address, assetId) { try { const accountInfo = await this.algodClient.accountInformation(address).do(); return accountInfo.assets?.some((asset) => asset.assetId === BigInt(assetId)) || false; } catch (error) { console.error("Error checking asset opt-in status:", error); return false; } } /** * Get asset balance for a specific account in microAsset (base units) * @param address Account address * @param assetId Asset ID * @returns Asset balance in base units */ async getAssetBalanceInMicroUnit(walletAddress, assetId) { try { const accountInfo = await this.algodClient.accountInformation(walletAddress).do(); const asset = accountInfo.assets?.find((asset) => asset.assetId === BigInt(assetId)); return asset ? BigInt(asset.amount) : BigInt(0); } catch (error) { console.error("Error fetching asset balance:", error); return BigInt(0); } } async getAssetBalance(walletAddress, assetId) { const balance = await this.getAssetBalanceInMicroUnit(walletAddress, assetId); const decimals = await (0, utils_1.getAssetDecimals)(this.algodClient, assetId); return (0, utils_1.formatAlgorandAsset)(balance, decimals); } async getAssetsBalance(walletAddress, assetIds) { const map = new Map(Array.from(assetIds.map((id) => [id, "0"]))); try { const accountInfo = await this.algodClient.accountInformation(walletAddress).do(); const assets = accountInfo.assets; if (!assets) { return map; } await Promise.all(assetIds.map(async (id) => { const asset = assets.find((asset) => asset.assetId === BigInt(id)); if (asset) { const decimals = await (0, utils_1.getAssetDecimals)(this.algodClient, id); const amount = (0, utils_1.formatAlgorandAsset)(asset.amount, decimals); map.set(id, amount); } })); return map; } catch (error) { console.error("Error fetching asset balance:", error); } return map; } /** * Get account balance in Algos * @param address Account address * @returns Balance in ALGO */ async getAccountBalance(address) { const amount = await this.getAccountBalanceInMicroAlgo(address); return (0, utils_1.formatAlgo)(amount); } /** * Get account balance in microAlgos (for internal calculations) * @param address Account address * @returns Balance in microAlgos */ async getAccountBalanceInMicroAlgo(address) { const accountInfo = await this.algodClient.accountInformation(address).do(); return accountInfo.amount; } } exports.AlgorandService = AlgorandService;