Skip to content
Merged
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
27 changes: 18 additions & 9 deletions apps/web/src/app/[lang]/[asset]/_components/activity-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { createTradeHistoryReader, tradeFingerprint } from "@/lib/api/trade-hist
import { xcpOrderRemaining } from "@/lib/order-legs";
import { tradeRoleForAddress } from "@launchpad/xcp69/trades";
import {
BURNED_ROW,
withBurnedRow,
currentHolderCount,
includeFormerHolders,
poolLockStatus,
Expand Down Expand Up @@ -102,8 +104,10 @@ export function ActivityTabs({
poolXcpRaw,
poolTokensRaw,
lpAsset,
burnedQuantity = "0",
}: {
asset: string;
burnedQuantity?: Raw;
mints: Fairmint[];
divisible: boolean;
/** During the sale there is no market yet: no trades, no book. */
Expand Down Expand Up @@ -221,14 +225,14 @@ export function ActivityTabs({
// Counterparty's balance endpoint is a live snapshot, not holder history.
// Restore absent minters/traders at zero so the table can show who sold out,
// while the tab/header/card counts above remain strictly current balances.
const holderHistory = includeFormerHolders(
const holderHistory = withBurnedRow(includeFormerHolders(
holders ?? [],
[
...mints.map((mint) => mint.source),
...(roomState?.trades ?? []).map((trade) => trade.address),
...(trades ?? []).map((trade) => trade.addr),
],
);
), BURN_ADDRESS, burnedQuantity);
// One row: the pool is a single concentration of supply, and how much of it
// is locked is a property OF that row, not a second holder.
const poolLock = poolLockStatus(
Expand All @@ -245,11 +249,11 @@ export function ActivityTabs({
: holderHistory;
const holderBalance = new Map(
holderRows
.filter((row) => row.address !== POOL_ROW)
.filter((row) => row.address !== POOL_ROW && row.address !== BURNED_ROW)
.map((row) => [row.address, row.quantity]),
);
// Shares are of circulating PLUS the locked pool, which is what makes them
// read as shares of supply rather than of whatever is left over.
// Display shares include the pool and removed tokens once, so destruction
// doesn't make the remaining holders' shares of this distribution inflate.
const holderTotal = sumRaw(holderRows.map((h) => h.quantity));

/**
Expand Down Expand Up @@ -454,7 +458,7 @@ export function ActivityTabs({
tab === "minters"
? minters.slice(from, from + PER_PAGE).map((r) => r.source)
: tab === "holders"
? holderRows.slice(from, from + PER_PAGE).map((h) => h.address)
? holderRows.slice(from, from + PER_PAGE).filter(h => h.address !== POOL_ROW && h.address !== BURNED_ROW).map((h) => h.address)
: [],
);

Expand Down Expand Up @@ -837,6 +841,7 @@ export function ActivityTabs({
{holderRows.slice(from, from + PER_PAGE).map((h, i) => {
const pct = ratio(h.quantity, holderTotal) * 100;
const isPool = h.address === POOL_ROW;
const isBurned = h.address === BURNED_ROW;
const isUtxo = h.address.startsWith("utxo:");
const soldOut = h.quantity === 0n;
const displayedQuantity = tokenQty(h.quantity, divisible);
Expand All @@ -858,7 +863,11 @@ export function ActivityTabs({
<span className="w-8 text-right text-xs text-gray-400 dark:text-gray-500">
{from + i + 1}
</span>
{isPool ? (
{isBurned ? (
<span className="font-sans font-medium text-orange-600 dark:text-orange-400" title={t("Includes tokens sent to the burn address and explicitly destroyed tokens.")}>
<span aria-hidden>🔥 </span>{t("Burned")}
</span>
) : isPool ? (
<>
{/* The chip follows the LP, not the pool. Liquidity
whose LP is burned can never be withdrawn; whatever
Expand Down Expand Up @@ -923,11 +932,11 @@ export function ActivityTabs({
doesn't mean anything for a holder who bought in
on the market; only the issuer's own row is a
fact worth flagging post-graduation. */}
<AddressBadges
{!isBurned && <AddressBadges
address={h.address}
issuerSource={issuerSource}
collections={collections?.get(h.address)}
/>
/>}
</span>
<span className="relative z-10 ml-auto shrink-0 whitespace-nowrap text-gray-900 dark:text-gray-100">
{soldOut ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ export function LaunchView({
<LaunchRoomProvider asset={asset} fairminterTxHash={fm.tx_hash} enabled>
<ActivityTabs
asset={asset}
burnedQuantity={burnedQuantity}
mints={mints}
divisible={fm.divisible}
issuerSource={fm.source}
Expand Down
13 changes: 12 additions & 1 deletion apps/web/src/lib/holders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ export interface HolderRow {
quantity: bigint;
}

export const BURNED_ROW = "__burned__";

/** The indexed total already combines confirmed burn sends and destructions.
* Replace the burn-address balance; adding it would count sends twice. */
export function withBurnedRow(rows: HolderRow[], burnAddress: string, burnedQuantity: Raw): HolderRow[] {
const result = rows.filter(row => row.address !== burnAddress && row.address !== BURNED_ROW);
const quantity = big(burnedQuantity);
if (quantity > 0n) result.push({ address: BURNED_ROW, quantity });
return result.sort((a, b) => compareRawDesc(a.quantity, b.quantity));
}

/**
* One row per ownership location, including locations whose live balance is
* zero. Counterparty can return an address more than once (for example an
Expand All @@ -37,7 +48,7 @@ export function currentHolderCount(
rows: HolderRow[],
excludedAddresses: Iterable<string> = [],
): number {
const excluded = new Set(excludedAddresses);
const excluded = new Set([BURNED_ROW, ...excludedAddresses]);
return rows.filter((row) => row.quantity > 0n && !excluded.has(row.address)).length;
}

Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/locales/_source.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,9 @@
"burn address": [
"app/[lang]/[asset]/_components/address-badges.tsx"
],
"Burned": [
"app/[lang]/[asset]/_components/activity-tabs.tsx"
],
"Burner": [
"app/[lang]/activity/_components/activity-view.tsx"
],
Expand Down Expand Up @@ -1983,6 +1986,9 @@
"In XCP terms, exactly. In fiat terms, only if XCP's price held during the week-long window. Refunds return quantity, not value.": [
"app/[lang]/faq/page.tsx"
],
"Includes tokens sent to the burn address and explicitly destroyed tokens.": [
"app/[lang]/[asset]/_components/activity-tabs.tsx"
],
"Inscribe the image on-chain.": [
"app/[lang]/create/page.tsx"
],
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "Terminar de lanzar {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "El commit de la inscripción de {asset} está transmitido, pero su reveal no. El lanzamiento solo existe cuando el reveal está en la cadena, y hasta entonces el BTC del commit espera en una salida que solo ese reveal puede gastar.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "El commit de la inscripción de {asset} nunca llegó a la cadena, y una de las monedas que gasta ya la gastó otra transacción confirmada, así que nunca llegará. Nada de su BTC espera un reveal, y puedes empezar un lanzamiento nuevo.",
"No expiry": "Sin vencimiento"
"No expiry": "Sin vencimiento",
"Burned": "Quemado",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "Incluye los tokens enviados a la dirección de quema y los tokens destruidos explícitamente."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "Terminer le lancement de {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "Le commit de l'inscription de {asset} est diffusé, mais pas son reveal. Le lancement n'existe qu'une fois le reveal on-chain ; d'ici là, les BTC du commit attendent dans une sortie que seul ce reveal peut dépenser.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "Le commit de l'inscription de {asset} n'est jamais arrivé on-chain, et une des pièces qu'il dépense a depuis été dépensée par une autre transaction confirmée : il n'y arrivera donc jamais. Aucun de ses BTC n'attend de reveal, et vous pouvez commencer un nouveau lancement.",
"No expiry": "Sans expiration"
"No expiry": "Sans expiration",
"Burned": "Brûlés",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "Comprend les tokens envoyés à l’adresse de destruction et les tokens explicitement détruits."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "{asset} のローンチを完了",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "{asset} の刻印のコミットはブロードキャスト済みですが、リビールはまだです。ローンチはリビールがオンチェーンになって初めて成立し、それまでコミットの BTC は、そのリビールだけが使える出力で待機します。",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "{asset} の刻印のコミットはオンチェーンに届かず、それが使うコインの一つはその後、別の承認済みトランザクションで使われたため、今後も届くことはありません。リビールを待っている BTC はなく、新しいローンチを始められます。",
"No expiry": "有効期限なし"
"No expiry": "有効期限なし",
"Burned": "バーン済み",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "バーンアドレスに送信されたトークンと、明示的に破棄されたトークンを含みます。"
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "{asset} 런칭 마치기",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "{asset} 인스크립션의 커밋은 전송되었지만 리빌은 아직입니다. 런칭은 리빌이 온체인에 올라가야 성립하며, 그때까지 커밋의 BTC는 그 리빌만 사용할 수 있는 출력에 머뭅니다.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "{asset} 인스크립션의 커밋은 온체인에 올라가지 못했고, 커밋이 사용하는 코인 하나가 이후 다른 확정된 트랜잭션에서 사용되어 앞으로도 올라갈 수 없습니다. 리빌을 기다리는 BTC는 없으며, 새 런칭을 시작할 수 있습니다.",
"No expiry": "만료 없음"
"No expiry": "만료 없음",
"Burned": "소각됨",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "소각 주소로 전송된 토큰과 명시적으로 파기된 토큰을 포함합니다."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "Concluir o lançamento de {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "O commit da inscrição de {asset} foi transmitido, mas o reveal não. O lançamento só existe quando o reveal está on-chain e, até lá, o BTC do commit espera numa saída que só esse reveal pode gastar.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "O commit da inscrição de {asset} nunca chegou à chain, e uma das moedas que ele gasta já foi gasta por outra transação confirmada, então nunca vai chegar. Nenhum BTC dele espera um reveal, e você pode começar um novo lançamento.",
"No expiry": "Sem expiração"
"No expiry": "Sem expiração",
"Burned": "Queimados",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "Inclui tokens enviados ao endereço de queima e tokens explicitamente destruídos."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "Завершить запуск {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "Commit записи {asset} отправлен в сеть, а reveal — нет. Запуск появляется только когда reveal в блокчейне, а до тех пор BTC из commit ждут в выходе, который может потратить только этот reveal.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "Commit записи {asset} так и не попал в блокчейн, а одну из монет, которые он тратит, уже потратила другая подтверждённая транзакция, так что он туда никогда не попадёт. Никакие его BTC не ждут reveal, и можно начать новый запуск.",
"No expiry": "Бессрочно"
"No expiry": "Бессрочно",
"Burned": "Сожжено",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "Включает токены, отправленные на адрес сжигания, и токены, уничтоженные явно."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "Завершити запуск {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "Commit інскрипції {asset} надіслано в мережу, а reveal — ні. Запуск з’являється лише тоді, коли reveal у блокчейні, а доти BTC з commit чекають у виході, який може витратити лише цей reveal.",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "Commit інскрипції {asset} так і не потрапив у блокчейн, а одну з монет, які він витрачає, уже витратила інша підтверджена транзакція, тож він туди ніколи не потрапить. Жодні його BTC не чекають на reveal, і можна почати новий запуск.",
"No expiry": "Безстроково"
"No expiry": "Безстроково",
"Burned": "Спалено",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "Включає токени, надіслані на адресу спалювання, і токени, знищені явно."
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/zh-hk.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "完成發射 {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "{asset} 銘刻的 commit 已廣播,但 reveal 尚未廣播。reveal 上鏈後發射才算成立;在此之前,commit 的 BTC 會停留在只有該 reveal 才能花費的輸出中。",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "{asset} 銘刻的 commit 始終沒有上鏈,而它花費的一枚幣已被另一筆已確認的交易花掉,所以它永遠不會上鏈。它的 BTC 沒有在等待 reveal,你可以開始新的發射。",
"No expiry": "永不過期"
"No expiry": "永不過期",
"Burned": "已銷毀",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括傳送到銷毀地址的代幣及透過銷毀交易銷毀的代幣。"
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "完成發射 {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "{asset} 銘刻的 commit 已廣播,但 reveal 尚未廣播。reveal 上鏈後發射才算成立;在此之前,commit 的 BTC 會停留在只有該 reveal 才能花費的輸出中。",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "{asset} 銘刻的 commit 始終沒有上鏈,而它花費的一枚幣已被另一筆已確認的交易花掉,所以它永遠不會上鏈。它的 BTC 沒有在等待 reveal,你可以開始新的發射。",
"No expiry": "永不過期"
"No expiry": "永不過期",
"Burned": "已銷毀",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括傳送到銷毀地址的代幣和透過銷毀交易銷毀的代幣。"
}
4 changes: 3 additions & 1 deletion apps/web/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -1295,5 +1295,7 @@
"Finish launching {asset}": "完成发射 {asset}",
"The inscription commit for {asset} is broadcast, but its reveal is not. The launch exists only once the reveal is on-chain, and until then the commit's BTC waits in an output only that reveal can spend.": "{asset} 铭刻的 commit 已广播,但 reveal 尚未广播。reveal 上链后发射才算成立;在此之前,commit 的 BTC 会停留在只有该 reveal 才能花费的输出中。",
"The inscription commit for {asset} never made it on-chain, and a coin it spends has since been spent by another confirmed transaction, so it never will. None of its BTC is waiting on a reveal, and you can start a new launch.": "{asset} 铭刻的 commit 始终没有上链,而它花费的一枚币已被另一笔已确认的交易花掉,所以它永远不会上链。它的 BTC 没有在等待 reveal,你可以开始新的发射。",
"No expiry": "永不过期"
"No expiry": "永不过期",
"Burned": "已销毁",
"Includes tokens sent to the burn address and explicitly destroyed tokens.": "包括发送到销毁地址的代币和通过销毁交易销毁的代币。"
}
Loading
Loading