/ src / Wallet / Wallet.php
Wallet.php
  1  <?php
  2  
  3  namespace BitcoindRPC\Wallet;
  4  
  5  use BitcoindRPC\RpcClient;
  6  use BitcoindRPC\RpcResponse;
  7  
  8  class Wallet
  9  {
 10      private RpcClient $client;
 11  
 12      public function __construct(RpcClient $client)
 13      {
 14          $this->client = $client;
 15      }
 16  
 17      public function abandonTransaction(string $txid): WalletResult
 18      {
 19          $response = $this->client->call('abandontransaction', [$txid]);
 20  
 21          return new WalletResult($response);
 22      }
 23  
 24      public function abortRescan(): WalletResult
 25      {
 26          $response = $this->client->call('abortrescan');
 27  
 28          return new WalletResult($response);
 29      }
 30  
 31      public function addMultisigAddress(
 32          int $nrequired, 
 33          array $keys, 
 34          string $label = "", 
 35          string $addressType = ""
 36      ): WalletResult {
 37          $params = [$nrequired, $keys];
 38          if ($label !== "") $params[] = $label;
 39          if ($addressType !== "") $params[] = $addressType;
 40  
 41          $response = $this->client->call('addmultisigaddress', $params);
 42          return new WalletResult($response);
 43      }
 44  
 45      public function backupWallet(string $destination): WalletResult
 46      {
 47          $response = $this->client->call('backupwallet', [$destination]);
 48          return new WalletResult($response);
 49      }
 50  
 51      public function bumpFee(string $txid, array $options = []): WalletResult
 52      {
 53          $params = [$txid];
 54          if (!empty($options)) $params[] = $options;
 55  
 56          $response = $this->client->call('bumpfee', $params);
 57          return new WalletResult($response);
 58      }
 59  
 60      public function createWallet(
 61          string $walletName,
 62          bool $disablePrivateKeys = false,
 63          bool $blank = false,
 64          string $passphrase = "",
 65          bool $avoidReuse = false,
 66          bool $descriptors = false,
 67          bool $loadOnStartup = true
 68      ): WalletResult {
 69          $params = [$walletName, $disablePrivateKeys, $blank, $passphrase, $avoidReuse];
 70          if ($descriptors) $params[] = $descriptors;
 71          $params[] = $loadOnStartup;
 72  
 73          $response = $this->client->call('createwallet', $params);
 74          return new WalletResult($response);
 75      }
 76  
 77      public function dumpPrivKey(string $address): WalletResult
 78      {
 79          $response = $this->client->call('dumpprivkey', [$address]);
 80          return new WalletResult($response);
 81      }
 82  
 83      public function dumpWallet(string $filename): WalletResult
 84      {
 85          $response = $this->client->call('dumpwallet', [$filename]);
 86          return new WalletResult($response);
 87      }
 88  
 89      public function encryptWallet(string $passphrase): WalletResult
 90      {
 91          $response = $this->client->call('encryptwallet', [$passphrase]);
 92          return new WalletResult($response);
 93      }
 94  
 95      public function getAddressesByLabel(string $label): WalletResult
 96      {
 97          $response = $this->client->call('getaddressesbylabel', [$label]);
 98          return new WalletResult($response);
 99      }
100  
101      public function getAddressInfo(string $address): WalletResult
102      {
103          $response = $this->client->call('getaddressinfo', [$address]);
104          return new WalletResult($response);
105      }
106  
107      public function getBalance(
108          string $dummy = "*",
109          int $minconf = 0,
110          bool $includeWatchonly = false,
111          bool $avoidReuse = true
112      ): WalletResult {
113          $response = $this->client->call('getbalance', [$dummy, $minconf, $includeWatchonly, $avoidReuse]);
114          return new WalletResult($response);
115      }
116  
117      public function getBalances(): WalletResult
118      {
119          $response = $this->client->call('getbalances');
120          return new WalletResult($response);
121      }
122  
123      public function getNewAddress(string $label = "", string $addressType = ""): WalletResult
124      {
125          $params = [];
126          if ($label !== "") $params[] = $label;
127          if ($addressType !== "") $params[] = $addressType;
128  
129          $response = $this->client->call('getnewaddress', $params);
130          return new WalletResult($response);
131      }
132  
133      public function getRawChangeAddress(string $addressType = ""): WalletResult
134      {
135          $params = [];
136          if ($addressType !== "") $params[] = $addressType;
137  
138          $response = $this->client->call('getrawchangeaddress', $params);
139          return new WalletResult($response);
140      }
141  
142      public function getReceivedByAddress(string $address, int $minconf = 1): WalletResult
143      {
144          $response = $this->client->call('getreceivedbyaddress', [$address, $minconf]);
145          return new WalletResult($response);
146      }
147  
148      public function getReceivedByLabel(string $label, int $minconf = 1): WalletResult
149      {
150          $response = $this->client->call('getreceivedbylabel', [$label, $minconf]);
151          return new WalletResult($response);
152      }
153  
154      public function getTransaction(
155          string $txid, 
156          bool $includeWatchonly = false, 
157          bool $verbose = true
158      ): WalletResult {
159          $response = $this->client->call('gettransaction', [$txid, $includeWatchonly, $verbose]);
160          return new WalletResult($response);
161      }
162  
163      public function getUnconfirmedBalance(): WalletResult
164      {
165          $response = $this->client->call('getunconfirmedbalance');
166          return new WalletResult($response);
167      }
168  
169      public function getWalletInfo(): WalletResult
170      {
171          $response = $this->client->call('getwalletinfo');
172          return new WalletResult($response);
173      }
174  
175      public function importAddress(
176          string $address, 
177          string $label = "", 
178          bool $rescan = true, 
179          bool $p2sh = false
180      ): WalletResult {
181          $params = [$address, $label, $rescan];
182          if ($p2sh) $params[] = $p2sh;
183  
184          $response = $this->client->call('importaddress', $params);
185          return new WalletResult($response);
186      }
187  
188      public function importDescriptors(array $requests): WalletResult
189      {
190          $response = $this->client->call('importdescriptors', [$requests]);
191          return new WalletResult($response);
192      }
193  
194      public function importMulti(array $requests, array $options = []): WalletResult
195      {
196          $params = [$requests];
197          if (!empty($options)) $params[] = $options;
198  
199          $response = $this->client->call('importmulti', $params);
200          return new WalletResult($response);
201      }
202  
203      public function importPrivKey(string $privkey, string $label = "", bool $rescan = true): WalletResult
204      {
205          $response = $this->client->call('importprivkey', [$privkey, $label, $rescan]);
206          return new WalletResult($response);
207      }
208  
209      public function importPrunedFunds(string $rawtransaction, string $txoutproof): WalletResult
210      {
211          $response = $this->client->call('importprunedfunds', [$rawtransaction, $txoutproof]);
212          return new WalletResult($response);
213      }
214  
215      public function importPubkey(string $pubkey, string $label = "", bool $rescan = true): WalletResult
216      {
217          $response = $this->client->call('importpubkey', [$pubkey, $label, $rescan]);
218          return new WalletResult($response);
219      }
220  
221      public function importWallet(string $filename): WalletResult
222      {
223          $response = $this->client->call('importwallet', [$filename]);
224          return new WalletResult($response);
225      }
226  
227      public function keyPoolRefill(int $newsize = 100): WalletResult
228      {
229          $response = $this->client->call('keypoolrefill', [$newsize]);
230          return new WalletResult($response);
231      }
232  
233      public function listAddressGroupings(): WalletResult
234      {
235          $response = $this->client->call('listaddressgroupings');
236          return new WalletResult($response);
237      }
238  
239      public function listLabels(string $purpose = ""): WalletResult
240      {
241          $params = [];
242          if ($purpose !== "") $params[] = $purpose;
243  
244          $response = $this->client->call('listlabels', $params);
245          return new WalletResult($response);
246      }
247  
248      public function listLockUnspent(): WalletResult
249      {
250          $response = $this->client->call('listlockunspent');
251          return new WalletResult($response);
252      }
253  
254      public function listReceivedByAddress(
255          int $minconf = 1,
256          bool $includeEmpty = false,
257          bool $includeWatchonly = false,
258          string $addressFilter = ""
259      ): WalletResult {
260          $params = [$minconf, $includeEmpty, $includeWatchonly];
261          if ($addressFilter !== "") $params[] = $addressFilter;
262  
263          $response = $this->client->call('listreceivedbyaddress', $params);
264          return new WalletResult($response);
265      }
266  
267      public function listReceivedByLabel(
268          int $minconf = 1,
269          bool $includeEmpty = false,
270          bool $includeWatchonly = false
271      ): WalletResult {
272          $response = $this->client->call('listreceivedbylabel', [$minconf, $includeEmpty, $includeWatchonly]);
273          return new WalletResult($response);
274      }
275  
276      public function listSinceBlock(
277          string $blockhash = "",
278          int $targetConfirmations = 1,
279          bool $includeWatchonly = false,
280          bool $includeRemoved = false
281      ): WalletResult {
282          $response = $this->client->call('listsinceblock', [
283              $blockhash, $targetConfirmations, $includeWatchonly, $includeRemoved
284          ]);
285          return new WalletResult($response);
286      }
287  
288      public function listTransactions(
289          string $label = "*",
290          int $count = 10,
291          int $skip = 0,
292          bool $includeWatchonly = false
293      ): WalletResult {
294          $response = $this->client->call('listtransactions', [$label, $count, $skip, $includeWatchonly]);
295          return new WalletResult($response);
296      }
297  
298      public function listUnspent(
299          int $minconf = 1,
300          int $maxconf = 9999999,
301          array $addresses = [],
302          bool $includeUnsafe = true,
303          array $queryOptions = []
304      ): WalletResult {
305          $params = [$minconf, $maxconf];
306          if (!empty($addresses)) $params[] = $addresses;
307          $params[] = $includeUnsafe;
308          if (!empty($queryOptions)) $params[] = $queryOptions;
309  
310          $response = $this->client->call('listunspent', $params);
311          return new WalletResult($response);
312      }
313  
314      public function listWalletDir(): WalletResult
315      {
316          $response = $this->client->call('listwalletdir');
317          return new WalletResult($response);
318      }
319  
320      public function listWallets(): WalletResult
321      {
322          $response = $this->client->call('listwallets');
323          return new WalletResult($response);
324      }
325  
326      public function loadWallet(string $filename): WalletResult
327      {
328          $response = $this->client->call('loadwallet', [$filename]);
329          return new WalletResult($response);
330      }
331  
332      public function lockUnspent(bool $unlock, array $transactions = []): WalletResult
333      {
334          $params = [$unlock];
335          if (!empty($transactions)) $params[] = $transactions;
336  
337          $response = $this->client->call('lockunspent', $params);
338          return new WalletResult($response);
339      }
340  
341      public function psbtBumpFee(string $txid, array $options = []): WalletResult
342      {
343          $params = [$txid];
344          if (!empty($options)) $params[] = $options;
345  
346          $response = $this->client->call('psbtbumpfee', $params);
347          return new WalletResult($response);
348      }
349  
350      public function removePrunedFunds(string $txid): WalletResult
351      {
352          $response = $this->client->call('removeprunedfunds', [$txid]);
353          return new WalletResult($response);
354      }
355  
356      public function rescanBlockchain(int $startHeight = 0, ?int $stopHeight = null): WalletResult
357      {
358          $params = [$startHeight];
359          if ($stopHeight !== null) $params[] = $stopHeight;
360  
361          $response = $this->client->call('rescanblockchain', $params);
362          return new WalletResult($response);
363      }
364  
365      public function send(array $outputs, int $confTarget = 6, string $estimateMode = "UNSET", ?float $feeRate = null): WalletResult
366      {
367          $params = [$outputs, $confTarget, $estimateMode];
368          if ($feeRate !== null) $params[] = $feeRate;
369  
370          $response = $this->client->call('send', $params);
371          return new WalletResult($response);
372      }
373  
374      public function sendMany(
375          array $outputs,
376          int $minconf = 1,
377          string $comment = "",
378          array $subtractFeeFrom = [],
379          bool $replaceable = false,
380          int $confTarget = 6,
381          string $estimateMode = "UNSET",
382          ?float $feeRate = null
383      ): WalletResult {
384          $params = [$outputs, $minconf, $comment];
385          if (!empty($subtractFeeFrom)) $params[] = $subtractFeeFrom;
386          $params[] = $replaceable;
387          $params[] = $confTarget;
388          $params[] = $estimateMode;
389          if ($feeRate !== null) $params[] = $feeRate;
390  
391          $response = $this->client->call('sendmany', $params);
392          return new WalletResult($response);
393      }
394  
395      public function sendToAddress(
396          string $address,
397          float $amount,
398          string $comment = "",
399          string $commentTo = "",
400          bool $subtractFeeFromAmount = false,
401          bool $replaceable = false,
402          int $confTarget = 6,
403          string $estimateMode = "UNSET",
404          ?float $feeRate = null
405      ): WalletResult {
406          $params = [
407              $address, $amount, $comment, $commentTo, 
408              $subtractFeeFromAmount, $replaceable, $confTarget, $estimateMode
409          ];
410          if ($feeRate !== null) $params[] = $feeRate;
411  
412          $response = $this->client->call('sendtoaddress', $params);
413          return new WalletResult($response);
414      }
415  
416      public function setHdSeed(bool $newkeypool = true, string $seed = ""): WalletResult
417      {
418          $params = [$newkeypool];
419          if ($seed !== "") $params[] = $seed;
420  
421          $response = $this->client->call('sethdseed', $params);
422          return new WalletResult($response);
423      }
424  
425      public function setLabel(string $address, string $label): WalletResult
426      {
427          $response = $this->client->call('setlabel', [$address, $label]);
428          return new WalletResult($response);
429      }
430  
431      public function setTxFee(float $amount): WalletResult
432      {
433          $response = $this->client->call('settxfee', [$amount]);
434          return new WalletResult($response);
435      }
436  
437      public function setWalletFlag(string $flag, bool $value): WalletResult
438      {
439          $response = $this->client->call('setwalletflag', [$flag, $value]);
440          return new WalletResult($response);
441      }
442  
443      public function signMessage(string $address, string $message): WalletResult
444      {
445          $response = $this->client->call('signmessage', [$address, $message]);
446          return new WalletResult($response);
447      }
448  
449      public function signRawTransactionWithWallet(
450          string $hexstring,
451          array $prevtxs = [],
452          string $sighashtype = "ALL"
453      ): WalletResult {
454          $params = [$hexstring];
455          if (!empty($prevtxs)) $params[] = $prevtxs;
456          $params[] = $sighashtype;
457  
458          $response = $this->client->call('signrawtransactionwithwallet', $params);
459          return new WalletResult($response);
460      }
461  
462      public function unloadWallet(string $walletName = "", ?bool $loadOnStartup = null): WalletResult
463      {
464          $params = [];
465          if ($walletName !== "") $params[] = $walletName;
466          if ($loadOnStartup !== null) $params[] = $loadOnStartup;
467  
468          $response = $this->client->call('unloadwallet', $params);
469          return new WalletResult($response);
470      }
471  
472      public function upgradeWallet(?int $version = null): WalletResult
473      {
474          $params = [];
475          if ($version !== null) $params[] = $version;
476  
477          $response = $this->client->call('upgradewallet', $params);
478          return new WalletResult($response);
479      }
480  
481      public function walletCreateFundedPsbt(
482          array $inputs,
483          array $outputs,
484          int $locktime = 0,
485          array $options = [],
486          bool $bip32derivs = false
487      ): WalletResult {
488          $params = [$inputs, $outputs, $locktime];
489          if (!empty($options)) $params[] = $options;
490          $params[] = $bip32derivs;
491  
492          $response = $this->client->call('walletcreatefundedpsbt', $params);
493          return new WalletResult($response);
494      }
495  
496      public function walletLock(): WalletResult
497      {
498          $response = $this->client->call('walletlock');
499          return new WalletResult($response);
500      }
501  
502      public function walletPassphrase(string $passphrase, int $timeout): WalletResult
503      {
504          $response = $this->client->call('walletpassphrase', [$passphrase, $timeout]);
505          return new WalletResult($response);
506      }
507  
508      public function walletPassphraseChange(string $oldpassphrase, string $newpassphrase): WalletResult
509      {
510          $response = $this->client->call('walletpassphrasechange', [$oldpassphrase, $newpassphrase]);
511          return new WalletResult($response);
512      }
513  
514      public function walletProcessPsbt(
515          string $psbt,
516          bool $sign = true,
517          string $sighashtype = "ALL",
518          bool $bip32derivs = false
519      ): WalletResult {
520          $response = $this->client->call('walletprocesspsbt', [$psbt, $sign, $sighashtype, $bip32derivs]);
521          return new WalletResult($response);
522      }
523  
524      // Utility methods
525      public function getDetailedBalance(): array
526      {
527          $result = $this->getBalances();
528          if (!$result->isSuccess()) {
529              return [];
530          }
531  
532          $data = $result->getData();
533          $mine = $data['mine'] ?? [];
534          $watchonly = $data['watchonly'] ?? [];
535  
536          return [
537              'total' => ($mine['trusted'] ?? 0) + ($mine['untrusted_pending'] ?? 0) + ($mine['immature'] ?? 0),
538              'confirmed' => $mine['trusted'] ?? 0,
539              'unconfirmed' => $mine['untrusted_pending'] ?? 0,
540              'immature' => $mine['immature'] ?? 0,
541              'watchonly' => ($watchonly['trusted'] ?? 0) + ($watchonly['untrusted_pending'] ?? 0) + ($watchonly['immature'] ?? 0),
542          ];
543      }
544  
545      public function createReceivingAddress(string $label = "", string $addressType = "bech32"): ?string
546      {
547          $result = $this->getNewAddress($label, $addressType);
548          return $result->isSuccess() ? $result->getData() : null;
549      }
550  
551      public function sendPayment(
552          string $toAddress,
553          float $amount,
554          string $description = "",
555          bool $subtractFee = false
556      ): ?string {
557          $result = $this->sendToAddress(
558              address: $toAddress,
559              amount: $amount,
560              comment: $description,
561              subtractFeeFromAmount: $subtractFee
562          );
563          return $result->isSuccess() ? $result->getData() : null;
564      }
565  }