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)}