The following additions are required to your wallet interface in order to support viewing and sending CoinSpark assets.

Displaying asset balances

Each asset in the Asset Type List for which isVisible is true should have its balance displayed inside the wallet.

To calculate the balance to display for an asset, carry out the following steps:

  • Extract that asset’s assetID and webPageJSON from the Asset Type List.
  • If the webPageJSON is null, display something like "Loading asset…" If the genesis is known, you can display "Loading asset from genesis.domainName…" instead. Either way, stop here.
  • Identify the entries in the Asset Balance List whose assetID matches that of the asset, and whose (unspentTxID, unspentVout) are in the wallet’s regular list of unspent bitcoin transaction outputs.
  • Calculate the raw quantity of the asset by summing the qty value for those entries, skipping over any null values.
  • Format this raw quantity as a string using the fields in webPageJSON according to the formula on this page.
  • If your wallet display separate confirmed and unconfirmed bitcoin balances, do the same for CoinSpark asset balances. Create two separate totals of the qty values in the Asset Balance List, containing confirm and unconfirmed transaction outputs respectively. Format each of these raw quantities as a string as above.
  • If the qty or qtyFailures field for any of the asset’s entries in the Asset Balance List is null then display something like "(updating…)" after the formatted balance.

The domain name of an asset, stored in the genesis.domainName field of the Asset Type List, should be displayed very prominently if available. This is the primary way of establishing the asset’s credibility, since the asset issuer renders an asset valid by hosting a web page on their website. Clicking on the domain name should take the user to the home page for that domain.

Other information about the asset, such as its name, issuer, description, icon, and expiry date should be displayed as appropriate. Clicking on the asset name should take the user to the asset web page.

If validChecked for an asset is not null and validFailures is 0 then a link should be provided to the contract_url from the webPageJSON field. If validFailures is more than 0, the wallet should display a prominent warning that the asset could not be validated. In this case and if the asset’s contractContent is not null, the wallet should allow the user to view the contract stored in contractContent. This cached contract (from which the asset hash was calculated) remains legally binding in the event of an issuer attempting to renege on the original agreement. The cached contract should be displayed based on the contractMIMEType stored, although it first few bytes can also be examined to determine its format.

If the assetRef.blockNum of the asset has less than 1008 confirmations (approximately one week) then the asset should be marked with a ‘New’ warning badge. Newly issued assets are at greater risk of being forgeries, since someone could have hacked into the website of an issuer without the issuer having yet found out. Users should be made aware of newly issued assets accordingly.

Refreshing an asset

Alongside each asset whose balance is displayed there should be a button to refresh the information about that asset. When this button is clicked, perform the following steps:

  1. Set the validChecked for that asset in the Asset Type List to null.
  2. For unspent transaction outputs only, set the qtyChecked for that asset in the Asset Balance List to null.
  3. Trigger the asynchronous checking of asset validity.

Sending CoinSpark assets

If possible, you should combine the form for sending CoinSpark assets with your regular form for sending bitcoin. Simply add a selector for the asset type. The first option should be "BTC", which sends a regular bitcoin transaction. The selector should then list all assets in the Asset Type List for which isVisible is true and assetRef and genesis are not null. Due to the possibility of a blockchain rearrangement, it is strongly recommended not to allow assets to be sent out if there have been less than 6 blockchain confirmations since their assetRef.blockNum.

In order to send a CoinSpark asset to another wallet, the user must provide a CoinSpark address for that wallet. Do not allow users to send CoinSpark assets to a regular bitcoin address, because those assets will likely not be detected and sent on unintentionally to someone else. After decoding the CoinSpark address provided, ensure that the COINSPARK_ADDRESS_FLAG_ASSETS bit is set in the address flags, meaning that it is safe to send CoinSpark assets to the recipient. If the address contains a non-zero payment reference, you should also take note of that for later on.

Prompt the user for the quantity of the asset to be sent. This quantity must be mapped back to a number of raw asset units by reversing the formula on this page. All subsequent operations use this quantity of raw units rather than the display value.

