Update Salvium wallet RPC support
This commit is contained in:
+3
-2
@@ -1,6 +1,7 @@
|
||||
// .gitignore
|
||||
// config.php
|
||||
# Local configuration and secrets
|
||||
config.php
|
||||
.private/
|
||||
|
||||
*.log
|
||||
.DS_Store
|
||||
*.swp
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
// test_address_format.php
|
||||
|
||||
// Increase error reporting
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
// --- Autoload dependencies ---
|
||||
// This assumes the script is in the root directory of your project.
|
||||
require_once __DIR__ . '/src/salvium_tipbot_wallet.php';
|
||||
|
||||
// --- Load Configuration ---
|
||||
$configFile = __DIR__ . '/config.php';
|
||||
if (!file_exists($configFile)) {
|
||||
die("ERROR: config.php not found. Please copy config.sample.php to config.php and fill in your details.\n");
|
||||
}
|
||||
$config = require $configFile;
|
||||
echo "✅ Config loaded successfully.\n";
|
||||
|
||||
// --- Instantiate Wallet ---
|
||||
try {
|
||||
$wallet = new Salvium\SalviumWallet(
|
||||
$config['SALVIUM_RPC_HOST'],
|
||||
$config['SALVIUM_RPC_PORT'],
|
||||
$config['SALVIUM_RPC_USERNAME'],
|
||||
$config['SALVIUM_RPC_PASSWORD']
|
||||
);
|
||||
echo "✅ Wallet class instantiated.\n";
|
||||
} catch (Exception $e) {
|
||||
die("ERROR: Could not instantiate the wallet class: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
// --- Call getNewSubaddress ---
|
||||
echo "ℹ️ Calling getNewSubaddress()...\n";
|
||||
$newAddress = $wallet->getNewSubaddress();
|
||||
|
||||
if (!$newAddress) {
|
||||
die("❌ ERROR: The wallet RPC call failed. Check if your salvium-wallet-rpc is running and accessible.\n");
|
||||
}
|
||||
|
||||
echo "✅ Received address: " . $newAddress . "\n";
|
||||
|
||||
// --- Validate the Address Format ---
|
||||
$expectedPrefix = 'SC1';
|
||||
if (str_starts_with($newAddress, $expectedPrefix)) {
|
||||
echo "✅ SUCCESS: The new address starts with the correct 'SC1' prefix.\n";
|
||||
} else {
|
||||
echo "❌ FAILURE: The new address does not start with 'SC1'.\n";
|
||||
echo " - Expected prefix: " . $expectedPrefix . "\n";
|
||||
echo " - Actual prefix: " . substr($newAddress, 0, 3) . "\n";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// migrate_addresses.php
|
||||
|
||||
// --- Setup & Dependencies ---
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
require_once __DIR__ . '/src/salvium_tipbot_db.php';
|
||||
require_once __DIR__ . '/src/salvium_tipbot_wallet.php';
|
||||
|
||||
use Salvium\SalviumTipBotDB;
|
||||
use Salvium\SalviumWallet;
|
||||
|
||||
// --- Load Configuration ---
|
||||
$configFile = __DIR__ . '/config.php';
|
||||
if (!file_exists($configFile)) {
|
||||
die("❌ ERROR: config.php not found.\n");
|
||||
}
|
||||
$config = require $configFile;
|
||||
|
||||
// --- Confirmation Prompt ---
|
||||
echo "*****************************************************************\n";
|
||||
echo "WARNING: This script will generate and assign a new 'SC1'\n";
|
||||
echo " address for EVERY user in your database.\n";
|
||||
echo " This action is irreversible.\n";
|
||||
echo "*****************************************************************\n";
|
||||
echo "Are you sure you want to continue? (yes/no): ";
|
||||
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$line = trim(fgets($handle));
|
||||
fclose($handle);
|
||||
|
||||
if (strtolower($line) !== 'yes') {
|
||||
die("🛑 Migration cancelled by user.\n");
|
||||
}
|
||||
|
||||
echo "\n✅ Confirmation received. Starting migration...\n";
|
||||
|
||||
// --- Initialize Classes ---
|
||||
try {
|
||||
$db = new SalviumTipBotDB($config);
|
||||
$wallet = new SalviumWallet(
|
||||
$config['SALVIUM_RPC_HOST'],
|
||||
$config['SALVIUM_RPC_PORT'],
|
||||
$config['SALVIUM_RPC_USERNAME'],
|
||||
$config['SALVIUM_RPC_PASSWORD']
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
die("❌ ERROR: Failed to initialize classes: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
// --- Fetch All Users ---
|
||||
$users = $db->getAllUsers();
|
||||
if (empty($users)) {
|
||||
die("ℹ️ No users found in the database. Nothing to do.\n");
|
||||
}
|
||||
$totalUsers = count($users);
|
||||
echo "✅ Found {$totalUsers} users to update.\n\n";
|
||||
|
||||
// --- Migration Loop ---
|
||||
$successCount = 0;
|
||||
foreach ($users as $index => $user) {
|
||||
$userId = $user['id'];
|
||||
$oldAddress = $user['salvium_subaddress'];
|
||||
|
||||
echo "Updating user #{$userId} (" . ($index + 1) . "/{$totalUsers})... ";
|
||||
|
||||
// 1. Generate new address
|
||||
$newAddress = $wallet->getNewSubaddress();
|
||||
|
||||
if (!$newAddress || !str_starts_with($newAddress, 'SC1')) {
|
||||
echo "❌ FAILED: Could not generate a valid new address from the wallet RPC.\n";
|
||||
continue; // Skip to the next user
|
||||
}
|
||||
|
||||
// 2. Update the database
|
||||
if ($db->updateUserSubaddress($userId, $newAddress)) {
|
||||
echo "✅ SUCCESS: New address assigned.\n";
|
||||
// Optional: uncomment the line below for detailed logging
|
||||
// echo " - Old: {$oldAddress}\n - New: {$newAddress}\n";
|
||||
$successCount++;
|
||||
} else {
|
||||
echo "❌ FAILED: Database update failed for user #{$userId}.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// --- Final Report ---
|
||||
echo "\n============================================\n";
|
||||
echo "🎉 Migration Complete!\n";
|
||||
echo "Successfully updated: {$successCount} / {$totalUsers} users.\n";
|
||||
echo "============================================\n";
|
||||
|
||||
?>
|
||||
@@ -12,6 +12,8 @@ class SalviumTipBotCommands {
|
||||
'claim' => ['private'],
|
||||
'withdraw' => ['private'],
|
||||
'balance' => ['private'],
|
||||
'ubc' => ['private'],
|
||||
'txs' => ['private'],
|
||||
'tip' => ['private', 'group', 'supergroup'],
|
||||
];
|
||||
|
||||
@@ -78,6 +80,155 @@ class SalviumTipBotCommands {
|
||||
return $user ? "Your balance: {$user['tip_balance']} SAL" : "No account found. Use /deposit first.";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $accountIndex The account index to query (usually 0 for the primary account).
|
||||
* @return string
|
||||
*/
|
||||
private function cmd_ubc(array $args, array $ctx): string {
|
||||
$allowedUsers = [1319626133, 1835687993];
|
||||
|
||||
if (!in_array($ctx['user_id'], $allowedUsers)) {
|
||||
// Return nothing or a generic error to keep the command hidden
|
||||
return "⛔ Unauthorized access.";
|
||||
}
|
||||
|
||||
|
||||
// 1. Get the subaddress index for this user (Default to 0 if not set)
|
||||
$subaddrIndex = $user['subaddr_index'] ?? 0;
|
||||
|
||||
// 2. Call the RPC
|
||||
// We request only this specific subaddress to keep the response light
|
||||
$rpcData = $this->wallet->getBalance(0, [$subaddrIndex]);
|
||||
|
||||
// 3. Locate the correct data in the nested array
|
||||
$targetData = null;
|
||||
|
||||
// Check if 'balances' exists (Root level)
|
||||
if (isset($rpcData['balances']) && is_array($rpcData['balances'])) {
|
||||
|
||||
// Loop through assets to find 'SAL1'
|
||||
foreach ($rpcData['balances'] as $asset) {
|
||||
if (isset($asset['asset_type']) && $asset['asset_type'] === 'SAL1') {
|
||||
|
||||
// Now look for the specific subaddress inside this asset
|
||||
if (isset($asset['per_subaddress']) && is_array($asset['per_subaddress'])) {
|
||||
foreach ($asset['per_subaddress'] as $sub) {
|
||||
if ($sub['address_index'] == $subaddrIndex) {
|
||||
$targetData = $sub;
|
||||
break 2; // Break both loops
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$targetData) {
|
||||
return "Error: Could not retrieve wallet data from Daemon.";
|
||||
}
|
||||
|
||||
// 4. Extract and Format Data
|
||||
// Convert atomic units (integers) to readable strings
|
||||
$totalBalance = $this->atomicToHuman($targetData['balance']);
|
||||
$unlockedBalance = $this->atomicToHuman($targetData['unlocked_balance']);
|
||||
$blocksToUnlock = $targetData['blocks_to_unlock'];
|
||||
|
||||
// 5. Build the response string
|
||||
$output = "Wallet Status (RPC)\n";
|
||||
$output .= "--------------------------\n";
|
||||
$output .= "Total: {$totalBalance} SAL\n";
|
||||
$output .= "Unlocked: {$unlockedBalance} SAL\n";
|
||||
$output .= "Spendable: {$unlockedBalance} SAL\n";
|
||||
|
||||
if ($blocksToUnlock > 0) {
|
||||
$output .= "Pending: Balance unlocks in {$blocksToUnlock} block(s)";
|
||||
} else {
|
||||
$output .= "Status: Fully synced & unlocked";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function cmd_txs(array $args, array $ctx): string {
|
||||
$allowedUsers = [1319626133, 1835687993];
|
||||
|
||||
if (!in_array($ctx['user_id'], $allowedUsers)) {
|
||||
return "Unauthorized access.";
|
||||
}
|
||||
|
||||
// 1. Parse Arguments (Using indices 1 and 2 as requested)
|
||||
// Usage: /cmd 1=in/out 2=pending
|
||||
$direction = isset($args[1]) && strtolower($args[1]) === 'in' ? 'in' : 'out';
|
||||
$isPending = isset($args[2]) && strtolower($args[2]) === 'pending';
|
||||
|
||||
// 2. Get Data
|
||||
$currentHeight = $this->wallet->getHeight();
|
||||
$rpcData = $this->wallet->getTransfersAll($direction, $isPending);
|
||||
|
||||
if (empty($rpcData) || !is_array($rpcData)) {
|
||||
$status = $isPending ? "pending" : "confirmed";
|
||||
return "No $status '$direction' transactions found.";
|
||||
}
|
||||
|
||||
// 3. Slice and Sort
|
||||
// Reverse first so the newest transactions are at index 0
|
||||
// $rpcData = array_reverse($rpcData);
|
||||
$limit = 10;
|
||||
$recentTxs = array_slice($rpcData, 0, $limit);
|
||||
// $recentTxs = array_reverse($recentTxs);
|
||||
|
||||
// 4. DATABASE LOOKUP
|
||||
// Extract TxIDs and get usernames
|
||||
$txIds = array_column($recentTxs, 'txid');
|
||||
$txOwners = $this->db->getTxOwners($txIds);
|
||||
|
||||
// 5. Build Output
|
||||
$output = "Last $limit " . ucfirst($direction) . ($isPending ? " (Pending)" : "") . " Transfers\n";
|
||||
$output .= "---------------------------------\n";
|
||||
|
||||
foreach ($recentTxs as $tx) {
|
||||
$amount = $this->atomicToHuman($tx['amount']);
|
||||
$hashShort = $tx['txid']; // Full hash as requested
|
||||
$txHeight = $tx['height'];
|
||||
|
||||
// Calculate Confirmations
|
||||
if ($txHeight == 0) {
|
||||
$confText = "Pending";
|
||||
} else {
|
||||
$confs = ($currentHeight && $currentHeight >= $txHeight)
|
||||
? ($currentHeight - $txHeight) + 1
|
||||
: 1;
|
||||
$confText = "$confs confs";
|
||||
}
|
||||
|
||||
// CHECK: Do we have a username for this TxID?
|
||||
$userString = "";
|
||||
if (isset($txOwners[$tx['txid']])) {
|
||||
$username = $txOwners[$tx['txid']];
|
||||
$userString = " | User: @$username";
|
||||
}
|
||||
|
||||
// Output format: User string attached to TxID line
|
||||
$output .= "TxID: $hashShort$userString\n";
|
||||
$output .= "Amt: $amount SAL\n";
|
||||
$output .= "Blk: $txHeight ($confText)\n\n";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function atomicToHuman($atomic): string {
|
||||
if (!is_numeric($atomic) || $atomic < 0) return "0.00";
|
||||
|
||||
// Salvium uses 8 decimal places (10^8)
|
||||
$decimals = 8;
|
||||
$divisor = bcpow('10', (string)$decimals);
|
||||
|
||||
// Use bcdiv for precision with large numbers, setting the output precision to 8
|
||||
return bcdiv((string)$atomic, $divisor, $decimals);
|
||||
}
|
||||
|
||||
|
||||
private function cmd_withdraw(array $args, array $ctx): string {
|
||||
if (count($args) < 3) return "Usage: /withdraw <address> <amount>";
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ function sendGif(int $chatId, string $fileIdOrUrl, ?string $caption = null): voi
|
||||
|
||||
function isValidSalviumAddress(string $address): bool {
|
||||
// Accepts standard and subaddress prefixes for Salvium (e.g. SaLvd, SaLvs)
|
||||
return preg_match('/^SaLv[a-zA-Z0-9]{95}$/', $address) === 1;
|
||||
// return preg_match('/^SaLv[a-zA-Z0-9]{95}$/', $address) === 1;
|
||||
return preg_match('/^SC1[a-zA-Z0-9]{95,96}$/', $address) === 1;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -310,6 +310,30 @@ class SalviumTipBotDB {
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts an array of TxIDs and returns an associative array [txid => username]
|
||||
*/
|
||||
public function getTxOwners(array $txids): array {
|
||||
if (empty($txids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Create the correct number of placeholders (?) for the IN clause
|
||||
$placeholders = str_repeat('?,', count($txids) - 1) . '?';
|
||||
|
||||
// Efficient JOIN query to get usernames for these specific TxIDs
|
||||
$sql = "SELECT w.txid, u.username
|
||||
FROM withdrawals w
|
||||
JOIN users u ON w.user_id = u.id
|
||||
WHERE w.txid IN ($placeholders)";
|
||||
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($txids);
|
||||
|
||||
// FETCH_KEY_PAIR returns an array like: ['txid123' => 'user_name', 'txid456' => 'other_user']
|
||||
return $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
}
|
||||
|
||||
public function logMessage(int $chatId, string $chatName, string $username, string $message, string $response): void {
|
||||
try {
|
||||
$sql = "INSERT INTO bot_log (chat_id, chat_name, username, message, response) VALUES (:chat_id, :chat_name, :username, :message, :response)";
|
||||
@@ -326,5 +350,17 @@ class SalviumTipBotDB {
|
||||
}
|
||||
}
|
||||
|
||||
// Add this function inside the SalviumTipBotDB class in src/salvium_tipbot_db.php
|
||||
|
||||
public function getAllUsers(): array {
|
||||
$stmt = $this->pdo->query("SELECT id, telegram_user_id, salvium_subaddress FROM users ORDER BY id ASC");
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function updateUserSubaddress(int $userId, string $newSubaddress): bool {
|
||||
$stmt = $this->pdo->prepare("UPDATE users SET salvium_subaddress = ? WHERE id = ?");
|
||||
return $stmt->execute([$newSubaddress, $userId]);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -14,7 +14,7 @@ class SalviumWallet {
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
/*
|
||||
private function _callRpc(string $method, array $params = []): array|false {
|
||||
$url = "http://{$this->host}:{$this->port}/json_rpc";
|
||||
$request = json_encode([
|
||||
@@ -51,18 +51,83 @@ class SalviumWallet {
|
||||
$decoded = json_decode($response, true);
|
||||
return $decoded['result'] ?? false;
|
||||
}
|
||||
*/
|
||||
// In src/salvium_tipbot_wallet.php
|
||||
|
||||
// REPLACE the existing _callRpc function with this corrected version.
|
||||
private function _callRpc(string $method, array $params = []): array|false {
|
||||
$url = "http://{$this->host}:{$this->port}/json_rpc";
|
||||
$request = json_encode([
|
||||
'jsonrpc' => '2.0',
|
||||
'id' => '0',
|
||||
'method' => $method,
|
||||
'params' => $params
|
||||
]);
|
||||
|
||||
// Add 'Content-Type' and disable the 'Expect: 100-continue' header.
|
||||
// The empty 'Expect:' header is the crucial fix for compatibility with some RPC servers.
|
||||
$headers = [
|
||||
'Content-Type: application/json',
|
||||
'Expect:'
|
||||
];
|
||||
|
||||
$options = [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $request,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
];
|
||||
|
||||
if ($this->username && $this->password) {
|
||||
$options[CURLOPT_USERPWD] = "{$this->username}:{$this->password}";
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, $options);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
error_log('RPC Curl Error: ' . curl_error($ch));
|
||||
curl_close($ch);
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
$decoded = json_decode($response, true);
|
||||
return $decoded['result'] ?? false;
|
||||
}
|
||||
public function getWalletBalance(): array|false {
|
||||
return $this->_callRpc('get_balance');
|
||||
}
|
||||
|
||||
/*
|
||||
public function getNewSubaddress(int $accountIndex = 0, ?string $label = null): string|false {
|
||||
$params = ['account_index' => $accountIndex];
|
||||
if ($label) $params['label'] = $label;
|
||||
$result = $this->_callRpc('create_address', $params);
|
||||
return $result['address'] ?? false;
|
||||
}
|
||||
*/
|
||||
// Replace the old getNewSubaddress function with this one.
|
||||
public function getNewSubaddress(int $accountIndex = 0, ?string $label = null): string|false {
|
||||
// Step 1: Create the address in the wallet's memory.
|
||||
$params = ['account_index' => $accountIndex];
|
||||
if ($label) {
|
||||
$params['label'] = $label;
|
||||
}
|
||||
$result = $this->_callRpc('create_address', $params);
|
||||
|
||||
if (empty($result['address'])) {
|
||||
error_log("Failed to create new subaddress via RPC.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: Immediately save the wallet to commit the new address to disk.
|
||||
// This is the crucial step that ensures the wallet monitors the new address.
|
||||
$this->saveWallet();
|
||||
|
||||
return $result['address'];
|
||||
}
|
||||
public function getAddresses(): array|false {
|
||||
$result = $this->_callRpc('get_address');
|
||||
return $result['addresses'] ?? false;
|
||||
@@ -97,9 +162,73 @@ class SalviumWallet {
|
||||
'failed' => $failed
|
||||
];
|
||||
$result = $this->_callRpc('get_transfers', $params);
|
||||
|
||||
// Fix: If pending is requested, Monero often puts it in 'pending' key, not in 'in'/'out'
|
||||
if ($pending && !empty($result['pending'])) {
|
||||
return $result['pending'];
|
||||
}
|
||||
|
||||
return $result[$inOrOut] ?? false;
|
||||
}
|
||||
|
||||
public function getTransfersAll(string $inOrOut = 'in', bool $pending = false, bool $failed = false, ?int $accountIndex = null): array|false {
|
||||
// If specific account requested, just do the standard call
|
||||
if ($accountIndex !== null) {
|
||||
return $this->_fetchTransfersForAccount($accountIndex, $inOrOut, $pending, $failed);
|
||||
}
|
||||
|
||||
// Otherwise, we must fetch ALL accounts and merge them
|
||||
$allAccounts = $this->_callRpc('get_accounts');
|
||||
if (empty($allAccounts['subaddress_accounts'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mergedTransfers = [];
|
||||
|
||||
foreach ($allAccounts['subaddress_accounts'] as $account) {
|
||||
$idx = $account['account_index'];
|
||||
|
||||
// Get transfers for this specific account
|
||||
$transfers = $this->_fetchTransfersForAccount($idx, $inOrOut, $pending, $failed);
|
||||
|
||||
if ($transfers) {
|
||||
// Add the account index to each transfer so you know where it came from
|
||||
foreach ($transfers as &$tx) {
|
||||
$tx['account_index'] = $idx;
|
||||
}
|
||||
// Merge into main list
|
||||
$mergedTransfers = array_merge($mergedTransfers, $transfers);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by timestamp (descending) so they appear in order
|
||||
usort($mergedTransfers, function ($a, $b) {
|
||||
return $b['timestamp'] <=> $a['timestamp'];
|
||||
});
|
||||
|
||||
return $mergedTransfers;
|
||||
}
|
||||
|
||||
// Helper to keep the main logic clean
|
||||
private function _fetchTransfersForAccount(int $idx, string $inOrOut, bool $pending, bool $failed): array {
|
||||
$params = [
|
||||
$inOrOut => true,
|
||||
'pending' => $pending,
|
||||
'failed' => $failed,
|
||||
'account_index' => $idx // <--- The critical fix
|
||||
];
|
||||
|
||||
$result = $this->_callRpc('get_transfers', $params);
|
||||
|
||||
// Handle the specific way pending txs are returned
|
||||
if ($pending && !empty($result['pending'])) {
|
||||
return $result['pending'];
|
||||
}
|
||||
|
||||
return $result[$inOrOut] ?? [];
|
||||
}
|
||||
|
||||
|
||||
public function getPayments(string $paymentId): array|false {
|
||||
$result = $this->_callRpc('get_payments', ['payment_id' => $paymentId]);
|
||||
return $result['payments'] ?? false;
|
||||
@@ -109,5 +238,33 @@ class SalviumWallet {
|
||||
$result = $this->_callRpc('get_height');
|
||||
return $result['height'] ?? false;
|
||||
}
|
||||
// Add this new function inside the SalviumWallet class.
|
||||
public function saveWallet(): bool {
|
||||
$result = $this->_callRpc('store');
|
||||
// The 'store' method returns an empty result on success, so we check for 'false' on failure.
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
|
||||
public function getBalance(int $accountIndex = 0, array $subaddrIndices = []): array|false {
|
||||
$params = [
|
||||
'account_index' => $accountIndex,
|
||||
];
|
||||
|
||||
// If specific subaddresses are requested, add them to the params
|
||||
if (!empty($subaddrIndices)) {
|
||||
$params['address_indices'] = $subaddrIndices;
|
||||
}
|
||||
|
||||
// Note: Since your transfer function uses 'SAL1', if Salvium stores
|
||||
// assets separately, you might see them in a 'per_subaddress' array
|
||||
// or need to pass 'asset_type' => 'SAL1' depending on strictness.
|
||||
// However, standard RPC usually defaults to the main chain asset.
|
||||
|
||||
$result = $this->_callRpc('get_balance', $params);
|
||||
|
||||
return $result ?? false;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user