Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ CARBONADO_ENDPOINT=http://localhost:7070/carbonado
UDAS_UTXO=3b367e1facc3174e97658295961faf6a4ed889129c881b7a73db1f074b49bd8a:
MARKETPLACE_SEED="lion bronze dumb tuna perfect fantasy wall orphan improve business harbor sadness"
MARKETPLACE_NOSTR=cd591c134a0d88991326b1619953d0eae2287d315a7c4a93c1e4883a8c26c464

# 1..100
MARKETPLACE_FEE_PERC=
# xpub..
MARKETPLACE_FEE_XPUB=

# :: Coordinator ::
COORDINATOR_NOSTR=9e8294eb38ba77c0fba982da8fbd370b8868c6dbfc9ca414aff4863c15dfbcff

# :: RGB PROXY ::
RGB_PROXY_ENDPOINT=http://localhost:3001
152 changes: 141 additions & 11 deletions lib/web/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,28 @@ export const createSwap = async (
): Promise<RgbSwapResponse> =>
JSON.parse(await BMC.create_swap(nostrHexSk, request));

export const createAuctionOffer = async (
nostrHexSk: string,
request: RgbAuctionOfferRequest
): Promise<RgbAuctionBidResponse> =>
JSON.parse(await BMC.create_auction_offers(nostrHexSk, request));


export const createAuctionBid = async (
nostrHexSk: string,
request: RgbAuctionBidRequest
): Promise<RgbAuctionBidResponse> =>
JSON.parse(await BMC.create_auction_bid(nostrHexSk, request));

export const finishAuction = async (
nostrHexSk: string,
request: string
): Promise<RgbAuctionOfferResponse[]> =>
JSON.parse(await BMC.finish_auction_offers(nostrHexSk, request));

export const listAuctions = async (): Promise<RgbOffersResponse> =>
JSON.parse(await BMC.list_auctions());