Call the calcCharge() method on the genesis field of this asset’s entry in the Asset Type List to determine if a CoinSpark payment charge will be deducted from the amount sent. If so, let the user choose whether this charge will be paid by the sender or by the recipient. If paid by the sender, calculate the amount to be sent by calling the genesis.calcGross() and show the extra that will be paid by the sender. If paid by the recipient, calculate the amount that will be received by calling the genesis.calcNet() method and show the amount that will be deducted from the recipient. (For C/C++ use CoinSparkGenesisCalcCharge(), CoinSparkGenesisCalcNet() and CoinSparkGenesisCalcGross() respectively.) The CoinSpark payment charge should not be confused with the bitcoin transaction fee, which is always paid by the sender.

Assuming there is sufficient quantity of the CoinSpark asset in the wallet, build a bitcoin transaction as follows:

  1. Determine the current bitcoin anti-dust threshold, which we will call antiDust. As of Bitcoin Core 0.9, this is 546 satoshis, though we recommend using a value of 1000 to be safe.
  2. Create a CoinSparkTransfer with the following values:
    • Set assetRef based on this asset’s entry in the Asset Type List.
    • Set inputs.first to 0 and inputs.count to 1 (this may be changed later).
    • Set outputs.first to 0 and outputs.count to 1.
    • Set qtyPerOutput to the raw number of units to be transferred.
  3. Calculate the minimum CoinSpark transaction fee, which we will call minCoinSparkFee, by creating a CoinSparkTransferList containing this single CoinSparkTransfer from above and calling its calcMinFee() method. (For C/C++, use CoinSparkTransfersCalcMinFee() instead.) Use the following values for the other parameters to the call:
    • Set countInputs to 1.
    • Set countOutputs to 2 (required for C/C++ only).
    • Set outputsSatoshis to an array of two values, each of which is set to antiDust.
    • Set outputsRegular to an array of two true values.
  4. Combine the wallet’s regular list of unspent transaction outputs with the Asset Balance List to identify unspent transaction outputs which have a non-zero balance of the asset.
  5. Start collecting those unspent transaction outputs, starting with those with the most blockchain confirmations, until their total balance of the asset is sufficient to send the quantity required.
  6. Update the inputs.count field of the CoinSparkTransfer to reflect the number of collected unspent transaction outputs.
  7. Calculate the minimum bitcoin transaction fee (under bitcoin’s regular rules) required for a transaction which spends all these outputs, has two outputs of its own, plus up to 40 bytes of metadata in an OP_RETURN transaction output. We will call this minBitcoinFee. (For a simple rule of thumb, divide the number of collected unspent transaction outputs by 5, then round up to the nearest integer, then multiply by 10,000 satoshis.)
  8. Calculate the transaction fee that will be used for this transaction as finalFee=max(minCoinSparkFee,minBitcoinFee), i.e. the larger of minCoinSparkFee and minBitcoinFee.
  9. Calculate the minimum quantity of bitcoin that will be required for this transaction as minBitcoin=2*antiDust+finalFee.
  10. Sum the total quantity of bitcoin in the collected unspent transaction outputs, which we will call totalBitcoin. If totalBitcoin<minBitcoin then add more unspent transaction outputs, starting with those with the most blockchain confirmations, until totalBitcoin>=minBitcoin.
  11. Now construct a transaction which spends all of the chosen unspent transaction outputs, and has the following outputs in order:
    1. A regular send-to-address script using the bitcoin address of the chosen recipient, decoded from the CoinSpark address that was provided. This first output should contain antiDust satoshis.
    2. A regular send-to-address script sending change back to the wallet user. This second output should contain totalBitcoin-antiDust-finalFee satoshis.
    3. An OP_RETURN script encoding the transfer metadata for the single CoinSparkTransfer created. This final output should contain no bitcoin. If the CoinSpark address provided by the user encoded a non-zero payment reference, you should also encode that payment reference as metadata then call CoinSparkMetadataCombine() to combine the two pieces of metadata together before calling CoinSparkMetadataToScript().
  12. Sign the transaction and send it out to the bitcoin network.

Migrating all CoinSpark assets

As mentioned in sending regular bitcoin, it is not possible to spend all of the bitcoin in a CoinSpark wallet without losing all of the CoinSpark assets. Therefore a separate operation must be provided, in which the user deliberately sends all of their remaining bitcoin and CoinSpark assets to another wallet.

