# Kaspa桌面插件(iOS)5.14 **Published by:** [Dodo](https://paragraph.com/@dodo-3/) **Published on:** 2023-05-10 **URL:** https://paragraph.com/@dodo-3/kaspa-ios-5-14 ## Content 5.14更新⬆️显示效果⬆️//使用方法: // 1、手机上下载安装Scriptable;应用商店地址可复制去打开或者直接去搜https://apps.apple.com/cn/app/scriptable/id1405459188 // 2、手机桌面空白出长按,引出左上角+号,点添加; // 3、出现的界面里下拉找到刚刚安装的Scriptable,选中后右滑到最大的显示效果,按下面添加小组件; // 4、进入Scriptable,选择左上角或者右上角“+”号将本文全部复制进去,随后按右上角Done; // 5、长按刚刚添加到桌面的小工具选择刚刚保存的脚本,等一会儿就可以显示了; //⚠️⚠️⚠️需科学上网环境,后面应该会考虑设置Server,我也在等rust😂 //你们要是想投喂也是可以的,一块两块不嫌少 十万八万不嫌多 🍺 kaspa:qpz2zwxd4krj8ju3q9nueu5yc7hjld0z6c7ythrrdd0k3awy3td2c2hzvslke 👈❤️ //另外有丰富前端经验的欢迎拿去砸碎重来只要做好记得发我用 //👇你的钱包地址👇 const walletAddresses = [ "kaspa:qqetp7ct8kqss99fxmymyz5t3fezppxp0t58wl6pawp27elqd46uudme00cl0", "kaspa:qpjw6xx9x5dv90ju68msey9p2s87efqk7segu9k4a4lrr024qtthukum5kgyy", "kaspa:qpwxu5fwzj8etlngl5kyvcpx8yk3jkqwrtvga6f33xe97f4x5kvq6z8swne8f", // 可添加多个钱包地址,建议不超过三条 ]; //👆根据上面格式,在双引号内填入钱包地址,考虑到合并交易特性,每次合并之后你可能需要在这里更新你的钱包地址获取最新余额 const apiDomain = "https://api.kaspa.org"; const coingeckoDomain = "https://api.coingecko.com"; const apiUrls = { blockreward: `${apiDomain}/info/blockreward`, perGHreward: "https://api.minerstat.com/v2/coins?list=KAS", price: `${coingeckoDomain}/api/v3/simple/price?ids=kaspa&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&precision=6`, hashrate: `${apiDomain}/info/hashrate`, coinsupply: `${apiDomain}/info/coinsupply`, //balance: `${apiDomain}/addresses/%walletAddress%/balance`, halving: `${apiDomain}/info/halving`, marketCapRank: `${coingeckoDomain}/api/v3/coins/kaspa?tickers=false&market_data=true&community_data=false&developer_data=false&sparkline=false` }; async function fetchData(url) { const response = await new Request(url).loadString(); return JSON.parse(response); } const blockrewardData = await fetchData(apiUrls.blockreward); const priceData = (await fetchData(apiUrls.price)).kaspa; const hashrateData = await fetchData(apiUrls.hashrate); const coinsupplyData = await fetchData(apiUrls.coinsupply); //const balanceData = await fetchData(apiUrls.balance); const perGHrewardData = (await fetchData(apiUrls.perGHreward))[0]; const halvingData = await fetchData(apiUrls.halving); const marketCapRankData = await fetchData(apiUrls.marketCapRank); const marketCapRank = marketCapRankData.market_cap_rank; const KAS_SYMBOL = "𐤊"; const widget = new ListWidget(); widget.setPadding(16, 0, 0, 0); const gradient = new LinearGradient(); gradient.locations = [0, 1]; gradient.colors = [ new Color("#70dabf"), new Color("#87ebc8") ]; widget.backgroundGradient = gradient; //title const titleBox = widget.addStack(); titleBox.setPadding(0, 16, 0, 16); const title = titleBox.addText(" Kaspa"); title.textColor = new Color("#fff"); title.font = new Font("HelveticaNeue-Bold", 25); titleBox.addSpacer(null); const blockrewardBox = titleBox.addStack(); blockrewardBox.layoutVertically(); blockrewardBox.spacing = 6; const blockrewardTitle = blockrewardBox.addText(" 区块奖励"); blockrewardTitle.textColor = new Color("#fff"); blockrewardTitle.font = Font.systemFont(10); const blockreward = blockrewardBox.addText(blockrewardData.blockreward.toFixed(2).toString() + ` ${KAS_SYMBOL}`); blockreward.textColor = new Color("#fff"); blockreward.font = Font.boldSystemFont(15); const contentBox = widget.addStack(); contentBox.backgroundColor = new Color("#F4F7FA"); contentBox.cornerRadius = 18; contentBox.spacing = 15; contentBox.setPadding(16, 16, 16, 16); contentBox.layoutVertically(); //row1 const row1 = contentBox.addStack(); row1.spacing = 15; const priceBox = row1.addStack(); const priceWrap = priceBox.addStack(); priceWrap.spacing = 6; priceWrap.layoutVertically(); for (const key in priceData) { if(key === 'usd'){ const row = priceWrap.addStack(); row.spacing = 6; row.layoutVertically(); const keyText = row.addText("当前价格"); keyText.textColor = Color.gray(); keyText.font = Font.systemFont(12); const valueText = row.addText(('$ ') + priceData[key].toFixed(4).toString()); valueText.textColor = Color.black(); valueText.font = Font.boldSystemFont(14); } } //row1.addSpacer(5); const changeBox = row1.addStack(); const changeWrap = changeBox.addStack(); changeWrap.spacing = 5; changeWrap.layoutVertically(); for (const key in priceData) { if (key === 'usd_24h_change') { let row = changeWrap.addStack() row.spacing = 6 row.layoutVertically(); let keyText = row.addText("24H涨跌") keyText.textColor = Color.gray(); keyText.font = Font.systemFont(12); let value = priceData[key] let valueText if (value >= 0) { valueText = row.addText("+" + value.toFixed(2).toString() + '%' + ' ↑') valueText.textColor = Color.green() valueText.font = Font.boldSystemFont(14); } else { valueText = row.addText(value.toFixed(2).toString() + '%' + ' ↓' ) valueText.textColor = Color.red() valueText.font = Font.boldSystemFont(14); } } } //row1.addSpacer(5); const volWrap = row1.addStack(); volWrap.spacing = 6; volWrap.layoutVertically(); const volText = volWrap.addText("24H成交"); volText.textColor = Color.gray(); volText.font = Font.systemFont(12); const volValue = priceData['usd_24h_vol'] / 1e6; const volValueText = volWrap.addText('$ ' + volValue.toFixed(2).toString() + 'M'); volValueText.textColor = Color.black(); volValueText.font = Font.boldSystemFont(14); //row1.addSpacer(null); const capWrap = row1.addStack(); for (const key in priceData){ if (key === 'usd_market_cap'){ let row = row1.addStack() row.spacing = 6 row.layoutVertically() let keyText = row.addText("当前市值") keyText.textColor = Color.gray() keyText.font = Font.systemFont(12) let value = priceData[key] let valueText if (value >= 1e9) { value = value / 1e9 valueText = row.addText(value.toFixed(1).toString() + ' B') } else if (value >= 1e6) { value = value / 1e6 valueText = row.addText(value.toFixed(1).toString() + ' M') } else { valueText = row.addText(value.toFixed(2).toString() + ' USD') } valueText.textColor = Color.black() valueText.font = Font.boldSystemFont(14) } } //row2 row2 = contentBox.addStack(); row2.spacing = 15; const hashrateWrap = row2.addStack(); hashrateWrap.spacing = 6; hashrateWrap.layoutVertically(); for (const key in hashrateData) { if (key === 'hashrate') { const hashrate = Math.round(hashrateData[key]); let displayHashrate, unit; if (hashrate > 1000) { displayHashrate = (hashrate / 1000).toFixed(2); unit = ' PH/s'; } else { displayHashrate = hashrate.toFixed(2); unit = ' TH/s'; } const row = hashrateWrap.addStack(); row.spacing = 6; row.layoutVertically(); const keyText = row.addText("全网算力"); keyText.textColor = Color.gray(); keyText.font = Font.systemFont(12); const valueText = row.addText(displayHashrate.toString() + unit); valueText.textColor = Color.black(); valueText.font = Font.boldSystemFont(14); } } //row2.addSpacer(null); const perGHrewardWrap = row2.addStack(); perGHrewardWrap.spacing = 6; perGHrewardWrap.layoutVertically(); const rewardKey = 'reward'; if (perGHrewardData.hasOwnProperty(rewardKey)) { const perGHreward = perGHrewardData[rewardKey] * 1e9 * 24; let displayPerGHreward; const row = perGHrewardWrap.addStack(); row.spacing = 6; row.layoutVertically(); const keyText = row.addText("收益/日/GH"); keyText.textColor = Color.gray(); keyText.font = Font.systemFont(12); const valueText = row.addText(perGHreward.toFixed(2).toString() + ` ${KAS_SYMBOL}`); valueText.textColor = Color.black(); valueText.font = Font.boldSystemFont(14); } //row2.addSpacer(null); const supplyBox = row2.addStack(); const circulatingSupply = coinsupplyData.circulatingSupply; const maxSupply = coinsupplyData.maxSupply; const percentage = (circulatingSupply / maxSupply) * 100; const row = supplyBox.addStack(); row.spacing = 6; row.layoutVertically(); const keyText = row.addText("已挖占比"); keyText.textColor = Color.gray(''); keyText.font = Font.systemFont(12); const valueText = row.addText(`${percentage.toFixed(2)} %`); valueText.textColor = Color.black(); valueText.font = Font.boldSystemFont(14); const marketCapRankRow = row2.addStack(); marketCapRankRow.spacing = 6; marketCapRankRow.layoutVertically(); const marketCapRankKeyText = marketCapRankRow.addText(" 市值排名"); marketCapRankKeyText.textColor = Color.gray(''); marketCapRankKeyText.font = Font.systemFont(12); const marketCapRankValueText = marketCapRankRow.addText(` # ${marketCapRank}`); marketCapRankValueText.textColor = Color.black(); marketCapRankValueText.font = Font.boldSystemFont(14); //row2.addSpacer(null); //row3 const row3 = contentBox.addStack(); row3.spacing = 15; const nextHalvingTimestamp = halvingData.nextHalvingTimestamp; const nextHalvingDate = new Date(nextHalvingTimestamp * 1000); const nextHalvingAmount = halvingData.nextHalvingAmount; const now = new Date(); const msToNextHalving = nextHalvingDate - now; const daysToNextHalving = msToNextHalving / (1000 * 60 * 60 * 24); const nextHalvingWrap = row3.addStack(); nextHalvingWrap.spacing = 16; nextHalvingWrap.layoutHorizontally(); const nextHalvingDateRow = nextHalvingWrap.addStack(); nextHalvingDateRow.spacing = 6; nextHalvingDateRow.layoutVertically(); const nextHalvingDateKeyText = nextHalvingDateRow.addText("下次减产"); nextHalvingDateKeyText.textColor = Color.gray(); nextHalvingDateKeyText.font = Font.systemFont(12); const nextHalvingDateValueText = nextHalvingDateRow.addText(nextHalvingDate.toLocaleDateString()); nextHalvingDateValueText.textColor = Color.black(); nextHalvingDateValueText.font = Font.boldSystemFont(14); row3.addSpacer(null); const nextHalvingDateCountRow = nextHalvingWrap.addStack(); nextHalvingDateCountRow.spacing = 6; nextHalvingDateCountRow.layoutVertically(); const nextHalvingDateCountKeyText = nextHalvingDateCountRow.addText("距减产"); nextHalvingDateCountKeyText.textColor = Color.gray(); nextHalvingDateCountKeyText.font = Font.systemFont(12); const nextHalvingDateCountValueText = nextHalvingDateCountRow.addText(daysToNextHalving.toFixed(1).toString() + " 天"); nextHalvingDateCountValueText.textColor = Color.black(); nextHalvingDateCountValueText.font = Font.boldSystemFont(14); const daysInCycle = 30; const percentageToNextHalving = (daysToNextHalving / daysInCycle) * 100; let countdownColor; if (percentageToNextHalving >= 60) { countdownColor = Color.black(); } else if (percentageToNextHalving >= 30) { countdownColor = Color.yellow(); } else { countdownColor = Color.red(); } nextHalvingDateCountValueText.textColor = countdownColor; row3.addSpacer(null); const nextHalvingAmountRow = nextHalvingWrap.addStack(); nextHalvingAmountRow.spacing = 6; nextHalvingAmountRow.layoutVertically(); const nextHalvingAmountKeyText = nextHalvingAmountRow.addText("减产后奖励"); nextHalvingAmountKeyText.textColor = Color.gray(); nextHalvingAmountKeyText.font = Font.systemFont(12); const nextHalvingAmountValueText = nextHalvingAmountRow.addText(nextHalvingAmount.toFixed(2).toString() + ` ${KAS_SYMBOL} `); nextHalvingAmountValueText.textColor = Color.black(); nextHalvingAmountValueText.font = Font.boldSystemFont(14); //row4 const row4 = contentBox.addStack(); row4.spacing = 15; const balanceBox = row4.addStack(); balanceBox.layoutVertically(); balanceBox.spacing = 6; const time = new Date(); const balanceKeyText = balanceBox.addText("钱包余额 " + time.toLocaleDateString() + " " + time.toLocaleTimeString()); balanceKeyText.textColor = Color.gray(); balanceKeyText.font = Font.systemFont(12); balanceKeyText.spacing=6; const walletBalanceFontSize = walletAddresses.length <= 2 ? 16 : 15; const walletValueFontSize = walletAddresses.length <= 2 ? 15 : 14; for ( const walletAddress of walletAddresses) { const balanceData = await fetchData(`${apiDomain}/addresses/${encodeURIComponent(walletAddress)}/balance`); const balance = balanceData.balance / 1e8; const balanceFormatted = new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(balance ); const walletBalanceBox = balanceBox.addStack(); walletBalanceBox.spacing = 6; walletBalanceBox.layoutHorizontally(); const price = priceData.usd; const walletValue = balance * price; const walletValueFormatted = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(walletValue); let balanceEmoji; if (balance >= 1e9 && balance < 10e9) { balanceEmoji = "🔱"; } else if (balance >= 100e6 && balance < 1e9) { balanceEmoji = "🐋"; } else if (balance >= 10e6 && balance < 100e6) { balanceEmoji = "🐳"; } else if (balance >= 1e6 && balance < 10e6) { balanceEmoji = "🦈"; } else if (balance >= 100e3 && balance < 1e6) { balanceEmoji = "🐬"; } else if (balance >= 10e3 && balance < 100e3) { balanceEmoji = "🐟"; } else if (balance >= 1e3 && balance < 10e3) { balanceEmoji = "🐙"; } else if (balance >= 100 && balance < 1e3) { balanceEmoji = "🦀"; } else { balanceEmoji = "🦐"; } const walletBalanceText = walletBalanceBox.addText(`${balanceEmoji} ${balanceFormatted} 𐤊`); walletBalanceText.textColor = new Color("#70dabf"); walletBalanceText.font = new Font("Menlo-Bold", walletBalanceFontSize); const walletValueText = walletBalanceBox.addText(walletValueFormatted); walletValueText.textColor = Color.gray(); walletValueText.font = new Font("Menlo", walletValueFontSize); } // API References const apiBox = widget.addStack(); apiBox.setPadding(8, 8, 0, 0); apiBox.layoutVertically(); apiBox.addSpacer(); const apiText = apiBox.addText("/* Kaspa, CoinGecko, Minerstat API/"); apiText.textColor = new Color("#808080"); apiText.font = new Font("Menlo-Regular", 7); const verText = apiBox.addText("-更新于2023年5月14日-"); verText.textColor = new Color("#000"); verText.font = new Font("Menlo-Regular", 7); const twitterBox = widget.addStack(); twitterBox.spacing = 6; twitterBox.setPadding(0, 8, 16, 0); twitterBox.addSpacer(); const heart = twitterBox.addText("❤️"); heart.textColor = new Color("#fff"); heart.font = new Font("HelveticaNeue-Bold", 12); const twitterImg = twitterBox.addImage(twitterLogo()); twitterImg.imageSize = new Size(16, 16); const twitterUsername = twitterBox.addText("@Dodo13080274 "); twitterUsername.textColor = new Color("#fff"); twitterUsername.font = new Font("HelveticaNeue-Bold", 14); if (!config.runsInWidget) { await widget.presentLarge(); } widget.url = "https://twitter.com/Dodo13080274"; Script.setWidget(widget); Script.complete(); function twitterLogo() { const data = Data.fromBase64String( "iVBORw0KGgoAAAANSUhEUgAAABsAAAAWCAYAAAAxSueLAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAErSURBVHgBvZWBdYIwEIYvvA7gCGxQNigjOAIblBHqBt0AukE7QegEugFsoBv8XkzwRQyYA/R773+RwOU/7yAheiGKFgJgw8OWlbqpg1Lq91FQ5gIlRjmrxT2tu7dxY8VK/cCatY81NMGIpzQxiRf/zspY+iaLcTTF0bBMYp9+pu2gDMVYtCu5hHq4gA48ZObygNkW8VR93Ju3xo41XNhcmwZ3PP65knQk4z84C/uSrE3er594RqbhmJH5I079DzUwy2ll+AO/eviv/o7Wp/EvEi+D5gmGP5N3uZwFwlvQHFIK/TOPD9aBllNztbrJJzibEstpEbflXQy/sYyCJMD2TrOOkPFFc4A9RvRTjWAPPOnWVUoWr2AbKy2bZmUkAbZkpk/7CIMjRo6fKc6UxWq3r/OykAAAAABJRU5ErkJggg==" ); return Image.fromData(data); } ## Publication Information - [Dodo](https://paragraph.com/@dodo-3/): Publication homepage - [All Posts](https://paragraph.com/@dodo-3/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@dodo-3): Subscribe to updates