key
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
parent
c8be082f53
commit
3721564786
10 changed files with 865 additions and 0 deletions
56
android/index.html
Normal file
56
android/index.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>damus</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta property="og:title" content="Damus">
|
||||
<meta property="og:description" content="A new social network that you control">
|
||||
<meta property="og:image" content="https://damus.io/img/damus.png">
|
||||
<meta property="og:url" content="https://damus.io">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<link rel="stylesheet" href="/css/normalize.css">
|
||||
<link rel="stylesheet" href="/css/skeleton.css?v=3">
|
||||
<link rel="stylesheet" href="/css/custom.css?v=4">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<section class="header">
|
||||
<span class="logo">
|
||||
<img src="/img/damus-nobg.svg"/>
|
||||
</span>
|
||||
<span class="damus">damus</span>
|
||||
</section>
|
||||
<div class="container">
|
||||
<section class="hero">
|
||||
<h2>Damus Android crowdfund</h2>
|
||||
<h5 class="subtitle">If ya'll help crowdfund me an android phone I can start working on an android version</h5>
|
||||
|
||||
<!-- <div class="row"> -->
|
||||
<!-- <h4 class="subtitle">Developers First</h4> -->
|
||||
<!-- <img class="code-example" src="img/code-placeholder.svg"> -->
|
||||
<!-- </div> -->
|
||||
</section>
|
||||
|
||||
<h4>Donations</h4>
|
||||
|
||||
<p>This is a bolt12 offer, you can pay this with a CLN node. Otherwise press the button to get a bolt11 invoice.</p>
|
||||
<a id="bolt12" href="lightning:lno1pgt5gctdw4ejqstwv3ex76tyyp3hymmhv3n82mnyzsyxgctdw4eju6t0rcs08sggen2ndwzjdpqlpfw9sgfth8n9sjs7kjfssrnurnp5lqk66ug">
|
||||
<div id="qrcode" class="tipjar-copy" style="float: left; width: 256px; cursor: pointer" ></div>
|
||||
</a>
|
||||
|
||||
<button style="margin-left: 20px; height: 50px" onclick="click_make_invoice(this)">Request bolt11 invoice</button>
|
||||
|
||||
<div id="tipjar-summary" style="clear: left; width: 100%; margin-bottom: 30px;">
|
||||
Loading donations...
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script src="/js/qrcode.min.js" > </script>
|
||||
<script src="/js/lnsocket.js?v=10" > </script>
|
||||
<script src="/js/tipjar.js?v=21" ></script>
|
||||
</html>
|
BIN
img/logo.png
Normal file
BIN
img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 143 KiB |
391
js/lnsocket.js
Normal file
391
js/lnsocket.js
Normal file
File diff suppressed because one or more lines are too long
BIN
js/lnsocket.wasm
Executable file
BIN
js/lnsocket.wasm
Executable file
Binary file not shown.
1
js/qrcode.min.js
vendored
Normal file
1
js/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
172
js/tipjar.js
Normal file
172
js/tipjar.js
Normal file
|
@ -0,0 +1,172 @@
|
|||
async function make_request(method, rune, params) {
|
||||
const LNSocket = await lnsocket_init()
|
||||
const ln = LNSocket()
|
||||
|
||||
ln.genkey()
|
||||
await ln.connect_and_init("03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71", "wss://cln.jb55.com:443")
|
||||
|
||||
const {result} = await ln.rpc({ rune, method, params })
|
||||
|
||||
ln.disconnect()
|
||||
return result
|
||||
}
|
||||
|
||||
function fetch_tipjar_summary() {
|
||||
const rune = "b3Xsg2AS2cknHYa6H94so7FAVQVdnRSP6Pv-1WOQEBc9NCZtZXRob2Q9b2ZmZXItc3VtbWFyeQ=="
|
||||
return make_request("offer-summary", rune, {
|
||||
offerid: "2043536dfec68d559102f73510927622812a230cfdda079e96fccbfe35a96d11",
|
||||
description: "@damus-android",
|
||||
limit: 5
|
||||
})
|
||||
}
|
||||
|
||||
function make_invoice(description) {
|
||||
const rune = "LZwGZJO7wZgmoScFQb5reZ0Ii8qPKCeUfTb-UcbDxWw9MTImbWV0aG9kPWludm9pY2U="
|
||||
description = (description && `${description} @damus-android`) || "@damus-android donation"
|
||||
return make_request("invoice", rune, {
|
||||
msatoshi: "any",
|
||||
label: `damus-android-${new Date().getTime()}`,
|
||||
description: description
|
||||
})
|
||||
}
|
||||
|
||||
function make_qrcode(dat) {
|
||||
const link = dat.toUpperCase()
|
||||
document.querySelector("#qrcode").innerHTML = ""
|
||||
const qr = new QRCode("qrcode", {
|
||||
text: link,
|
||||
width: 256,
|
||||
height: 256,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
correctLevel : QRCode.CorrectLevel.L
|
||||
})
|
||||
}
|
||||
|
||||
async function click_make_invoice(el) {
|
||||
const offerdata = document.querySelector("#offerdata")
|
||||
const tipjar_img = document.querySelector("#tipjar-offer-qr")
|
||||
|
||||
const note = prompt("Leave a note!", "")
|
||||
|
||||
const invoice = await make_invoice(note)
|
||||
|
||||
make_qrcode("LIGHTNING:" + invoice.bolt11)
|
||||
document.querySelector("#bolt12").href = "lightning:" + invoice.bolt11
|
||||
|
||||
el.style.display = "none";
|
||||
}
|
||||
|
||||
async function copy_tip() {
|
||||
const offer = document.querySelector("#offerdata").value.trim();
|
||||
try {
|
||||
await navigator.clipboard.writeText(offer)
|
||||
alert("Invoice copied to clipboard!")
|
||||
} catch(err) {
|
||||
console.log("clipboard copy error", err)
|
||||
document.querySelector("#offerdata").style.display = "block"
|
||||
}
|
||||
}
|
||||
|
||||
async function go() {
|
||||
const summary = await fetch_tipjar_summary()
|
||||
|
||||
const el = document.querySelector("#tipjar-summary")
|
||||
const bolt12 = document.querySelector("#bolt12")
|
||||
|
||||
make_qrcode(bolt12.href)
|
||||
el.innerHTML = render_tips(summary)
|
||||
}
|
||||
|
||||
function render_tips(res) {
|
||||
const total_sats = res.total_msatoshi / 1000
|
||||
const goal = 3000000
|
||||
const perc = `${((total_sats / goal) * 100).toPrecision(2)}%`
|
||||
const total_fmt = `${format_amount(total_sats)} / ${format_amount(goal)} sats goal (${perc})`
|
||||
return `
|
||||
<p>Total: <b>${total_fmt}</b></p>
|
||||
<div class="progres" style="height:20px; background-color: #f1f1f1">
|
||||
<div class="progress-bar" style="background-color: #f44336; height: 100%;width: ${perc}"></div>
|
||||
</div>
|
||||
<h5>Recent Donors</h5>
|
||||
${render_table(res.paid_invoices)}
|
||||
<h5>Top Donors</h5>
|
||||
${render_table(res.top_donors)}
|
||||
`
|
||||
}
|
||||
|
||||
function render_table(invoices)
|
||||
{
|
||||
return `
|
||||
<table style="width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Amount</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${invoices.map(render_tip).join("\n")}
|
||||
</tbody>
|
||||
</table>
|
||||
`
|
||||
}
|
||||
|
||||
function format_amount(amt)
|
||||
{
|
||||
return amt.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function render_tip(tip)
|
||||
{
|
||||
let note = tip.payer_note ? tip.payer_note : (tip.description || "Anonymous")
|
||||
note = note.replace("@damus-android", "")
|
||||
const amount = format_amount(tip.msatoshi / 1000)
|
||||
const now = Math.floor(new Date().getTime() / 1000)
|
||||
const date = time_delta(now * 1000, tip.paid_at * 1000)
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${note}</td>
|
||||
<td>${amount} sats</td>
|
||||
<td class="reldate">${date}</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
|
||||
function time_delta(current, previous) {
|
||||
var msPerMinute = 60 * 1000;
|
||||
var msPerHour = msPerMinute * 60;
|
||||
var msPerDay = msPerHour * 24;
|
||||
var msPerMonth = msPerDay * 30;
|
||||
var msPerYear = msPerDay * 365;
|
||||
|
||||
var elapsed = current - previous;
|
||||
|
||||
if (elapsed < msPerMinute) {
|
||||
return Math.round(elapsed/1000) + ' seconds ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerHour) {
|
||||
return Math.round(elapsed/msPerMinute) + ' minutes ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerDay ) {
|
||||
return Math.round(elapsed/msPerHour ) + ' hours ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerMonth) {
|
||||
return Math.round(elapsed/msPerDay) + ' days ago';
|
||||
}
|
||||
|
||||
else if (elapsed < msPerYear) {
|
||||
return Math.round(elapsed/msPerMonth) + ' months ago';
|
||||
}
|
||||
|
||||
else {
|
||||
return Math.round(elapsed/msPerYear ) + ' years ago';
|
||||
}
|
||||
}
|
||||
|
||||
go()
|
168
key/bech32.js
Normal file
168
key/bech32.js
Normal file
|
@ -0,0 +1,168 @@
|
|||
var ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
||||
var ALPHABET_MAP = {};
|
||||
for (var z = 0; z < ALPHABET.length; z++) {
|
||||
var x = ALPHABET.charAt(z);
|
||||
ALPHABET_MAP[x] = z;
|
||||
}
|
||||
function polymodStep(pre) {
|
||||
var b = pre >> 25;
|
||||
return (((pre & 0x1ffffff) << 5) ^
|
||||
(-((b >> 0) & 1) & 0x3b6a57b2) ^
|
||||
(-((b >> 1) & 1) & 0x26508e6d) ^
|
||||
(-((b >> 2) & 1) & 0x1ea119fa) ^
|
||||
(-((b >> 3) & 1) & 0x3d4233dd) ^
|
||||
(-((b >> 4) & 1) & 0x2a1462b3));
|
||||
}
|
||||
function prefixChk(prefix) {
|
||||
var chk = 1;
|
||||
for (var i = 0; i < prefix.length; ++i) {
|
||||
var c = prefix.charCodeAt(i);
|
||||
if (c < 33 || c > 126)
|
||||
return 'Invalid prefix (' + prefix + ')';
|
||||
chk = polymodStep(chk) ^ (c >> 5);
|
||||
}
|
||||
chk = polymodStep(chk);
|
||||
for (var i = 0; i < prefix.length; ++i) {
|
||||
var v = prefix.charCodeAt(i);
|
||||
chk = polymodStep(chk) ^ (v & 0x1f);
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
function convertbits(data, inBits, outBits, pad) {
|
||||
var value = 0;
|
||||
var bits = 0;
|
||||
var maxV = (1 << outBits) - 1;
|
||||
var result = [];
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
value = (value << inBits) | data[i];
|
||||
bits += inBits;
|
||||
while (bits >= outBits) {
|
||||
bits -= outBits;
|
||||
result.push((value >> bits) & maxV);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits > 0) {
|
||||
result.push((value << (outBits - bits)) & maxV);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bits >= inBits)
|
||||
return 'Excess padding';
|
||||
if ((value << (outBits - bits)) & maxV)
|
||||
return 'Non-zero padding';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function toWords(bytes) {
|
||||
return convertbits(bytes, 8, 5, true);
|
||||
}
|
||||
function fromWordsUnsafe(words) {
|
||||
var res = convertbits(words, 5, 8, false);
|
||||
if (Array.isArray(res))
|
||||
return res;
|
||||
}
|
||||
function fromWords(words) {
|
||||
var res = convertbits(words, 5, 8, false);
|
||||
if (Array.isArray(res))
|
||||
return res;
|
||||
throw new Error(res);
|
||||
}
|
||||
function getLibraryFromEncoding(encoding) {
|
||||
var ENCODING_CONST;
|
||||
if (encoding === 'bech32') {
|
||||
ENCODING_CONST = 1;
|
||||
}
|
||||
else {
|
||||
ENCODING_CONST = 0x2bc830a3;
|
||||
}
|
||||
function encode(prefix, words, LIMIT) {
|
||||
LIMIT = LIMIT || 90;
|
||||
if (prefix.length + 7 + words.length > LIMIT)
|
||||
throw new TypeError('Exceeds length limit');
|
||||
prefix = prefix.toLowerCase();
|
||||
// determine chk mod
|
||||
var chk = prefixChk(prefix);
|
||||
if (typeof chk === 'string')
|
||||
throw new Error(chk);
|
||||
var result = prefix + '1';
|
||||
for (var i = 0; i < words.length; ++i) {
|
||||
var x = words[i];
|
||||
if (x >> 5 !== 0)
|
||||
throw new Error('Non 5-bit word');
|
||||
chk = polymodStep(chk) ^ x;
|
||||
result += ALPHABET.charAt(x);
|
||||
}
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
chk = polymodStep(chk);
|
||||
}
|
||||
chk ^= ENCODING_CONST;
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
var v = (chk >> ((5 - i) * 5)) & 0x1f;
|
||||
result += ALPHABET.charAt(v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function __decode(str, LIMIT) {
|
||||
LIMIT = LIMIT || 90;
|
||||
if (str.length < 8)
|
||||
return str + ' too short';
|
||||
if (str.length > LIMIT)
|
||||
return 'Exceeds length limit';
|
||||
// don't allow mixed case
|
||||
var lowered = str.toLowerCase();
|
||||
var uppered = str.toUpperCase();
|
||||
if (str !== lowered && str !== uppered)
|
||||
return 'Mixed-case string ' + str;
|
||||
str = lowered;
|
||||
var split = str.lastIndexOf('1');
|
||||
if (split === -1)
|
||||
return 'No separator character for ' + str;
|
||||
if (split === 0)
|
||||
return 'Missing prefix for ' + str;
|
||||
var prefix = str.slice(0, split);
|
||||
var wordChars = str.slice(split + 1);
|
||||
if (wordChars.length < 6)
|
||||
return 'Data too short';
|
||||
var chk = prefixChk(prefix);
|
||||
if (typeof chk === 'string')
|
||||
return chk;
|
||||
var words = [];
|
||||
for (var i = 0; i < wordChars.length; ++i) {
|
||||
var c = wordChars.charAt(i);
|
||||
var v = ALPHABET_MAP[c];
|
||||
if (v === undefined)
|
||||
return 'Unknown character ' + c;
|
||||
chk = polymodStep(chk) ^ v;
|
||||
// not in the checksum?
|
||||
if (i + 6 >= wordChars.length)
|
||||
continue;
|
||||
words.push(v);
|
||||
}
|
||||
if (chk !== ENCODING_CONST)
|
||||
return 'Invalid checksum for ' + str;
|
||||
return { prefix: prefix, words: words };
|
||||
}
|
||||
function decodeUnsafe(str, LIMIT) {
|
||||
var res = __decode(str, LIMIT);
|
||||
if (typeof res === 'object')
|
||||
return res;
|
||||
}
|
||||
function decode(str, LIMIT) {
|
||||
var res = __decode(str, LIMIT);
|
||||
if (typeof res === 'object')
|
||||
return res;
|
||||
throw new Error(res);
|
||||
}
|
||||
return {
|
||||
decodeUnsafe: decodeUnsafe,
|
||||
decode: decode,
|
||||
encode: encode,
|
||||
toWords: toWords,
|
||||
fromWordsUnsafe: fromWordsUnsafe,
|
||||
fromWords: fromWords
|
||||
};
|
||||
}
|
||||
|
||||
const bech32 = getLibraryFromEncoding('bech32');
|
||||
const bech32m = getLibraryFromEncoding('bech32m');
|
44
key/index.html
Normal file
44
key/index.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>damus key converter</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta property="og:title" content="Damus">
|
||||
<meta property="og:description" content="A new social network that you control">
|
||||
<meta property="og:image" content="https://damus.io/img/logo.png">
|
||||
<meta property="og:url" content="https://damus.io">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<link rel="stylesheet" href="/css/normalize.css">
|
||||
<link rel="stylesheet" href="/css/skeleton.css?v=2">
|
||||
<link rel="stylesheet" href="/css/custom.css?v=4">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<section class="header">
|
||||
<span class="logo">
|
||||
<img src="/img/damus-nobg.svg"/>
|
||||
</span>
|
||||
|
||||
<span class="damus">damus</span>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h1>Key Converter</h2>
|
||||
<p>Convert a damus key to an old-style hex key</p>
|
||||
<label for="bech32">damus key</label>
|
||||
<input type="text" class="u-full-width" placeholder="npub... OR nsec..." id="damus-key">
|
||||
|
||||
<label for="bech32">hex key</label>
|
||||
<input type="text" class="u-full-width" placeholder="" id="hex-key">
|
||||
</div>
|
||||
|
||||
<script src="bech32.js" ></script>
|
||||
<script src="key.js" ></script>
|
||||
</body>
|
||||
</html>
|
32
key/key.js
Normal file
32
key/key.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
function hex_char(val)
|
||||
{
|
||||
if (val < 10)
|
||||
return String.fromCharCode(48 + val)
|
||||
if (val < 16)
|
||||
return String.fromCharCode(97 + val - 10)
|
||||
}
|
||||
|
||||
function hex_encode(buf)
|
||||
{
|
||||
str = ""
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
const c = buf[i]
|
||||
str += hex_char(c >> 4)
|
||||
str += hex_char(c & 0xF)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function go() {
|
||||
const el = document.querySelector("#damus-key")
|
||||
const hex_el = document.querySelector("#hex-key")
|
||||
|
||||
el.addEventListener("input", () => {
|
||||
const decoded = bech32.decode(el.value)
|
||||
const bytes = fromWords(decoded.words)
|
||||
hex_el.value = hex_encode(bytes)
|
||||
});
|
||||
}
|
||||
|
||||
go()
|
1
privacy-policy.txt
Normal file
1
privacy-policy.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Damus doesn't store any information about you other than the posts which you make, which are published to the damus relay as well other relays if configured.
|
Loading…
Add table
Add a link
Reference in a new issue