Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

bills: make keeps contract details live from keeps-config

+78 -14
+78 -14
system/public/bills.aesthetic.computer/index.html
··· 762 762 <div class="stat-box"> 763 763 <div class="stat-label">fees earned</div> 764 764 <div class="stat-value loading" id="keeps-fees" style="color: var(--green);">...</div> 765 - <div class="stat-sub">2.5 XTZ / mint</div> 765 + <div class="stat-sub" id="keeps-fees-sub">2.5 XTZ / mint</div> 766 766 </div> 767 767 <div class="stat-box"> 768 768 <div class="stat-label">minted</div> ··· 780 780 <table> 781 781 <tbody> 782 782 <tr><td>contract</td><td><code id="keeps-contract">...</code></td></tr> 783 - <tr><td>network</td><td>Tezos mainnet</td></tr> 783 + <tr><td>network</td><td id="keeps-network">...</td></tr> 784 + <tr><td>profile/version</td><td id="keeps-profile">...</td></tr> 785 + <tr><td>config source</td><td id="keeps-source">...</td></tr> 784 786 <tr><td>mint fee</td><td>2.5 XTZ</td></tr> 785 787 <tr><td>royalties</td><td>10% (9% artist, 1% platform)</td></tr> 786 - <tr><td>marketplace</td><td><a id="keeps-objkt-link" href="#">view on objkt.com</a></td></tr> 788 + <tr><td>marketplace</td><td><a id="keeps-objkt-link" href="#" target="_blank" rel="noopener">view on objkt.com</a></td></tr> 789 + <tr><td>explorer</td><td><a id="keeps-tzkt-link" href="#" target="_blank" rel="noopener">view on tzkt.io</a></td></tr> 787 790 </tbody> 788 791 </table> 789 792 </div> ··· 1091 1094 // LIVE DATA: Keeps (Tezos via TzKT API) 1092 1095 // ═══════════════════════════════════════════════════════════ 1093 1096 1094 - const KEEPS_CONTRACT = 'KT1J15kADMuRWh9kJZzosBeRBYPjYr7RvhoN'; 1097 + const DEFAULT_KEEPS_CONTRACT = 'KT1Q1irsjSZ7EfUN4qHzAB2t7xLBPsAWYwBB'; 1098 + const KEEPS_CONFIG_URL = '/api/keeps-config?network=mainnet'; 1099 + const KEEPS_CONFIG_FALLBACK_URL = 'https://aesthetic.computer/api/keeps-config?network=mainnet'; 1095 1100 const TZKT_API = 'https://api.tzkt.io'; 1096 1101 const KEEPS_FEE_XTZ = 2.5; 1097 1102 1098 - document.getElementById('keeps-contract').textContent = KEEPS_CONTRACT; 1099 - document.getElementById('keeps-objkt-link').href = 1100 - 'https://objkt.com/collection/' + KEEPS_CONTRACT; 1101 - 1102 1103 (async function loadKeeps() { 1103 1104 try { 1105 + const keepsContractEl = document.getElementById('keeps-contract'); 1106 + const keepsNetworkEl = document.getElementById('keeps-network'); 1107 + const keepsProfileEl = document.getElementById('keeps-profile'); 1108 + const keepsSourceEl = document.getElementById('keeps-source'); 1109 + const keepsObjktLinkEl = document.getElementById('keeps-objkt-link'); 1110 + const keepsTzktLinkEl = document.getElementById('keeps-tzkt-link'); 1111 + 1112 + let keepsContract = DEFAULT_KEEPS_CONTRACT; 1113 + let keepsNetwork = 'mainnet'; 1114 + let keepsProfile = 'unknown'; 1115 + let keepsVersion = ''; 1116 + let keepsSource = 'fallback'; 1117 + let objktUrl = 'https://objkt.com/collections/' + keepsContract; 1118 + let tzktUrl = 'https://tzkt.io/' + keepsContract; 1119 + 1120 + try { 1121 + let configRes = await fetch(KEEPS_CONFIG_URL); 1122 + if (!configRes.ok) configRes = await fetch(KEEPS_CONFIG_FALLBACK_URL); 1123 + if (configRes.ok) { 1124 + const config = await configRes.json(); 1125 + if (config?.contractAddress) keepsContract = config.contractAddress; 1126 + if (config?.network) keepsNetwork = config.network; 1127 + if (config?.profile) keepsProfile = config.profile; 1128 + if (config?.version) keepsVersion = config.version; 1129 + if (config?.source) keepsSource = config.source; 1130 + if (config?.objktCollectionUrl) objktUrl = config.objktCollectionUrl; 1131 + if (config?.tzktContractUrl) tzktUrl = config.tzktContractUrl; 1132 + } 1133 + } catch (e) { 1134 + // fall back to default contract 1135 + } 1136 + 1137 + objktUrl = objktUrl.includes('/collection/') || objktUrl.includes('/collections/') 1138 + ? objktUrl 1139 + : ('https://objkt.com/collections/' + keepsContract); 1140 + tzktUrl = tzktUrl || ('https://tzkt.io/' + keepsContract); 1141 + 1142 + if (keepsContractEl) keepsContractEl.textContent = keepsContract; 1143 + if (keepsNetworkEl) keepsNetworkEl.textContent = `Tezos ${keepsNetwork}`; 1144 + if (keepsProfileEl) keepsProfileEl.textContent = keepsVersion ? `${keepsProfile} · ${keepsVersion}` : keepsProfile; 1145 + if (keepsSourceEl) keepsSourceEl.textContent = keepsSource; 1146 + if (keepsObjktLinkEl) keepsObjktLinkEl.href = objktUrl; 1147 + if (keepsTzktLinkEl) keepsTzktLinkEl.href = tzktUrl; 1148 + 1104 1149 // Fetch contract storage for token count 1105 - const storageRes = await fetch(TZKT_API + '/v1/contracts/' + KEEPS_CONTRACT + '/storage'); 1150 + const storageRes = await fetch(TZKT_API + '/v1/contracts/' + keepsContract + '/storage'); 1106 1151 const storage = storageRes.ok ? await storageRes.json() : null; 1107 1152 1108 1153 // Fetch token holders 1109 - const holdersRes = await fetch(TZKT_API + '/v1/tokens?contract=' + KEEPS_CONTRACT + '&limit=1000'); 1154 + const holdersRes = await fetch(TZKT_API + '/v1/tokens?contract=' + keepsContract + '&limit=1000'); 1110 1155 const tokens = holdersRes.ok ? await holdersRes.json() : []; 1111 1156 1112 1157 // Count unique owners from token holders 1113 - const ownersRes = await fetch(TZKT_API + '/v1/tokens/balances?token.contract=' + KEEPS_CONTRACT + '&balance.gt=0&limit=1000'); 1158 + const ownersRes = await fetch(TZKT_API + '/v1/tokens/balances?token.contract=' + keepsContract + '&balance.gt=0&limit=1000'); 1114 1159 const balances = ownersRes.ok ? await ownersRes.json() : []; 1115 1160 const uniqueOwners = new Set(balances.map(b => b.account?.address)).size; 1116 1161 ··· 1131 1176 } catch (e) { /* use fallback */ } 1132 1177 1133 1178 // Fetch total transfer volume 1134 - const transfersRes = await fetch(TZKT_API + '/v1/operations/transactions?target=' + KEEPS_CONTRACT + '&entrypoint=keep&limit=1000&select=amount'); 1179 + const transfersRes = await fetch(TZKT_API + '/v1/operations/transactions?target=' + keepsContract + '&entrypoint=keep&limit=1000&select=amount'); 1135 1180 let totalVolumeXTZ = feesXTZ; // At minimum, minting fees 1136 1181 if (transfersRes.ok) { 1137 1182 const amounts = await transfersRes.json(); 1138 1183 totalVolumeXTZ = amounts.reduce((sum, a) => sum + (a / 1000000), 0); 1139 1184 } 1140 1185 1141 - const feesUSD = Math.round(feesXTZ * xtzPrice); 1142 - keepsIncomeUSD = feesUSD; // Monthly approximation (project is ~1 month old) 1186 + // Monthly estimate from mints in the last 30 days 1187 + let monthlyMints = 0; 1188 + try { 1189 + const start30d = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); 1190 + const mintTransfersRes = await fetch( 1191 + TZKT_API + '/v1/tokens/transfers?token.contract=' + keepsContract + 1192 + '&timestamp.ge=' + encodeURIComponent(start30d) + 1193 + '&limit=1000' 1194 + ); 1195 + if (mintTransfersRes.ok) { 1196 + const transfers = await mintTransfersRes.json(); 1197 + monthlyMints = transfers.filter(t => t.from == null).length; 1198 + } 1199 + } catch (e) { /* keep fallback below */ } 1200 + 1201 + const monthlyFeesXTZ = monthlyMints * KEEPS_FEE_XTZ; 1202 + keepsIncomeUSD = Math.round(monthlyFeesXTZ * xtzPrice); 1203 + if (monthlyMints > 0) { 1204 + const keepsFeesSub = document.getElementById('keeps-fees-sub'); 1205 + if (keepsFeesSub) keepsFeesSub.textContent = `2.5 XTZ / mint · ${monthlyMints} mints in last 30d`; 1206 + } 1143 1207 1144 1208 document.getElementById('keeps-volume').textContent = 1145 1209 Math.round(totalVolumeXTZ) + ' XTZ';