/**
  * this test requires / asumes that the test wallet it uses contains a balance
  *
  * we keep the wallet topped off with some coins,
  * but if some funny guy ever empties it or if you use your own API key to run the test then it needs to be topped off again
  *
  * @throws \Exception
  */
 public function testWalletTransaction()
 {
     $client = $this->setupBlocktrailSDK();
     /** @var Wallet $wallet */
     $wallet = $client->initWallet(["identifier" => "unittest-transaction", "passphrase" => "password"]);
     $this->assertEquals("give pause forget seed dance crawl situate hole keen", $wallet->getPrimaryMnemonic());
     $this->assertEquals("unittest-transaction", $wallet->getIdentifier());
     $this->assertEquals("M/9999'", $wallet->getBlocktrailPublicKeys()[9999][1]);
     $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKeys()[9999][0]);
     $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->key());
     $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key());
     list($confirmed, $unconfirmed) = $wallet->getBalance();
     $this->assertGreaterThan(0, $confirmed + $unconfirmed, "positive unconfirmed balance");
     $this->assertGreaterThan(0, $confirmed, "positive confirmed balance");
     list($path, $address) = $wallet->getNewAddressPair();
     $this->assertTrue(strpos($path, "M/9999'/0/") === 0);
     $this->assertTrue(BitcoinLib::validate_address($address, false, null));
     ///*
     $value = BlocktrailSDK::toSatoshi(0.0002);
     $txHash = $wallet->pay([$address => $value], null, false, true, Wallet::FEE_STRATEGY_BASE_FEE);
     $this->assertTrue(!!$txHash);
     sleep(1);
     // sleep to wait for the TX to be processed
     try {
         $tx = $client->transaction($txHash);
     } catch (ObjectNotFound $e) {
         $this->fail("404 for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     }
     $this->assertTrue(!!$tx, "check for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     $this->assertEquals($txHash, $tx['hash']);
     $this->assertEquals(BlocktrailSDK::toSatoshi(0.0001), $tx['total_fee']);
     $this->assertTrue(count($tx['outputs']) <= 2);
     $this->assertTrue(in_array($value, array_column($tx['outputs'], 'value')));
     //*/
     /*
      * do another TX but with a LOW_PRIORITY_FEE
      */
     $value = BlocktrailSDK::toSatoshi(0.0001);
     $txHash = $wallet->pay([$address => $value], null, false, true, Wallet::FEE_STRATEGY_LOW_PRIORITY);
     $this->assertTrue(!!$txHash);
     sleep(1);
     // sleep to wait for the TX to be processed
     try {
         $tx = $client->transaction($txHash);
     } catch (ObjectNotFound $e) {
         $this->fail("404 for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     }
     $this->assertTrue(!!$tx, "check for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     $this->assertEquals($txHash, $tx['hash']);
     $this->assertTrue(count($tx['outputs']) <= 2);
     $this->assertTrue(in_array($value, array_column($tx['outputs'], 'value')));
     /*
      * do another TX but with a custom - high - fee
      */
     $value = BlocktrailSDK::toSatoshi(0.0001);
     $forceFee = BlocktrailSDK::toSatoshi(0.001);
     $txHash = $wallet->pay([$address => $value], null, false, true, Wallet::FEE_STRATEGY_BASE_FEE, $forceFee);
     $this->assertTrue(!!$txHash);
     sleep(1);
     // sleep to wait for the TX to be processed
     try {
         $tx = $client->transaction($txHash);
     } catch (ObjectNotFound $e) {
         $this->fail("404 for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     }
     $this->assertTrue(!!$tx, "check for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     $this->assertEquals($txHash, $tx['hash']);
     $this->assertEquals($forceFee, $tx['total_fee']);
     $this->assertTrue(count($tx['outputs']) <= 2);
     $this->assertTrue(in_array($value, array_column($tx['outputs'], 'value')));
     /*
      * do another TX with OP_RETURN using TxBuilder
      */
     $value = BlocktrailSDK::toSatoshi(0.0002);
     $moon = "MOOOOOOOOOOOOON!";
     $txBuilder = new TransactionBuilder();
     $txBuilder->randomizeChangeOutput(false);
     $txBuilder->addRecipient($address, $value);
     $txBuilder->addOpReturn($moon);
     $txBuilder = $wallet->coinSelectionForTxBuilder($txBuilder);
     $txHash = $wallet->sendTx($txBuilder);
     $this->assertTrue(!!$txHash);
     sleep(1);
     // sleep to wait for the TX to be processed
     try {
         $tx = $client->transaction($txHash);
     } catch (ObjectNotFound $e) {
         $this->fail("404 for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     }
     $this->assertTrue(!!$tx, "check for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]");
     $this->assertEquals($txHash, $tx['hash']);
     $this->assertTrue(count($tx['outputs']) <= 3);
     $this->assertEquals($value, $tx['outputs'][0]['value']);
     $this->assertEquals(0, $tx['outputs'][1]['value']);
     $this->assertEquals("6a" . "10" . bin2hex($moon), $tx['outputs'][1]['script_hex']);
 }
// uncomment to create a couple of 0.001 BTC UTXOs (and 1 big output remains)
//  after doing this run the example twice and the 2nd time around you can see it only using a single UTXO
//var_dump($wallet->pay([$paymentAddress => $wallet->getMaxSpendable()['max']]));
//var_dump($wallet->pay([
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//    $wallet->getNewAddress() => $amount,
//]));
//exit(1);
$optimalFeePerKB = $wallet->getOptimalFeePerKB();
$lowPriorityFeePerKB = $wallet->getLowPriorityFeePerKB();
$txBuilder = new TransactionBuilder();
$txBuilder->addRecipient($paymentAddress, $amount);
// get coinselection for the payment we want to make
$txBuilder = $wallet->coinSelectionForTxBuilder($txBuilder);
// debug info
echo "--------------------------------------------\n";
var_dump("utxos", $txBuilder->getUtxos());
echo "--------------------------------------------\n";
var_dump("outputs", $txBuilder->getOutputs());
list($fee, $change) = $wallet->determineFeeAndChange($txBuilder, $optimalFeePerKB, $lowPriorityFeePerKB);
var_dump("fee", $fee);
var_dump("change", $change);
// set the fee and update the output value
$txBuilder->setFee($fee);
$txBuilder->updateOutputValue(0, $amount - $fee);
// EXTRA: remove any UTXOs we don't need to minize the fee of the TX
use Blocktrail\SDK\Blocktrail;
use Blocktrail\SDK\BlocktrailSDK;
use Blocktrail\SDK\Connection\Exceptions\ObjectNotFound;
use Blocktrail\SDK\TransactionBuilder;
use Blocktrail\SDK\Wallet;
use Blocktrail\SDK\WalletInterface;
require_once __DIR__ . "/../vendor/autoload.php";
$client = new BlocktrailSDK("MY_APIKEY", "MY_APISECRET", "BTC", true, 'v1');
// $client->setVerboseErrors();
// $client->setCurlDebugging();
/**
 * @var $wallet             \Blocktrail\SDK\WalletInterface
 * @var $backupMnemonic     string
 */
try {
    /** @var Wallet $wallet */
    $wallet = $client->initWallet(["identifier" => "example-wallet", "passphrase" => "example-strong-password"]);
} catch (ObjectNotFound $e) {
    /** @var Wallet $wallet */
    list($wallet, $primaryMnemonic, $backupMnemonic, $blocktrailPublicKeys) = $client->createNewWallet(["identifier" => "example-wallet", "passphrase" => "example-strong-password", "key_index" => 9999]);
    $wallet->doDiscovery();
}
// var_dump($wallet->deleteWebhook());
// var_dump($wallet->setupWebhook("http://www.example.com/wallet/webhook/example-wallet"));
// $utxos = $wallet->utxos()['data'];
// $utxo = $utxos[array_rand($utxos)];
$txBuilder = new TransactionBuilder();
$txBuilder->addRecipient($wallet->getNewAddress(), Blocktrail::DUST + 1);
$txBuilder->addOpReturn("TO DAAAA MOOOON@!的");
$txBuilder = $wallet->coinSelectionForTxBuilder($txBuilder);
echo $wallet->sendTx($txBuilder);
 public function determineFeeAndChange(TransactionBuilder $txBuilder)
 {
     $send = $txBuilder->getOutputs();
     $utxos = $txBuilder->getUtxos();
     $fee = $txBuilder->getFee();
     $change = null;
     // if the fee is fixed we just need to calculate the change
     if ($fee !== null) {
         $change = $this->determineChange($utxos, $send, $fee);
         // if change is not dust we need to add a change output
         if ($change > Blocktrail::DUST) {
             $send[] = ['address' => 'change', 'value' => $change];
         } else {
             // if change is dust we do nothing (implicitly it's added to the fee)
             $change = 0;
         }
     } else {
         $fee = $this->determineFee($utxos, $send);
         $change = $this->determineChange($utxos, $send, $fee);
         if ($change > 0) {
             $changeIdx = count($send);
             // set dummy change output
             $send[$changeIdx] = ['address' => 'change', 'value' => $change];
             // recaculate fee now that we know that we have a change output
             $fee2 = $this->determineFee($utxos, $send);
             // unset dummy change output
             unset($send[$changeIdx]);
             // if adding the change output made the fee bump up and the change is smaller than the fee
             //  then we're not doing change
             if ($fee2 > $fee && $fee2 > $change) {
                 $change = 0;
             } else {
                 $change = $this->determineChange($utxos, $send, $fee2);
                 // if change is not dust we need to add a change output
                 if ($change > Blocktrail::DUST) {
                     $send[$changeIdx] = ['address' => 'change', 'value' => $change];
                 } else {
                     // if change is dust we do nothing (implicitly it's added to the fee)
                     $change = 0;
                 }
             }
         }
     }
     $fee = $this->determineFee($utxos, $send);
     return [$fee, $change];
 }
<?php

use Blocktrail\SDK\BlocktrailSDK;
use Blocktrail\SDK\Connection\Exceptions\ObjectNotFound;
use Blocktrail\SDK\TransactionBuilder;
use Blocktrail\SDK\Wallet;
use Blocktrail\SDK\WalletInterface;
require_once __DIR__ . "/../vendor/autoload.php";
$client = new BlocktrailSDK("MY_APIKEY", "MY_APISECRET", "BTC", true, 'v1');
// $client->setVerboseErrors();
// $client->setCurlDebugging();
/**
 * @var $wallet             \Blocktrail\SDK\WalletInterface
 * @var $backupMnemonic     string
 */
try {
    /** @var Wallet $wallet */
    $wallet = $client->initWallet(["identifier" => "example-wallet", "passphrase" => "example-strong-password"]);
} catch (ObjectNotFound $e) {
    list($wallet, $primaryMnemonic, $backupMnemonic, $blocktrailPublicKeys) = $client->createNewWallet(["identifier" => "example-wallet", "passphrase" => "example-strong-password", "key_index" => 9999]);
    $wallet->doDiscovery();
}
// var_dump($wallet->deleteWebhook());
// var_dump($wallet->setupWebhook("http://www.example.com/wallet/webhook/example-wallet"));
$utxos = $wallet->utxos()['data'];
$utxo = $utxos[array_rand($utxos)];
$txBuilder = new TransactionBuilder();
var_dump($utxo['hash'], $utxo['idx'], $utxo['value'], $utxo['address'], $utxo['scriptpubkey_hex'], $utxo['path'], $utxo['redeem_script']);
$txBuilder->spendOutput($utxo['hash'], $utxo['idx']);
$txBuilder->addRecipient($wallet->getNewAddress(), $utxo['value'] / 2);
var_dump($wallet->sendTx($txBuilder));