aboutsummaryrefslogtreecommitdiffstats
path: root/lib/buildliquidtx.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-08-16 21:34:13 -0700
committerAdam Soltys <[email protected]>2020-08-16 21:34:13 -0700
commit7ab3036cb1576fe38ba1fd423a58b7cd97a58579 (patch)
tree305b1a0aa321389b60385009876bbdb1fe0a1c68 /lib/buildliquidtx.js
parentb2208797d3c5559c7e8cc7d35b7a020da0c61193 (diff)
downloadcoinos-server-7ab3036cb1576fe38ba1fd423a58b7cd97a58579.tar.gz
coinos-server-7ab3036cb1576fe38ba1fd423a58b7cd97a58579.zip
non-custodial liquid wallets
Diffstat (limited to 'lib/buildliquidtx.js')
-rw-r--r--lib/buildliquidtx.js125
1 files changed, 125 insertions, 0 deletions
diff --git a/lib/buildliquidtx.js b/lib/buildliquidtx.js
new file mode 100644
index 00000000..d9b14d13
--- /dev/null
+++ b/lib/buildliquidtx.js
@@ -0,0 +1,125 @@
+const axios = require("axios");
+const liquid = require("liquidjs-lib");
+const coinselect = require("coinselect");
+const split = require("coinselect/split");
+const api = prod ? "https://blockstream.info/api" : "http://localhost:3002";
+const SATS = 100000000;
+const network = prod ? liquid.networks["liquid"] : liquid.networks["regtest"];
+
+module.exports = async ({ address, asset, amount, feeRate, user }) => {
+ const { btcasset: btc } = config.liquid;
+
+ if (!feeRate) {
+ let { feerate } = await lq.estimateSmartFee(6);
+ if (!feerate) feerate = 0.00002;
+ feeRate = feerate * SATS;
+ }
+ feeRate = Math.round(feeRate / 1000);
+ if (asset !== btc) feeRate = 0;
+
+ const payments = await db.Payment.findAll({
+ where: {
+ received: true,
+ account_id: user.account_id,
+ },
+ });
+
+ let utxos = [];
+ for (let i = 0; i < payments.length; i++) {
+ let url = `${api}/address/${payments[i].address}/utxo`;
+ try {
+ utxos.push(...(await axios.get(url)).data);
+ } catch (e) {
+ l.error("problem fetching utxos", e.message, url);
+ }
+ }
+
+ let feeInput = utxos.find((utxo) => utxo.asset === btc && utxo.value > 1000);
+ if (!feeInput) throw new Error("Couldn't find fee input");
+
+ utxos = utxos.filter((utxo) => utxo.asset === asset);
+
+ let feeChange = feeInput.value - 1000;
+
+ let targets = [
+ {
+ address,
+ value: amount,
+ },
+ ];
+
+ let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate);
+
+ if (!inputs || !outputs) {
+ l.info(
+ "utxos",
+ amount,
+ feeRate,
+ utxos.map((u) => u.value),
+ inputs,
+ outputs
+ );
+ throw new Error(
+ "Couldn't find inputs for transaction, check your balance/fee"
+ );
+ }
+
+ let psbt = new liquid.Psbt({ network });
+ let total = 0;
+
+ let { address: changeAddress } = payments[payments.length - 1];
+
+ outputs.map(({ address, value }) => {
+ total += value;
+ if (!address) {
+ address = changeAddress;
+ change.push(address);
+ }
+
+ psbt = psbt.addOutput({
+ address,
+ asset,
+ value,
+ });
+ });
+
+ for (let i = 0; i < inputs.length; i++) {
+ let { txid, vout } = inputs[i];
+ psbt = psbt.addInput({
+ hash: txid,
+ index: vout,
+ nonWitnessUtxo: Buffer.from(await lq.getRawTransaction(txid), "hex"),
+ });
+ }
+
+ if (asset !== btc) {
+ l.info("feeInput", feeInput);
+ psbt.addInput({
+ hash: feeInput.txid,
+ index: feeInput.vout,
+ nonWitnessUtxo: Buffer.from(
+ await lq.getRawTransaction(feeInput.txid),
+ "hex"
+ ),
+ });
+
+ psbt.addOutput({
+ changeAddress,
+ asset: btc,
+ value: feeChange,
+ });
+ }
+
+
+ psbt.addOutput({
+ asset: btc,
+ script: Buffer.alloc(0),
+ value: fee,
+ nonce: Buffer.alloc(1, 0),
+ });
+
+ feeRate = Math.round(feeRate * 1000);
+ psbt = psbt.toBase64();
+
+ return { tx: psbt, feeRate };
+};