export const directSwap = async (
nostrHexSk: string,
request: RgbBidRequest
Expand Down Expand Up @@ -432,15 +454,15 @@ export interface InvoiceResponse {

export interface PsbtRequest {
/// Asset UTXOs
asset_inputs: PsbtInputRequest[];
assetInputs: PsbtInputRequest[];
/// Asset Descriptor Change
asset_descriptor_change: string;
assetDescriptorChange: string;
/// Asset Terminal Change (default: /10/0)
asset_terminal_change: string;
assetTerminalChange: string;
/// Bitcoin UTXOs
bitcoin_inputs: PsbtInputRequest[];
bitcoinInputs: PsbtInputRequest[];
/// Bitcoin Change Addresses (format: {address}:{amount})
bitcoin_changes: string[];
bitcoinChanges: string[];
/// Bitcoin Fee
fee: PsbtFeeRequest;
/// Allow RBF
Expand All @@ -453,11 +475,11 @@ interface PsbtInputRequest {
/// Asset or Bitcoin UTXO
utxo: string;
/// Asset or Bitcoin UTXO Terminal (ex. /0/0)
utxo_terminal: string;
utxoTerminal: string;
/// Asset or Bitcoin Tweak
tapret?: string;
/// Asset or Bitcoin Tweak
sigh_hash?: PsbtSigHashRequest;
sighHash?: PsbtSigHashRequest;
}

interface PsbtSigHashRequest {
Expand Down Expand Up @@ -748,7 +770,7 @@ export interface RgbTransferDetail {
}

export interface TxStatus {
not_found?: any;
notFound?: any;
error?: string;
mempool?: any;
block?: number;
Expand Down Expand Up @@ -793,8 +815,93 @@ export interface RgbOfferRequest {
changeTerminal: string;
/// Bitcoin Change Addresses (format: {address}:{amount})
bitcoinChanges: string[];
presig: boolean;
expire_at?: number;
strategy: RgbSwapStrategy;
expireAt?: number;
}

export interface RgbSwapStrategy {
auction?: string,
p2p?: string,
hotswap?: string,
}

export interface RgbAuctionStrategy {
auction?: string,
airdrop?: string,
}

export interface RgbAuctionOfferRequest {
offers: RgbOfferRequest[],

signKeys: string[],

strategy: RgbAuctionStrategy,

fee?: PsbtFeeRequest
}

export interface RgbOfferUpdateRequest {
/// The Contract ID
contract_id: string,
/// The Offer ID
offer_id: string,
// Swap PSBT
offer_psbt: string
}

export interface RgbOfferUpdateResponse {
/// The Contract ID
contract_id: string,
/// The Offer ID
offer_id: string,
/// Updated?
updated: boolean
}

export interface RgbAuctionBidRequest {
/// The Offer ID
offerId: string,
/// Asset Amount
assetAmount: string,
/// Universal Descriptor
descriptor: string,
/// Bitcoin Terminal Change
changeTerminal: string,
/// Descriptors to Sign
signKeys: string[],
/// Bitcoin Fee
fee: PsbtFeeRequest,
}

export interface RgbAuctionBidResponse {
/// The Bid ID
bidId: string,
/// The Offer ID
offerId: string,
/// Fee Value
feeValue: number,
}

export interface RgbMatchResponse {
/// Transfer ID
consigId: string,
/// Offer ID
offerId: string,
/// Bid ID
bidId: string,
}

export interface RgbAuctionOfferResponse {
/// Offer ID
offerId: string,
/// Contract ID
contractId: string,
/// Asset/Contract Amount
assetAmount: number,
/// Bitcoin Price
bitcoinPrice: number,
/// Bundle ID
bundleId: string,
}

export interface RgbOfferResponse {
Expand All @@ -810,6 +917,8 @@ export interface RgbOfferResponse {
sellerAddress: string;
/// Seller PSBT (encoded in base64)
sellerPsbt: string;
/// Bundle ID (collection)
bundleId?: string,
}

export interface RgbBidRequest {
Expand Down Expand Up @@ -875,9 +984,30 @@ export interface PublicRgbOfferResponse {
/// Bitcoin Price
bitcoinPrice: bigint;
/// Initial Offer PSBT
offerPsbt: string;
offerPsbt?: string;
}

export interface RgbAuctionFinishResponse {
/// Bundle ID
bundle_id: string,
/// New Change Outpoint
outpoint: string,
/// Sold Items
sold: Map<string, RgbSwapItem>,
/// Reamining Items
remaining: Map<string, RgbSwapItem>,
}

export interface RgbSwapItem {
/// Contract ID
contractId: string,
/// Iface
iface: string,
/// Final Consig
contractAmount: string,
}


export interface PublicRgbBidResponse {
/// Bid ID
bidId: string;
Expand Down
90 changes: 81 additions & 9 deletions src/bin/bitmaskd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ use axum::{
use bitcoin_30::secp256k1::{ecdh::SharedSecret, PublicKey, SecretKey};
use bitmask_core::{
bitcoin::{save_mnemonic, sign_and_publish_psbt_file},
carbonado::{handle_file, metrics, server_retrieve, server_store, store},
carbonado::{
auctions_retrieve, auctions_store, handle_file, marketplace_retrieve, marketplace_store,
metrics, store,
},
constants::{
get_marketplace_nostr_key, get_marketplace_seed, get_network, get_udas_utxo, switch_network,
},
Expand All @@ -34,11 +37,12 @@ use bitmask_core::{
proxy_media_data_store, proxy_media_retrieve, proxy_metadata_retrieve,
},
rgb::{
accept_transfer, clear_watcher as rgb_clear_watcher, create_invoice, create_psbt,
create_watcher, full_transfer_asset, get_contract, import as rgb_import, issue_contract,
list_contracts, list_interfaces, list_schemas, list_transfers as list_rgb_transfers,
reissue_contract, remove_transfer as remove_rgb_transfer,
save_transfer as save_rgb_transfer,
accept_transfer,
carbonado::retrieve_auctions_offers,
clear_watcher as rgb_clear_watcher, create_invoice, create_psbt, create_watcher,
full_transfer_asset, get_contract, import as rgb_import, issue_contract, list_contracts,
list_interfaces, list_schemas, list_transfers as list_rgb_transfers, reissue_contract,
remove_transfer as remove_rgb_transfer, save_transfer as save_rgb_transfer,
structs::{
RgbProxyConsigCarbonadoReq, RgbProxyConsigFileReq, RgbProxyConsigUpload,
RgbProxyMediaCarbonadoReq, RgbProxyMediaFileReq,
Expand Down Expand Up @@ -474,7 +478,7 @@ async fn co_store(
},
}

metrics::update(&filepath).await?;
// metrics::update(&filepath).await?;

Ok((StatusCode::OK, TypedHeader(cc), "Success"))
}
Expand Down Expand Up @@ -527,7 +531,7 @@ async fn co_server_store(
body: Bytes,
) -> Result<impl IntoResponse, AppError> {
info!("POST /carbonado/server/{name}, {} bytes", body.len());
let (filepath, encoded) = server_store(&name, &body, None).await?;
let (filepath, encoded) = marketplace_store(&name, &body, None).await?;

match OpenOptions::new()
.read(true)
Expand Down Expand Up @@ -627,7 +631,7 @@ async fn co_metadata(
async fn co_server_retrieve(Path(name): Path<String>) -> Result<impl IntoResponse, AppError> {
info!("GET /server/{name}");

let result = server_retrieve(&name).await;
let result = marketplace_retrieve(&name).await;
let cc = CacheControl::new().with_no_cache();

match result {
Expand Down Expand Up @@ -687,6 +691,71 @@ async fn rgb_proxy_media_data_save(
Ok((StatusCode::OK, Json(resp)))
}

async fn rgb_retrieve_auction(
Path((bundle_id, name)): Path<(String, String)>,
) -> Result<impl IntoResponse, AppError> {
info!("GET /auction/{bundle_id}");

let result = auctions_retrieve(&bundle_id, &name).await;
let cc = CacheControl::new().with_no_cache();
match result {
Ok((bytes, _)) => {
debug!("read {0} bytes.", bytes.len());
Ok((StatusCode::OK, TypedHeader(cc), bytes))
}
Err(e) => {
debug!("file read error {0} .Details: {1}.", name, e.to_string());
Ok((StatusCode::OK, TypedHeader(cc), Vec::<u8>::new()))
}
}
}

async fn rgb_store_auction(
Path((bundle_id, name)): Path<(String, String)>,
body: Bytes,
) -> Result<impl IntoResponse, AppError> {
info!("POST /auction/{bundle_id}");
let (filepath, encoded) = auctions_store(&bundle_id, &name, &body, None).await?;

match OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&filepath)
{
Ok(file) => {
let present_header = match carbonado::file::Header::try_from(&file) {
Ok(header) => header,
_ => carbonado::file::Header::try_from(&body)?,
};
let present_len = present_header.encoded_len - present_header.padding_len;
debug!("present_len: {present_len}");
let resp = fs::write(&filepath, &encoded).await;
debug!("file override status {}", resp.is_ok());
}
Err(err) => match err.kind() {
ErrorKind::NotFound => {
debug!("no file found, writing 0 bytes.");
fs::write(&filepath, &body).await?;
}
_ => {
error!("error in POST /carbonado/server/{name}: {err}");
return Err(err.into());
}
},
}

let cc = CacheControl::new().with_no_cache();
Ok((StatusCode::OK, TypedHeader(cc)))
}

async fn rgb_destroy_auction(
Path((bundle_id, _name)): Path<(String, String)>,
) -> Result<impl IntoResponse, AppError> {
info!("DELETE /auction/{bundle_id}");
Ok((StatusCode::OK, Json("")))
}

const BMC_VERSION: &str = env!("CARGO_PKG_VERSION");

async fn status() -> Result<impl IntoResponse, AppError> {
Expand Down Expand Up @@ -812,6 +881,9 @@ async fn main() -> Result<()> {
.route("/proxy/media-metadata", post(rgb_proxy_media_data_save))
.route("/proxy/media-metadata/:id", get(rgb_proxy_media_retrieve))
.route("/proxy/media/:id", get(rgb_proxy_metadata_retrieve))
.route("/auction/:bundle_id/:name", get(rgb_retrieve_auction))
.route("/auction/:bundle_id/:name", post(rgb_store_auction))
.route("/auction/:bundle_id/:name", delete(rgb_destroy_auction))
.route("/metrics.json", get(json_metrics))
.route("/metrics.csv", get(csv_metrics));

Expand Down
Loading