NFTEA Gallery

Bitshares NFT gamification possibilities

Deriving randomness from an NFT's latest trades

We can use the /history/trades_es API endpoint to retrieve the historical trades for an NFT; looking into the recent NFTEA.BALANCE trade history we can get the block_num and trx_id values within the block_data object:

[
  {
    "account_history": {
      "id": "2.9.x",
      "account": "1.2.x",
      "operation_id": "1.11.x",
      "sequence": 93,
      "next": "2.9.x"
    },
    "operation_history": {
      "trx_in_block": 3,
      "op_in_trx": 0,
      "operation_result": "[0,{}]",
      "virtual_op": 2,
      "op": "",
      "op_object": { ... }
    },
    "operation_type": 4,
    "operation_id_num": 1181063518,
    "block_data": {
      "block_num": 64345924,
      "block_time": "2021-12-07T20:24:33",
      "trx_id": "1886e34bdbe3fb2cbb84961c3144d1cd99efbb69"
    }
  }
]

With the above retrieved block_num let's fetch the block contents to acquire the block's transaction_merkle_root and even the witness_signature.

{
  "previous": "03d5d743aceccdaf6b0f073140f22dd9e57d0bbd",
  "timestamp": "2021-12-07T20:24:33",
  "witness": "1.6.184",
  "transaction_merkle_root": "0154553be82cd8773883c50867a7ba7d5b8f14c7",
  "extensions": [],
  "witness_signature": "20009fe0a85b2e5d764bd55c0c992f952ed796072f34a0c4fa10b14f52b5813e62178ef21e128dffdc89eb7838c5875f123c395c879f32e1392122fe43023068cd",
  "transactions": [
    ...
  ],
  "id": "64345924"
}

We can combine the trade's "trx_id": "1886e34bdbe3fb2cbb84961c3144d1cd99efbb69" with the block's "transaction_merkle_root": "0154553be82cd8773883c50867a7ba7d5b8f14c7" (and optionally the witness_signature) to produce a random NFT effect such as showing one of several images contained within the NFT.

It's important for developers and users to know that simply using the trx_id for deriving a random outcome is fundamentally flawed as it's possible to 'mine' a desirable trx_id prior to network broadcast by simply checking the generated trx_id prior to broadcast (and if undesirable regenerating the trx in a loop).

If you were to solely use the transaction_merkle_root then if two NFT from the same collection were to be purchased within the same block they would both have the same random effect; by combining it with the individual trx_id you avoid this potential outcome.

So how could we merge the id 1886e34bdbe3fb2cbb84961c3144d1cd99efbb69 and merkle root 0154553be82cd8773883c50867a7ba7d5b8f14c7?

We could strip the letters, use addition/multiplication on the two numbers together then take last 2 digits:

let trx_id = "1886e34bdbe3fb2cbb84961c3144d1cd99efbb69"
let transaction_merkle_root = "0154553be82cd8773883c50867a7ba7d5b8f14c7"

function onlyNumbers (str) {
  let maxLen = str.length < 5 ? str.length : 5;
  return parseInt(
          str
          .split('')
          .map(char => !isNaN(char) ? char : null)
          .filter(x => x)
          .join('')
          .slice(0,5)
        );
}

let combination = (onlyNumbers(trx_id) + onlyNumbers(transaction_merkle_root)).toString();
let outcome = combination.substring(combination.length - 2, combination.length);

console.log(outcome) // outcome = 08

So if we've got 100 images we would pick the 8th image to display for this user. If you had 10 images, you could do combination.substring(combination.length - 1, combination.length); for 0-9 and for 1000 images you could do combination.substring(combination.length - 3, combination.length);, etc.

A longer term solution would be to replace letters with numbers in case of an extremely unlikely scenario whereby the trx_id or merkle root are all letters, which would cause an error in the above script.

This is simplest to implement for an NFT with a single holder as you can lookup the last trade then fetch its block.

If you're planning on implementing this functionality for an NFT with multiple holders of whole NFT units then you'd need to track all historical trades from the date of issuance & also track who owns which and what image should be shown to them, this increases in complexity when someone transfers the asset rather than selling it directly on the market.

Market ticker API 'latest' field

Using the Bitshares Insight API we can access the market ticker for any valid Bitshares asset trading pairs.

Let's look at the contents of BTS:NFTEA.BALANCE:

{
  ...
  "latest": "20000",
  ...
}

Compared to the contents of BTS:NFTEA.COLOUR (Which hasn't been bought yet on the BTS DEX):

{
  ...
  "latest": "0",
  ...
}

We are able to retrieve the last trading rate for the trading pair with a single API query no matter how long ago this trade took place. We could therefore dynamically change the public facing view of a BTS NFT with this value with relative ease, albeit you would require NFT galleries to run additional code for this to work.

Potential uses of this 'latest' field

Encouraging trade of NFTs on the BTS DEX

By improving/evolving an NFT based on the latest field value, then this could encourage the active trade of the NFT on the Bitshares DEX.

This is somewhat similar to how certain pokemon only evolve once traded, encouraging trading activity between users.

Treasure chest

If the latest field is 0 then the galleries could show a closed chest with its contents hidden from the public, then once sold the treasure chest opens and contains physical 3d bitshares tokens reflecting the latest value (e.g. 1000 BTS -> a 1k pile of BTS tokens).

If the NFT was sold for a lesser amount the treasure pile in the NFT would visibly decrease.

If you bought it at 1k BTS then transferred it to another account then its visuals would still reflect the 1k BTS trade, as no market ticker change has taken place. You might want to do this if BTS rose significantly in value & you wanted to avoid reducing the visual appearance despite the monetary appearance being the same when selling the NFT on.

Item power/mana levels

Say you purchased a sword for a videogame in the metaverse, the ticker latest value could influence the stats/appearance of the item in game.

Could the latest field be cheated?

A whale could certainly purchase the NFT (using an alternative account) on the open market to drive up the ticker value without costing themselves more than the blockchain fees, however they would need to place it for sale on the public DEX order book during which time it could be purchased by an uninvolved individual.

If you were to impose a market fee on an NFT then an individual trading between their own accounts would have to pay more than just blockchain fees.

Such back and forth market trades between two accounts owned by the same user could be considered taxable events which could cost them significantly, so it's not without risk.

To somewhat offset the risk of a latest value suddenly inflating, you could instead fetch the values from the last x trades (rather than the latest) and perform a moving average calculation to dampen the effect of a single manual trade.

That said, it could be perceived as using your bag of BTS to stake an NFT into a higher quality state for off order book trades, providing further utility to your BTS, depends on the NFT use case.

Order book API bid/ask fields

The get_order_book API can be used to retrieve the current bids & asks on a market trading pair.

By showing off the bid and ask participants (robohash id + account name), either in a table or visually in a virtual art gallery auction you could encourage other Bitshares users to bid/sell on the NFT.

NFTEA Gallery

An open source NFT gallery powered by the Bitshares blockchain!
All displayed NFTs are tradeable on the Bitshares decentralized exchange, get collecting!
Created using Next.js and running on Vercel