Skip to content

Commit

Permalink
Support Auction Swaps (#430)
Browse files Browse the repository at this point in the history
Support Auction Swaps
  • Loading branch information
crisdut authored Dec 26, 2023
2 parents 1f3044c + 0b5a312 commit 11edf38
Show file tree
Hide file tree
Showing 26 changed files with 4,970 additions and 647 deletions.
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

0 comments on commit 11edf38

Please sign in to comment.