aboutsummaryrefslogtreecommitdiffstats
path: root/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-05-18 15:37:50 -0700
committerAdam Soltys <[email protected]>2020-05-18 17:19:43 -0700
commitff4facd8960f605fa1ff9fa68d73ddd267cb0b8f (patch)
treedbb5b8e76e7eb54cd5ba097b7734c785b058d8f5 /routes
parent11724003fbbcd12b90b27dc370c9537eb1bbcbf2 (diff)
downloadcoinos-server-ff4facd8960f605fa1ff9fa68d73ddd267cb0b8f.tar.gz
coinos-server-ff4facd8960f605fa1ff9fa68d73ddd267cb0b8f.zip
asset issuance
Diffstat (limited to 'routes')
-rw-r--r--routes/assets.js109
-rw-r--r--routes/liquid/receive.js53
-rw-r--r--routes/payments.js1
3 files changed, 147 insertions, 16 deletions
diff --git a/routes/assets.js b/routes/assets.js
index 10eee1f5..926bdf74 100644
--- a/routes/assets.js
+++ b/routes/assets.js
@@ -1,4 +1,5 @@
const axios = require("axios");
+const crypto = require("crypto");
app.get("/assets", async (req, res) => {
let assets;
@@ -6,8 +7,112 @@ app.get("/assets", async (req, res) => {
try {
assets = await axios.get("https://assets.blockstream.info/");
res.send(assets.data);
- } catch(e) {
+ } catch (e) {
l.error("error fetching assets", e);
res.status(500).send("error fetching assets");
- }
+ }
+});
+
+app.post("/assets", auth, async (req, res) => {
+ try {
+ const sha256 = crypto.createHash("sha256");
+ const { id: user_id } = req.user;
+ const token_address = await lq.getNewAddress("", "legacy");
+ const asset_address = await lq.getNewAddress("", "legacy");
+ const blind = false;
+ const info = await lq.getAddressInfo(asset_address);
+ const { pubkey: issuer_pubkey } = info;
+
+ const { name, asset_amount, token_amount, precision, ticker } = req.body;
+ const domain = "adamsoltys.com";
+ const version = 0;
+
+ const contract = {
+ entity: { domain },
+ issuer_pubkey,
+ name,
+ precision,
+ ticker,
+ version
+ };
+
+ sha256.update(JSON.stringify(contract));
+ const hash = sha256.digest("hex");
+ const contract_hash = hash
+ .match(/[a-f0-9]{2}/g)
+ .reverse()
+ .join("");
+ const rawtx = await lq.createRawTransaction([], { data: "00" });
+ const funded = await lq.fundRawTransaction(rawtx, { feeRate: 0.00003 });
+ const ria = await lq.rawIssueAsset(funded.hex, [
+ {
+ asset_amount,
+ token_amount,
+ asset_address,
+ token_address,
+ blind,
+ contract_hash
+ }
+ ]);
+ const { asset, hex } = ria[0];
+ const brt = await lq.blindRawTransaction(hex, true, [], false);
+ const srt = await lq.signRawTransactionWithWallet(brt);
+ const allowed = (await lq.testMempoolAccept([srt.hex]))[0].allowed;
+ if (allowed) {
+ const txid = await lq.sendRawTransaction(srt.hex);
+
+ await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: {
+ user_id,
+ asset: config.liquid.btcasset
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
+ if (Math.round(funded.fee * SATS) > account.balance) {
+ l.error("amount exceeds balance", amount, fee, account.balance);
+ throw new Error("insufficient funds");
+ }
+
+ account.balance -= Math.round(funded.fee * SATS);
+ await account.save({ transaction });
+
+ account = await db.Account.create(
+ {
+ asset,
+ user_id,
+ ticker,
+ precision,
+ name,
+ balance: 0,
+ pending: asset_amount * SATS
+ },
+ { transaction }
+ );
+
+ const payment = await db.Payment.create({
+ account_id: account.id,
+ user_id,
+ hash: txid,
+ amount: asset_amount * SATS,
+ received: true,
+ confirmed: false,
+ address: asset_address,
+ network: "LBTC"
+ });
+
+ issuances[txid] = { user_id, asset, asset_amount, payment_id: payment.id };
+
+ let user = await getUserById(user_id, transaction);
+ emit(user.username, "user", user);
+ });
+
+ res.send(issuances[txid]);
+ }
+ } catch (e) {
+ l.error("asset issuance failed", e.message);
+ res.status(500).send("Failed to issue asset");
+ }
});
diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js
index 9ef67967..803caa90 100644
--- a/routes/liquid/receive.js
+++ b/routes/liquid/receive.js
@@ -20,7 +20,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
if (payments.includes(blinded.txid)) return;
Promise.all(
- tx.vout.map(async (o) => {
+ tx.vout.map(async o => {
if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return;
const { asset } = o;
@@ -30,19 +30,19 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
if (Object.keys(addresses).includes(address)) {
let user = await db.User.findOne({
where: {
- username: addresses[address],
- },
+ username: addresses[address]
+ }
});
let confirmed = 0;
let params = {
user_id: user.id,
- asset,
+ asset
};
let account = await db.Account.findOne({
- where: params,
+ where: params
});
if (account) {
@@ -83,9 +83,9 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
invoice = await db.Invoice.findOne({
where: {
user_id: user.id,
- network: "LBTC",
+ network: "LBTC"
},
- order: [["id", "DESC"]],
+ order: [["id", "DESC"]]
});
}
@@ -104,7 +104,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
tip,
confirmed,
address,
- network: "LBTC",
+ network: "LBTC"
});
payments.push(blinded.txid);
@@ -124,17 +124,42 @@ let queue = {};
zmqRawBlock.on("message", async (topic, message, sequence) => {
const payments = await db.Payment.findAll({
- where: { confirmed: 0 },
+ where: { confirmed: 0 }
});
- const hashes = payments.map((p) => p.hash);
+ const hashes = payments.map(p => p.hash);
const block = elements.Block.fromHex(message.toString("hex"), true);
const hash = await lq.getBlockHash(block.height);
const json = await lq.getBlock(hash, 2);
- json.tx.map((tx) => {
- if (hashes.includes(tx.txid)) queue[tx.txid] = 1;
+ json.tx.map(async tx => {
+ if (issuances[tx.txid]) {
+ await db.transaction(async transaction => {
+ const { user_id, asset, asset_amount, payment_id } = issuances[tx.txid];
+ const account = await db.Account.findOne({
+ where: { user_id, asset },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ account.balance = asset_amount * SATS;
+ account.pending = 0;
+ await account.save({ transaction });
+
+ const payment = await db.Payment.findOne({
+ where: { id: payment_id },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
+ payment.confirmed = true;
+ await payment.save({ transaction });
+
+ const user = await getUserById(user_id, transaction);
+ emit(user.username, "user", user);
+ });
+ }
+ else if (hashes.includes(tx.txid)) queue[tx.txid] = 1;
});
});
@@ -150,8 +175,8 @@ setInterval(async () => {
where: { hash, confirmed: 0, received: 1 },
include: {
model: db.Account,
- as: "account",
- },
+ as: "account"
+ }
});
p.confirmed = 1;
diff --git a/routes/payments.js b/routes/payments.js
index 81d7540b..ab35c9a0 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -6,6 +6,7 @@ const { join } = require("path");
(async () => {
seen = [];
addresses = {};
+ issuances = {};
await db.User.findAll({
attributes: ["username", "address", "liquid"]