Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asset detail page clean up #253

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ terraform/.terraform
terraform/.terraform/environment
terraform/*.tfstate.backup
terraform/*.tfstate
**/.DS_Store
*~
11 changes: 10 additions & 1 deletion client/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ export default function main({ DOM, HTTP, route, storage, scanner: scan$, search
// In elements, we block rendering until the assetMap is loaded. Otherwise, we can start immediately.
, isReady$ = process.env.ASSET_MAP_URL ? assetMap$.mapTo(true).startWith(false) : O.of(true)

// Asset Icons Response
, assetIcons$ = !process.env.ASSET_ICONS_URL ? O.of({}) :
reply('asset-icons')

// Currently visible view
, view$ = O.merge(page$.mapTo(null)
, goHome$.mapTo('recentBlocks')
Expand Down Expand Up @@ -241,7 +245,7 @@ export default function main({ DOM, HTTP, route, storage, scanner: scan$, search
, mempool$, mempoolRecent$, feeEst$
, tx$, txAnalysis$, openTx$
, goAddr$, addr$, addrTxs$, addrQR$
, assetMap$, goAsset$, asset$, assetTxs$
, assetMap$, assetIcons$, goAsset$, asset$, assetTxs$
, isReady$, loading$, page$, view$, title$, theme$
})

Expand Down Expand Up @@ -334,6 +338,11 @@ export default function main({ DOM, HTTP, route, storage, scanner: scan$, search
? { category: 'asset-txs', method: 'GET', path: `/asset/${d.asset_id}/txs/chain/${last(d.last_txids)}` }
: { category: 'asset-txs', method: 'GET', path: `/asset/${d.asset_id}/txs` }])

// Fetch Asset Icons
, !process.env.ASSET_ICONS_URL ? O.empty() : O.of(
{ category: 'asset-icons', method: 'GET', path: process.env.ASSET_ICONS_URL})


// fetch more txs for asset page
, moreSTxs$.map(d => ({ category: 'asset-txs-more', method: 'GET', path: `/asset/${d.asset_id}/txs/chain/${d.last_txid}` }))
).map(setBase)
Expand Down
108 changes: 108 additions & 0 deletions client/src/views/asset-advanced-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import Snabbdom from "snabbdom-pragma";
import { formatJson, formatAssetAmount, formatSat, formatNumber } from './util'

export default (asset, is_native_asset, mempool_stats, chain_stats, t) => {

return(
<div className= "stats-table">
{ is_native_asset
// Native asset
? [
mempool_stats.peg_in_amount > 0 && <div>
<div>{t`Pegged in (unconfirmed)`}</div>
<div className="mono">{formatSat(mempool_stats.peg_in_amount)}</div>
</div>

, mempool_stats.peg_out_amount > 0 && <div>
<div>{t`Pegged out (unconfirmed)`}</div>
<div className="mono">{formatSat(mempool_stats.peg_out_amount)}</div>
</div>

, mempool_stats.burned_amount > 0 && <div>
<div>{t`Burned (unconfirmed)`}</div>
<div className="mono">{formatSat(mempool_stats.burned_amount)}</div>
</div>

, <div>
<div>{t`Peg/burn transaction count`}</div>
<div className="mono">{formatNumber(chain_stats.tx_count)}</div>
</div>

, mempool_stats.peg_out_amount > 0 && <div>
<div>{t`Peg/burn transaction count (unconfirmed)`}</div>
<div className="mono">{formatNumber(mempool_stats.tx_count)}</div>
</div>
]

// Issued assets
: [
<div>
<div>{t`Issuance transaction`}</div>
<div><a href={`tx/${asset.issuance_txin.txid}?input:${asset.issuance_txin.vin}&expand`}>{`${asset.issuance_txin.txid}:${asset.issuance_txin.vin}`}</a></div>
</div>

, <div>
<div>{t`Included in Block`}</div>
<div>{ asset.status.confirmed
? <a href={`block/${asset.status.block_hash}`} className="mono">{asset.status.block_hash}</a>
: t`Unconfirmed`
}</div>
</div>

, asset.contract_hash && <div>
<div>{t`Contract hash`}</div>
<div className="mono">{asset.contract_hash}</div>
</div>

, asset.contract && <div>
<div>{t`Contract JSON`}</div>
<div className="mono contract-json">{formatJson(asset.contract)}</div>
</div>

, <div>
<div>{t`Decimal places`}</div>
<div>{asset.precision || 0}</div>
</div>

, <div>
<div>{t`Number of issuances`}</div>
<div>{chain_stats.issuance_count}</div>
</div>

, mempool_stats.issuance_count > 0 && <div>
<div>{t`Number of issuances (unconfirmed)`}</div>
<div>{mempool_stats.issuance_count}</div>
</div>

, mempool_stats.issued_amount > 0 && <div>
<div>{t`Issued amount (unconfirmed)`}</div>
<div>{formatAssetAmount(mempool_stats.issued_amount, asset.precision, t)}</div>
</div>

, mempool_stats.burned_amount > 0 && <div>
<div>{t`Burned amount (unconfirmed)`}</div>
<div>{formatAssetAmount(mempool_stats.burned_amount, asset.precision, t)}</div>
</div>

, <div>
<div>{t`Reissuance tokens created`}</div>
<div>{chain_stats.reissuance_tokens == null ? t`Confidential`
: chain_stats.reissuance_tokens === 0 ? t`None`
: formatNumber(chain_stats.reissuance_tokens) }</div>
</div>

, chain_stats.burned_reissuance_tokens > 0 && <div>
<div>{t`Reissuance tokens burned`}</div>
<div>{formatNumber(chain_stats.burned_reissuance_tokens)}</div>
</div>

, mempool_stats.burned_reissuance_tokens > 0 && <div>
<div>{t`Reissuance tokens burned (unconfirmed)`}</div>
<div>{formatNumber(mempool_stats.burned_reissuance_tokens)}</div>
</div>

]
}
</div>
)
}
70 changes: 70 additions & 0 deletions client/src/views/asset-issuance-history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Snabbdom from "snabbdom-pragma";
import { formatTime, formatAssetAmount } from './util'

export default (assetTxs, chain_stats, asset, t) => {

const getCalculatedAssetTx = () => {
// Supply Starts at Zero
let supply = 0
// Sort AssetTxs by block_time and Calculate Supply Change / Total Supply
assetTxs.sort((a,b) => (a.status.block_time > b.status.block_time) ? 1 : ((b.status.block_time > a.status.block_time) ? -1 : 0))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The transactions are already sorted with newest first, if you want oldest first you can just reverse() it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried "assetTxs.reverse()" It wasn't working that's why I used ".sort"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't working how? It didn't reverse the array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It didn't reverse the array. I tried console.log(assetTxs) and assetTxs.reverse() got the same array. No changes. :(

let calAssetTx = assetTxs.map(tx => {
let calTxObj = {}
calTxObj.txid = tx.txid
calTxObj.block_height = tx.status.block_height
calTxObj.block_time = formatTime(tx.status.block_time)

// Find all Issuance and add them to Supply
tx.vin.forEach(vinTx => {
if("issuance" in vinTx && vinTx.issuance.asset_id === asset.asset_id){
calTxObj.supplyChange = chain_stats.has_blinded_issuances ? t`Confidential` : `+ ${formatAssetAmount((vinTx.issuance.assetamount), asset.precision, t).text}`
calTxObj.totalSupply = chain_stats.has_blinded_issuances ? t`Confidential` : formatAssetAmount((supply + vinTx.issuance.assetamount), asset.precision, t)
supply = (supply + vinTx.issuance.assetamount)
}
})

// Find all Burn Transactions and Remove them from Supply
tx.vout.forEach(voutTx => {
if(voutTx.scriptpubkey_type === "op_return" && voutTx.asset === asset.asset_id){
if(voutTx.value > 0){
calTxObj.supplyChange = chain_stats.has_blinded_issuances ? t`Confidential` : `- ${formatAssetAmount((voutTx.value), asset.precision, t).text}`
calTxObj.totalSupply = chain_stats.has_blinded_issuances ? t`Confidential` : formatAssetAmount((supply - voutTx.value), asset.precision, t)
supply = (supply - voutTx.value)
}
}
})
return calTxObj
})

// Reverse Calculated AssetTx to Display Newest to Older
return calAssetTx.reverse()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could consider deriving calculatedAssetTx as a map() over assetTxs that transforms them into calTxObjs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made


let calculatedAssetTx = assetTxs === null ? "" : getCalculatedAssetTx()


return(
<div>
{ assetTxs === null ? <p>{t`No Issuance History`}</p>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A null here would mean that the transactions haven't loaded yet, an empty array would indicate that there are no transactions (which shouldn't really be possible if the asset exists).

Better to show nothing (or more ideally, a loading indicator) rather than the user seeing "No Issuance History" flashing for a moment and disappearing.

: <div className="assets-table">
<div className="assets-table-row header">
<div className="assets-table-cell">{t`Block`}</div>
<div className="assets-table-cell">{t`Time`}</div>
<div className="assets-table-cell right-align">{t`Supply Change`}</div>
<div className="assets-table-cell right-align">{t`Total Supply`}</div>
</div>
{calculatedAssetTx.map(tx =>
<div className="assets-table-link-row">
<a className="assets-table-row asset-data" href={`tx/${tx.txid}`}>
<div className="assets-table-cell highlighted-text" data-label={t`Block`}>{tx.block_height}</div>
<div className="assets-table-cell" data-label={t`Time`}>{tx.block_time}</div>
<div className="assets-table-cell right-align asset-id" data-label={t`Supply Change`}>{tx.supplyChange}</div>
<div className="assets-table-cell right-align" data-label={t`Total Supply`}>{tx.totalSupply}</div>
</a>
</div>
)}
</div>
}
</div>
)
}
12 changes: 10 additions & 2 deletions client/src/views/asset-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Snabbdom from 'snabbdom-pragma'

import layout from './layout'

export default ({ assetMap, t, ...S }) => {
export default ({ assetMap, assetIcons, t, ...S }) => {

const assets = Object.entries(assetMap)
.map(([ asset_id, [ domain, ticker, name ] ]) => ({ asset_id, domain, ticker, name }))
Expand All @@ -28,7 +28,15 @@ export default ({ assetMap, t, ...S }) => {
{assets.map(asset =>
<div className="assets-table-link-row">
<a className="assets-table-row asset-data" href={`asset/${asset.asset_id}`}>
<div className="assets-table-cell" data-label={t`Name`}>{asset.name}</div>
<div className="assets-table-cell" data-label={t`Name`}>
<div className="assets-table-name">
{assetIcons === null ? "" :
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it okay that the asset-icon-placeholder won't be added to the DOM while the icons are loading?

assetIcons[`${asset.asset_id}`] === undefined ?
<div className="asset-icon-placeholder"></div> :
<img src={`data:image/png;base64,${assetIcons[`${asset.asset_id}`]}`} className="asset-icon"/>}
<span>{asset.name}</span>
</div>
</div>
<div className="assets-table-cell ticker" data-label={t`Ticker`}>{asset.ticker || <em>None</em>}</div>
<div className="assets-table-cell" data-label={t`Issuer domain`}>{asset.domain}</div>
<div className="assets-table-cell asset-id highlighted-text" data-label={t`Asset ID`}>{asset.asset_id}</div>
Expand Down
97 changes: 97 additions & 0 deletions client/src/views/asset-summary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Snabbdom from "snabbdom-pragma";
import { formatAssetAmount, formatSat } from './util'

export default (is_native_asset, asset, assetIcons, nativeAssetName,
nativeAssetLabel, entity_type, circulating, chain_stats, is_non_reissuable, t) => {
return(
<div className="asset-summary">
{ is_native_asset
// Native asset
? [
<div>
<div className="asset-logo-name">
<div className="asset-logo">
<div>
{assetIcons === null ? "" :
assetIcons[`${asset.asset_id}`] === undefined ?
<div className="asset-icon-placeholder"></div> :
<img src={`data:image/png;base64,${assetIcons[`${asset.asset_id}`]}`} className="asset-icon"/>}
</div>
</div>
<div className="asset-name">{nativeAssetName}<br/><span>{nativeAssetLabel}</span></div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Asset ID`)}</div>
<div className="asset-text">{asset.asset_id}
{ process.browser && <div className="code-button">
<div className="code-button-btn" role="button" data-clipboardCopy={asset.asset_id}></div>
</div> }
</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Total Active Supply`)}</div>
<div className="asset-text">{formatSat(circulating)}</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Total Amount Burned`)}</div>
<div className="asset-text">{formatSat(chain_stats.burned_amount)}</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t`Pegged in`}</div>
<div className="asset-text">{formatSat(chain_stats.peg_in_amount)}</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t`Pegged out`}</div>
<div className="asset-text">{formatSat(chain_stats.peg_out_amount)}</div>
</div>
</div>
]
// Issued assets
: [
<div>
<div className="asset-logo-name">
<div className="asset-logo">
<div>
{assetIcons === null ? "" :
assetIcons[`${asset.asset_id}`] === undefined ?
<div className="asset-icon-placeholder"></div> :
<img src={`data:image/png;base64,${assetIcons[`${asset.asset_id}`]}`} className="asset-icon"/>}
</div>
</div>
<div className="asset-name">{asset.name}<br/><span>{asset.ticker}</span></div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Issuer`)}</div>
<div className="asset-text">{asset.entity[entity_type]}</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Asset ID`)}</div>
<div className="asset-text">{asset.asset_id}
{ process.browser && <div className="code-button">
<div className="code-button-btn" role="button" data-clipboardCopy={asset.asset_id}></div>
</div> }
</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Total Active Supply`)}</div>
<div className="asset-text">{ circulating == null ? t`Confidential`
: formatAssetAmount(circulating, asset.precision, t) }</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Total Amount Burned`)}</div>
<div className="asset-text">{formatAssetAmount(chain_stats.burned_amount, asset.precision, t)}</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Reissuable`)}</div>
<div className="asset-text">{ is_non_reissuable ? t`No` : t`Yes` }</div>
</div>
<div className="asset-label-text">
<div className="asset-label">{t(`Reissuable Token for Asset`)}</div>
<div className="asset-text">{asset.reissuance_token}</div>
</div>
</div>
]
}
</div>
)
}
Loading