/** * Composes a send transaction * @param string $asset A counterparty asset name or BTC * @param mixed $quantity Quantity of asset to send. Accepts a float or a Tokenly\CounterpartyTransactionComposer\Quantity or Tokenly\CryptoQuantity\CryptoQuantity object. Use a Quantity object for indivisible assets. * @param mixed $destination A single destination bitcoin address. For BTC sends an array of [[address, amount], [address, amount]] is also allowed. Amounts should be float values. * @param string $private_key_wif The private key in ASCII WIF format. This can be null to compose an unsigned transaction. * @param array $utxos An array of UTXOs. Each UTXO should be ['txid' => txid, 'n' => n, 'amount' => amount (in satoshis), 'script' => script hexadecimal string] * @param mixed $change_address_collection a single address string to receive all change. Or an array of [[address, amount], [address, amount], [address]]. Amounts should be float values. An address with no amount for the last entry will send the remaining change to that address. * @param float $fee A fee * @param float $btc_dust Amount of BTC dust to send with the Counterparty transaction. * @return Array returns a ComposedTransaction object */ public function composeSend($asset, $quantity, $destination, $private_key_wif, $utxos, $change_address_collection = null, $fee = null, $btc_dust = null) { if ($asset == 'BTC') { return $this->composeBTCSend($quantity, $destination, $private_key_wif, $utxos, $change_address_collection, $fee); } $fee_satoshis = $fee === null ? self::DEFAULT_FEE : intval(round($fee * self::SATOSHI)); $btc_dust_satoshis = $btc_dust === null ? self::DEFAULT_BTC_DUST : intval(round($btc_dust * self::SATOSHI)); // get total and change amount $change_amounts = $this->calculateAndValidateChange($utxos, $btc_dust_satoshis, $fee_satoshis, $change_address_collection); $tx_builder = TransactionFactory::build(); // add the UTXO inputs $transaction_outputs = $this->addInputsAndReturnPreviousOutputs($utxos, $tx_builder); // pay the btc_dust to the destination if (is_array($destination)) { throw new Exception("Multiple destinations are not supported for counterparty sends", 1); } $tx_builder->payToAddress($btc_dust_satoshis, AddressFactory::fromString($destination)); // build the OP_RETURN script $op_return_builder = new OpReturnBuilder(); $op_return = $op_return_builder->buildOpReturn($quantity, $asset, $utxos[0]['txid']); $script = ScriptFactory::create()->op('OP_RETURN')->push(Buffer::hex($op_return, 28))->getScript(); $tx_builder->output(0, $script); // pay the change to self $this->payChange($change_amounts, $tx_builder); // sign if ($private_key_wif !== null) { $signed_transaction = $this->signTx($private_key_wif, $tx_builder, $transaction_outputs); return $this->buildReturnValuesFromTransactionAndInputs($signed_transaction, $utxos, true); } return $this->buildReturnValuesFromTransactionAndInputs($tx_builder->get(), $utxos, false); }
/** * @expectedException Exception * @expectedExceptionMessage Asset ID was too low */ public function testComposeTooSmallNumericAssetID() { $op_return_builder = new OpReturnBuilder(); $fake_txid = 'deadbeef00000000000000000000000000000000000000000000000000001111'; $op_return_builder->buildOpReturn(100, 'A95428956661682176', $fake_txid); }