Skip to content

Commit

Permalink
feat(instant): add instant buy support to order
Browse files Browse the repository at this point in the history
  • Loading branch information
kodemon committed Aug 1, 2023
1 parent 35aceb4 commit c419425
Show file tree
Hide file tree
Showing 36 changed files with 479 additions and 199 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

# Development

.bin
.env
.mongodb

# Dependencies

Expand Down
9 changes: 9 additions & 0 deletions docker-compose-mongo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: "3"
services:
database:
image: mongo:latest
container_name: sado-mongo
ports:
- ${MONGO_PORT}:27017
volumes:
- ./.mongodb:/data/db
23 changes: 0 additions & 23 deletions docker-compose.yml

This file was deleted.

14 changes: 7 additions & 7 deletions .env.example → dotenv
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# API Configuration

domain=http://localhost
PORT=3030
TOKEN=replacewithsecrettoken

# Databases
# MONGO

MONGODB_NAME=sado
MONGODB_URI=mongodb://localhost:27017
MONGO_HOSTNAME="localhost"
MONGO_PORT=27017
MONGO_DATABASE="sado-testnet"
MONGO_USERNAME=""
MONGO_PASSWORD=""

# IPFS

IPFS_GATEWAY=http://localhost:8080
IPFS_API=http://localhost:3009

# Ocean

OCEAN_ENDPOINT=https://ocean.defichain.com/v0/mainnet

# Slack

SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"build": "tsc",
"start": "DEBUG=sado-* ts-node-dev ./src/main.ts",
"worker": "DEBUG=sado-* ts-node ./src/worker.ts",
"mongodb:start": "docker compose -f ./docker-compose-mongo.yaml up -d",
"mongodb:stop": "docker compose -f ./docker-compose-mongo.yaml down",
"clean": "rm -rf dist",
"lint": "eslint ./src --max-warnings 0 --report-unused-disable-directives --fix"
},
Expand All @@ -30,6 +32,7 @@
"node-fetch": "^2.6.11",
"p-limit": "^3.1.0",
"tiny-secp256k1": "^2.2.2",
"varuint-bitcoin": "^1.1.2",
"ws": "^8.13.0"
},
"devDependencies": {
Expand Down
9 changes: 9 additions & 0 deletions src/Bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { mongo } from "./Services/Mongo";

export async function bootstrap() {
await database();
}

async function database() {
await mongo.connect();
}
29 changes: 20 additions & 9 deletions src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,26 @@ import { envToNumber, getEnvironmentVariable } from "./Libraries/Environment";
import { DEFAULT_NETWORK } from "./Libraries/Network";

export const config = {
port: getEnvironmentVariable("PORT", envToNumber),
token: getEnvironmentVariable("TOKEN"),
lookupEndpoint: `https://${DEFAULT_NETWORK}.ordit.io/utxo`,
ipfsGateway: getEnvironmentVariable("IPFS_GATEWAY"),
ipfsApi: getEnvironmentVariable("IPFS_API"),
oceanEndpoint: getEnvironmentVariable("OCEAN_ENDPOINT"),
api: {
domain: getEnvironmentVariable("DOMAIN"),
port: getEnvironmentVariable("PORT", envToNumber),
token: getEnvironmentVariable("TOKEN"),
},
lookup: {
endpoint: `https://${DEFAULT_NETWORK}.ordit.io/utxo`,
},
ipfs: {
gateway: getEnvironmentVariable("IPFS_GATEWAY"),
api: getEnvironmentVariable("IPFS_API"),
},
mongo: {
name: getEnvironmentVariable("MONGODB_NAME"),
uri: getEnvironmentVariable("MONGODB_URI"),
hostname: getEnvironmentVariable("MONGO_HOSTNAME"),
port: getEnvironmentVariable("MONGO_PORT", envToNumber),
database: getEnvironmentVariable("MONGO_DATABASE"),
username: getEnvironmentVariable("MONGO_USERNAME"),
password: getEnvironmentVariable("MONGO_PASSWORD"),
},
slack: {
webhook: getEnvironmentVariable("SLACK_WEBHOOK_URL"),
},
slack: getEnvironmentVariable("SLACK_WEBHOOK_URL"),
};
65 changes: 48 additions & 17 deletions src/Methods/Order/CreateOrder/CreateOrderPsbt.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { BadRequestError } from "@valkyr/api";
import { address, payments, Psbt } from "bitcoinjs-lib";
import { payments, Psbt } from "bitcoinjs-lib";

import { Lookup } from "../../../Services/Lookup";
import { utils } from "../../../Utilities";
import { PsbtInput } from "../../../Utilities/PSBT";
import { Params } from "./Params";

export async function createOrderPsbt(cid: string, params: Params, lookup: Lookup): Promise<Psbt> {
Expand Down Expand Up @@ -36,24 +35,56 @@ export async function createOrderPsbt(cid: string, params: Params, lookup: Looku
let total = 0;
let fee = 0;

const type = utils.bitcoin.getAddressType(params.order.maker);
if (type === undefined) {
throw new BadRequestError("Order maker address does not match supported address types.");
}

const pubkey = params.signature.pubkey;

for (const utxo of utxos) {
const { txid, n, sats } = utxo;

const input: PsbtInput = {
hash: txid,
index: n,
witnessUtxo: {
script: address.toOutputScript(params.order.maker, lookup.btcnetwork),
value: sats,
},
};

if (params.signature.pubkey) {
input.tapInternalKey = Buffer.from(params.signature.pubkey, "hex");
const { txid, n, value } = utxo;

const sats = utils.bitcoin.btcToSat(value);

switch (type) {
case "taproot": {
if (pubkey === undefined) {
throw new BadRequestError("Taproot address requires a pubkey");
}
let tapInternalKey = Buffer.from(pubkey, "hex");
if (tapInternalKey.length === 33) {
tapInternalKey = tapInternalKey.slice(1, 33);
}
psbt.addInput({
hash: txid,
index: n,
witnessUtxo: {
script: utils.taproot.getPaymentOutput(tapInternalKey, lookup.btcnetwork),
value: sats,
},
tapInternalKey,
});
break;
}

case "bech32": {
psbt.addInput({
hash: txid,
index: n,
witnessUtxo: {
script: Buffer.from(utxo.scriptPubKey.hex, "hex"),
value: sats,
},
});
break;
}

default: {
psbt.addInput({ hash: txid, index: n });
}
}

psbt.addInput(input);

total += sats;
fee = utils.psbt.getEstimatedFee(psbt, params.satsPerByte);

Expand Down
29 changes: 25 additions & 4 deletions src/Methods/Order/CreateOrder/Method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BadRequestError, method, NotFoundError } from "@valkyr/api";

import { Lookup } from "../../../Services/Lookup";
import { utils } from "../../../Utilities";
import { order } from "../../../Utilities/Order";
import { validate } from "../../../Validators";
import { createOrderPsbt } from "./CreateOrderPsbt";
import { params } from "./Params";
Expand All @@ -28,10 +29,30 @@ export const createOrder = method({
// ### Validate Signature
// Make sure that the order is verifiable by the API when it is received.

if (params.signature.format === "psbt") {
validate.order.psbt(params.signature.value, params.order.location, lookup.btcnetwork);
} else {
validate.order.message(utils.order.toHex(params.order), params.order.maker, params.signature.value);
switch (params.signature.format) {
case "psbt": {
validate.order.signature.psbt(params.signature.value, params.order.location, lookup.btcnetwork);
break;
}
case "ordit": {
if (params.signature.pubkey === undefined) {
throw new BadRequestError("Signature format 'ordit' requires a public key");
}
validate.order.signature.ordit(
order.toHex(params.order),
params.signature.pubkey,
params.signature.value,
lookup.btcnetwork
);
break;
}
case "core": {
validate.order.signature.core(order.toHex(params.order), params.order.maker, params.signature.value);
break;
}
default: {
throw new BadRequestError(`Signature format ${params.signature.format} is not supported`);
}
}

// ### Store Order
Expand Down
18 changes: 5 additions & 13 deletions src/Methods/Order/CreateOrder/Params.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import Schema, { array, number, string, Type, unknown } from "computed-types";
import Schema, { number, string, Type } from "computed-types";

import { orderSchema } from "../../../Utilities/Order/OrderPayload";
import { validate } from "../../../Validators";
import { schema } from "../../../Validators/Schema";

export const params = Schema({
network: validate.schema.network,
order: Schema({
type: validate.schema.type,
ts: number,
location: validate.schema.location,
cardinals: number,
maker: string,
expiry: number.optional(),
satoshi: number.optional(),
meta: unknown.record(string, unknown).optional(),
orderbooks: array.of(string).optional(),
}),
order: orderSchema,
signature: Schema({
value: string,
format: string.optional(),
format: schema.signature.format,
desc: string.optional(),
pubkey: string.optional(),
}),
Expand Down
3 changes: 2 additions & 1 deletion src/Methods/Order/CreateOrder/UploadOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ export async function uploadOrder(params: Params): Promise<string> {
ts: params.order.ts,
type: params.order.type,
location: params.order.location,
cardinals: params.order.cardinals,
maker: params.order.maker,
cardinals: params.order.cardinals,
instant: params.order.instant,
expiry: params.order.expiry,
satoshi: params.order.satoshi,
meta: params.order.meta,
Expand Down
11 changes: 11 additions & 0 deletions src/Methods/Order/CreateSignableMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { method } from "@valkyr/api";

import { utils } from "../../Utilities";
import { orderSchema } from "../../Utilities/Order/OrderPayload";

export const createSignableMessage = method({
params: orderSchema,
handler: async (order) => {
return utils.order.toHex(order);
},
});
2 changes: 2 additions & 0 deletions src/Methods/Order/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { api } from "../../Api";
import { createOrder } from "./CreateOrder/Method";
import { createSignableMessage } from "./CreateSignableMessage";
import { createSignablePsbt } from "./CreateSignablePsbt";
import { getOrder } from "./GetOrder";

api.register("CreateOrder", createOrder);
api.register("CreateSignableMessage", createSignableMessage);
api.register("CreateSignablePsbt", createSignablePsbt);
api.register("GetOrder", getOrder);
2 changes: 1 addition & 1 deletion src/Middleware/HasValidToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { config } from "../Config";

export const hasValidToken: Action = async (ctx, res) => {
const authorization = ctx.headers?.authorization;
if (authorization === undefined || authorization !== `Bearer ${config.token}`) {
if (authorization === undefined || authorization !== `Bearer ${config.api.token}`) {
return res.reject(new UnauthorizedError());
}
return res.accept();
Expand Down
4 changes: 2 additions & 2 deletions src/Models/Collection.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ipfs } from "../Services/IPFS";
import { db } from "../Services/Mongo";
import { mongo } from "../Services/Mongo";
import { IPFSCollection } from "./IPFS";
import { Transaction } from "./Transaction";

const collection = db.collection<CollectionDocument>("collections");
const collection = mongo.db.collection<CollectionDocument>("collections");

export async function addCollection(tx: Transaction): Promise<void> {
const document = await ipfs.getCollection(tx.cid);
Expand Down
5 changes: 3 additions & 2 deletions src/Models/IPFS.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { db } from "../Services/Mongo";
import { mongo } from "../Services/Mongo";

export const collection = db.collection<IPFSDocument>("ipfs");
export const collection = mongo.db.collection<IPFSDocument>("ipfs");

/*
|--------------------------------------------------------------------------------
Expand Down Expand Up @@ -36,6 +36,7 @@ export type IPFSOrder = {
location: string;
maker: string;
cardinals: number;
instant?: string;
expiry?: number;
satoshi?: number;
meta?: Record<string, unknown>;
Expand Down
4 changes: 2 additions & 2 deletions src/Models/Notification.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { db } from "../Services/Mongo";
import { mongo } from "../Services/Mongo";

export const collection = db.collection<Notification>("notifications");
export const collection = mongo.db.collection<Notification>("notifications");

/*
|--------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/Models/Offer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { Network } from "../Libraries/Network";
import { PriceList } from "../Libraries/PriceList";
import { ipfs } from "../Services/IPFS";
import { Lookup } from "../Services/Lookup";
import { db } from "../Services/Mongo";
import { mongo } from "../Services/Mongo";
import { utils } from "../Utilities";
import { validate } from "../Validators";
import { IPFSOffer, IPFSOrder } from "./IPFS";
import { Inscription, Ordinal, Transaction, Vout } from "./Transaction";

const collection = db.collection<OfferDocument>("offers");
const collection = mongo.db.collection<OfferDocument>("offers");

export class Offer {
/**
Expand Down
Loading

0 comments on commit c419425

Please sign in to comment.