Skip to content

Instantly share code, notes, and snippets.

@ItsShadowl
Forked from rubensayshi/forward.php
Created October 19, 2016 10:11
Show Gist options
  • Save ItsShadowl/c250ee540316bc4d306fdb0135ccf447 to your computer and use it in GitHub Desktop.
Save ItsShadowl/c250ee540316bc4d306fdb0135ccf447 to your computer and use it in GitHub Desktop.
<?php
/**
* example script to receive webhook events for a wallet and forward the bitcoin to another address (by the mapping)
*/
use BitWasp\BitcoinLib\RawTransaction;
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";
/**
* easy debugging helper, both var_dump and error_log for w/e you are
* @param $m
*/
function debug($m) {
var_dump($m);
error_log(json_encode($m));
};
// address map of old -> new addresses
// you could read JSON file
// $addressMap = json_decode(file_get_contents(__DIR__ . "address_map.json"), true);
$addressMap = [
// "old" => "new",
"2NE51wGFR4dQYWAG6jUYTtnsAyDeBv3jUyV" => "2MvusSia19Yk19Rw8PjGVjj1TjvxcP1eT23", // example of my own wallet
];
// init SDK with API key + secret
$testnet = true;
$client = new BlocktrailSDK("MY_APIKEY", "MY_APISECRET", "BTC", $testnet);
// wallet identifier and password for a little down the line
$walletIdentifier = "testyrgridwallet";
$walletPassword = "testyrgridwallet";
// read POST JSON
$json = file_get_contents("php://input");
$data = json_decode($json, true);
// sanity check
if (!isset($data['network']) || !isset($data['event_type'])) {
throw new \Exception("Invalid request");
}
// sanity check
if (($testnet && $data['network'] !== 'tBTC') || (!$testnet && $data['network'] !== 'BTC')) {
throw new \Exception("Invalid network");
}
// sanity check
if ($data['event_type'] != "address-transactions") {
debug("event we don't care about, ignore!");
exit(0);
}
$transaction = $data['data'];
$confirmations = $transaction['confirmations'];
$walletInfo = $data['wallet'];
$addressInfo = $data['addresses'];
// we should always have a webhook call for either unconfirmed or fist confirmed, ignore others
// it wouldn't hurt to replace this with something that keeps track if the transaction was already processed
// because then we don't have to try and spend it (blindly), see also the trycatch around the sending of the TX
if ($confirmations > 1) {
debug("> 1 confirmation, ignore!");
exit(0);
}
// ignore outgoign txs
if ($walletInfo['balance'] <= 0) {
debug("Outgoing transaction, ignore!");
exit(0);
}
if (count($walletInfo['addresses']) > 0) {
throw new \Exception("This is very weird, incoming transaction with multiple addresses owned by this wallet [{$transaction['hash']}]");
}
// uncommit for easy debugging info
// debug(json_encode($data));
// loop over all addresses that are part of the wallet
foreach ($walletInfo['addresses'] as $address) {
// check if address received BTC (balance change is positive)
$sum = $addressInfo[$address];
if ($sum <= Blocktrail::BASE_FEE) {
throw new \Exception("TX that isn't work forwarding because it's less than the fee [{$transaction['hash']}]");
}
if ($sum > 0) {
// check if we know a mapping for this address
if (!isset($addressMap[$address])) {
throw new \Exception("Address not in mapping [{$address}] for TX [{$transaction['hash']}]");
}
// new address
$newAddress = $addressMap[$address];
// filter out the outputs that are relevant (we don't care about the change)
$outputs = array_filter($transaction['outputs'], function($output) use($address) { return $output['address'] == $address; });
// sanity check
if (count($outputs) == 0) {
throw new \Exception("Impossible");
}
// make a TxBuilder so we can construct a custom TX
$txBuilder = new TransactionBuilder();
// use the outputs of the TX as the inputs for the new TX
foreach ($outputs as $output) {
$txBuilder->spendOutput($transaction['hash'], $output['index'], $output['value'], $output['address']);
}
// use fixed BASE_FEE
$fee = Blocktrail::BASE_FEE;
// send total minus fee to new address
// alternatively you could hardcode an address of a completely seperate wallet for these funds
// when your webhook would support the old addresses still
$txBuilder->addRecipient($newAddress, $sum - $fee);
// fixed fee, so it won't do coinselection (which would probably cause change etc, to hard to deal with)
$txBuilder->setFee($fee);
// init wallet, need it now
$wallet = $client->initWallet([
"identifier" => $walletIdentifier,
"passphrase" => $walletPassword
]);
// send
try {
$txHash = $wallet->sendTx($txBuilder, false);
debug($txHash);
exit(0);
} catch (\Exception $e) {
if ($confirmations === 1) {
// ignore, we probably failed to do the TX because we already did it when confirmations=0
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment