Skip to content

Marketplace Example

The following code shows what the off-chain code looks like for basic NFT marketplace that uses Balius SDK.

The idea is simple: the marketplace address can lock NFT and release them only if the correct price for the NFT is paid. To accomplish that, our off-chain component is going to do the following:

  • for each time our marketplace address receives an UTxO representing an buyable asset, we store a reference in database table.
  • for each time our marketplace address releases an UTxO representing a buyable asset, we remove it from our database table.
  • if someone wants to query the list available assets, we lookup available items in the db and provide them as a json values.
  • if someone want to buy one of the available assets, we create a partial transaction and return it for the user to balance & sign.

Here’s the code:

::warning:: this is WIP, specific function names and structures will most likely suffer some changes. The essence of the framework and the semantic meaning of the artifacts will remain.

const MARKETPLACE: &str = "addr1xxx";
#[derive(Datum)]
struct AssetDatum {
price: PlutusInt,
}
#[chain_event]
#[match_inbound_utxo(to_address=MARKETPLACE)]
fn on_new_asset(utxo: UTxO) -> Result<()> {
let db = use_extension::<Database>();
let datum = utxo.datum_as::<AssetDatum>();
if datum.is_none() {
bail!("unexpected utxo");
}
for asset in utxo.assets() {
db.execute(
"INSERT INTO (policy, asset, price) VALUES ({}, {}, {})",
asset.policy_id,
asset.asset_name,
datum.price,
);
}
Ok(())
}
#[chain_event]
#[match_outbound_utxo(from_address=MARKETPLACE)]
fn on_asset_sold(utxo: UTxO) -> Result<()> {
let db = use_extension::<Database>();
for asset in utxo.assets() {
db.execute(
"DELETE FROM assets WHERE policy={} and asset={}",
asset.policy_id,
asset.asset_name,
);
}
Ok(())
}
#[extrinsic_event]
#[match_http_route(path = "/assets", method = "GET")]
fn query_assets() -> Result<JsonValue> {
let db = use_extension::<Database>();
let assets = db.query_all("SELECT * from assets;").to_json();
Ok(assets)
}
#[derive(Serialize, Deserialize)]
struct Order {
policy_id: String,
asset_name: String,
buyer: Address,
}
#[extrinsic_event]
#[match_http_route(path = "/orders", method = "POST")]
fn create_order(params: Order) -> Result<PartialTx> {
let db = use_extension::<Database>();
let asset = db.query_first(
"SELECT * from assets WHERE policy={}, name={};",
params.policy_id,
params.asset_name,
);
if asset.is_none() {
bail!("we don't have that asset");
}
let market = party_from_address(MARKETPLACE);
let buyer = party_from_address(params.buyer);
let tx = TxBuilder::new()
.transfer_asset(
market, // from party
buyer, // to party
params.policy_id,
params.asset_name,
TransferQuantity::All,
)
.output_ada(
market, // to party
asset.price, // lovelace amount
)
.build();
Ok(tx)
}