The user must provide a valid CoinSpark address to perform this operation. Do not allow users to migrate their assets to a regular bitcoin address, because those assets will likely not be detected and sent on unintentionally to someone else. After decoding the CoinSpark address provided, ensure that the COINSPARK_ADDRESS_FLAG_ASSETS bit is set in the address flags, meaning that it is safe to send CoinSpark assets to the recipient.

After confirming with the user that they are sure this wish to perform the migration, build a bitcoin transaction as follows:

  1. Collect a list of all the unspent transaction outputs available to the wallet.
  2. Sum the total quantity of bitcoin in the collected unspent transaction outputs, which we will call totalBitcoin.
  3. Calculate the minimum bitcoin transaction fee (under bitcoin’s regular rules) required for a transaction which spends all these outputs, has one output of its own, plus up to 40 bytes of metadata in an OP_RETURN transaction output. We will call this minBitcoinFee. (For a simple rule of thumb, divide the number of collected unspent transaction outputs by 5, then round up to the nearest integer, then multiply by 10,000 satoshis.)
  4. Create up to 4 CoinSparkTransfer structures/objects for up to 4 asset types with non-zero balance. Set the fields of each CoinSparkTransfer as follows:
    • Set assetRef based on this asset’s entry in the Asset Type List.
    • Set inputs.first to 0 and inputs.count to the total number of unspent transaction outputs.
    • Set outputs.first to 0 and outputs.count to 1.
    • Set qtyPerOutput to 1. This value does not matter because all units flow to the last non-OP_RETURN output by default.

    This will enable the most efficient possible encoding of these transfers in the OP_RETURN metadata, squeezing four into 40 bytes until bitcoin block numbers go above 2^24 (some time after the year 2,300) or bitcoin block sizes go above 16 MB (as of Bitcoin Core 0.9, the limit is 1 MB).

  5. Construct a transaction which spends all unspent transaction outputs available to the wallet and has the following outputs in order:
    1. A regular send-to-address script using the bitcoin address decoded from the CoinSpark address that was provided. This first output should contain totalBitcoin-minBitcoinFee satoshis.
    2. An OP_RETURN script encoding the transfer metadata for the set of up to 4 CoinSparkTransfers created. If the encoding fails, try encoding the first 3 CoinSparkTransfers only.This final output should contain no bitcoin.
  6. Sign the transaction and send it out to the bitcoin network.

If there were asset types in the wallet with non-zero balance which could not be encoded in the OP_RETURN script, the user will have to import them manually into their new CoinSpark wallet. Display the asset references for those remaining asset to the user and explain that they must be imported manually. In the event the user missed this message, they can still obtain the missing asset references from the asset list.

Asset list

A detailed report of all assets in the Asset Type List should be accessible to the user in a separate window or panel. Unlike the asset balances displayed, this list should include assets whose isVisible field is false. For each asset, all of the information show in the asset balances should be displayed, along with the following additional information:

  • When the asset was added, based on the whenAdded field.
  • An explanation of the asset’s payment charges, from the genesis field.
  • The human-readable asset reference, from the assetRef field.
  • When the asset’s validity was last checked, based on the validChecked field.
  • The size of the asset’s cached contract in its contractContent field.

In addition, this list should enable the following functions:

  • Toggle the isVisible field of a particular asset type. If an asset is made visible, the asynchronous checking of asset validity should be triggered.
  • Add an asset manually using its asset reference. When a user does this, perform the following steps:
    • Check if there is an entry in the Asset Type List whose assetRef matches asset reference provided. If so, make a note of the assetID.
    • If not, add a new entry into the Asset Type List as follows – all unspecified fields should be set to null:
      • Set assetID to a new and unique value.
      • Set whenAdded to the current date/time.
      • Set fromSource to manual.
      • Set isVisible to true.
      • Set assetRef to the asset reference provided.
      • Set validFailures to 0.
    • For each unspent bitcoin transaction output which the wallet can spend, add a new entry into the Asset Balance List as follows:
      • Set unspentTxID to the txid of the unspent bitcoin transaction output.
      • Set unspentVout to the index of the unspent output of that transaction.
      • Set assetID to the value found in or added to the Asset Type List above.
      • Set qty and qtyChecked to null.
      • Set qtyFailures to 0.
    • If an asset was added to the Asset Type List, trigger the asynchronous fetching of genesis transactions.
    • Otherwise, if an entry was added to the Asset Balance List, trigger the asynchronous checking of asset balances.

« Previous: Interface Changes