mirror of
https://github.com/aljazceru/hypergolic.git
synced 2025-12-19 14:34:20 +01:00
Merge branch 'MASTER' into fix-style
This commit is contained in:
136
package-lock.json
generated
136
package-lock.json
generated
@@ -12,15 +12,19 @@
|
|||||||
"@mempool/mempool.js": "^2.3.0",
|
"@mempool/mempool.js": "^2.3.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.8.2",
|
"@nostr-dev-kit/ndk": "^2.8.2",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
|
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
|
||||||
|
"@nostr-dev-kit/ndk-cache-nostr": "^0.1.0",
|
||||||
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
||||||
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"bitcoin-address-validation": "^2.2.3",
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
|
"bloomfilter": "^0.0.18",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
"embla-carousel-svelte": "^8.1.3",
|
"embla-carousel-svelte": "^8.1.3",
|
||||||
"formsnap": "^1.0.0",
|
"formsnap": "^1.0.0",
|
||||||
|
"immutable": "^4.3.7",
|
||||||
|
"js-sha256": "^0.11.0",
|
||||||
"lucide-svelte": "^0.383.0",
|
"lucide-svelte": "^0.383.0",
|
||||||
"mode-watcher": "^0.3.0",
|
"mode-watcher": "^0.3.0",
|
||||||
"paneforge": "^0.0.4",
|
"paneforge": "^0.0.4",
|
||||||
@@ -39,6 +43,7 @@
|
|||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
|
"@types/bloomfilter": "^0.0.2",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^20.14.2",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"flowbite": "^2.3.0",
|
"flowbite": "^2.3.0",
|
||||||
@@ -797,6 +802,115 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-cache-nostr/-/ndk-cache-nostr-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-NqQ9RWp9z21Tyrs8GoGrdAjzbEyJktkCQt/+rx6AexR4Gb3StkmlBpAaNrKib3l7ePHPJQWKYPSdR0pWqvnGrw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nostr-dev-kit/ndk": "2.10.0",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"typescript": "^5.4.4",
|
||||||
|
"websocket-polyfill": "^0.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@noble/ciphers": {
|
||||||
|
"version": "0.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||||
|
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@nostr-dev-kit/ndk": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-TqCAAo6ylORraAXrzRkCGFN2xTMiFbdER8Y8CtUT0HwOpFG/Wn+PBNeDeDmqkl/6LaPdeyXmVwCWj2KcUjIwYA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": "^1.4.0",
|
||||||
|
"@noble/hashes": "^1.3.1",
|
||||||
|
"@noble/secp256k1": "^2.0.0",
|
||||||
|
"@scure/base": "^1.1.1",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
|
"node-fetch": "^3.3.1",
|
||||||
|
"nostr-tools": "^2.7.1",
|
||||||
|
"tseep": "^1.1.1",
|
||||||
|
"typescript-lru-cache": "^2.0.0",
|
||||||
|
"utf8-buffer": "^1.0.0",
|
||||||
|
"websocket-polyfill": "^0.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/@scure/base": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools": {
|
||||||
|
"version": "2.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.2.tgz",
|
||||||
|
"integrity": "sha512-Bq3Ug0SZFtgtL1+0wCnAe8AJtI7yx/00/a2nUug9SkhfOwlKS92Tef12iCK9FdwXw+oFZWMtRnSwcLayQso+xA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/ciphers": "^0.5.1",
|
||||||
|
"@noble/curves": "1.2.0",
|
||||||
|
"@noble/hashes": "1.3.1",
|
||||||
|
"@scure/base": "1.1.1",
|
||||||
|
"@scure/bip32": "1.3.1",
|
||||||
|
"@scure/bip39": "1.2.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"nostr-wasm": "v0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools/node_modules/@noble/curves": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nostr-dev-kit/ndk-cache-nostr/node_modules/nostr-tools/node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nostr-dev-kit/ndk-svelte": {
|
"node_modules/@nostr-dev-kit/ndk-svelte": {
|
||||||
"version": "2.2.15",
|
"version": "2.2.15",
|
||||||
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-svelte/-/ndk-svelte-2.2.15.tgz",
|
"resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk-svelte/-/ndk-svelte-2.2.15.tgz",
|
||||||
@@ -1413,6 +1527,12 @@
|
|||||||
"tailwindcss": ">=3.0.0 || insiders"
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/bloomfilter": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bloomfilter/-/bloomfilter-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-XWY6sYrOqHMPVf2pwITSQ5ZQWjk2QSQibHcXPJtjuYGHkweOkcU8BbgWSYynWVFMjeS/OVhYCyR+V0puTNC/hQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/chrome": {
|
"node_modules/@types/chrome": {
|
||||||
"version": "0.0.74",
|
"version": "0.0.74",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.74.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.74.tgz",
|
||||||
@@ -1763,6 +1883,11 @@
|
|||||||
"node": "^18 || >=20"
|
"node": "^18 || >=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bloomfilter": {
|
||||||
|
"version": "0.0.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/bloomfilter/-/bloomfilter-0.0.18.tgz",
|
||||||
|
"integrity": "sha512-CbnyHE78gY1tpXS/Ap+B0RJxKdRWCDzjBnX97UJSG8rdLv1PK8GiTWc/CCQyWu6PWVD4lUceeFrqC6Mf3nMgOA=="
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@@ -2963,6 +3088,11 @@
|
|||||||
"url": "https://github.com/sponsors/typicode"
|
"url": "https://github.com/sponsors/typicode"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immutable": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
|
||||||
|
},
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -3168,6 +3298,11 @@
|
|||||||
"@sideway/pinpoint": "^2.0.0"
|
"@sideway/pinpoint": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-sha256": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q=="
|
||||||
|
},
|
||||||
"node_modules/json-schema-to-ts": {
|
"node_modules/json-schema-to-ts": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.0.tgz",
|
||||||
@@ -5423,7 +5558,6 @@
|
|||||||
"version": "5.4.5",
|
"version": "5.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
"devOptional": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
|
"@types/bloomfilter": "^0.0.2",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^20.14.2",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"flowbite": "^2.3.0",
|
"flowbite": "^2.3.0",
|
||||||
@@ -42,15 +43,19 @@
|
|||||||
"@mempool/mempool.js": "^2.3.0",
|
"@mempool/mempool.js": "^2.3.0",
|
||||||
"@nostr-dev-kit/ndk": "^2.8.2",
|
"@nostr-dev-kit/ndk": "^2.8.2",
|
||||||
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
|
"@nostr-dev-kit/ndk-cache-dexie": "^2.4.2",
|
||||||
|
"@nostr-dev-kit/ndk-cache-nostr": "^0.1.0",
|
||||||
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
"@nostr-dev-kit/ndk-svelte": "^2.2.15",
|
||||||
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
"@nostr-dev-kit/ndk-svelte-components": "^2.2.16",
|
||||||
"@sveltejs/adapter-static": "^3.0.1",
|
"@sveltejs/adapter-static": "^3.0.1",
|
||||||
"bitcoin-address-validation": "^2.2.3",
|
"bitcoin-address-validation": "^2.2.3",
|
||||||
"bits-ui": "^0.21.10",
|
"bits-ui": "^0.21.10",
|
||||||
|
"bloomfilter": "^0.0.18",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk-sv": "^0.0.17",
|
"cmdk-sv": "^0.0.17",
|
||||||
"embla-carousel-svelte": "^8.1.3",
|
"embla-carousel-svelte": "^8.1.3",
|
||||||
"formsnap": "^1.0.0",
|
"formsnap": "^1.0.0",
|
||||||
|
"immutable": "^4.3.7",
|
||||||
|
"js-sha256": "^0.11.0",
|
||||||
"lucide-svelte": "^0.383.0",
|
"lucide-svelte": "^0.383.0",
|
||||||
"mode-watcher": "^0.3.0",
|
"mode-watcher": "^0.3.0",
|
||||||
"paneforge": "^0.0.4",
|
"paneforge": "^0.0.4",
|
||||||
|
|||||||
@@ -1,16 +1,30 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Card from "@/components/ui/card";
|
import * as Card from '@/components/ui/card';
|
||||||
|
|
||||||
|
import Heading from './Heading.svelte';
|
||||||
import Heading from "./Heading.svelte";
|
import InputBitcoinAddress from './InputBitcoinAddress.svelte';
|
||||||
import InputBitcoinAddress from "./InputBitcoinAddress.svelte";
|
import { Button } from '@/components/ui/button';
|
||||||
import { Button } from "@/components/ui/button";
|
import { ndk } from '@/ndk';
|
||||||
import { ndk } from "@/ndk";
|
import { currentUser } from '@/stores/session';
|
||||||
import { currentUser } from "@/stores/session";
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
import validate from 'bitcoin-address-validation';
|
||||||
import validate from "bitcoin-address-validation";
|
import type { Rocket } from '@/event_helpers/rockets';
|
||||||
|
import { derived } from 'svelte/store';
|
||||||
|
|
||||||
let bitcoinAddress: string;
|
let bitcoinAddress: string;
|
||||||
|
export let rocket: Rocket;
|
||||||
|
|
||||||
|
let associatedAddresses = derived(currentUser, ($currentUser) => {
|
||||||
|
let addresses: Set<string> = new Set();
|
||||||
|
if ($currentUser) {
|
||||||
|
for (let [_, a] of rocket.BitcoinAssociations()) {
|
||||||
|
if (a.Pubkey == $currentUser.pubkey && a.Address && validate(a.Address)) {
|
||||||
|
addresses.add(a.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses;
|
||||||
|
});
|
||||||
|
|
||||||
function publish(address: string) {
|
function publish(address: string) {
|
||||||
if (!$ndk.signer) {
|
if (!$ndk.signer) {
|
||||||
@@ -21,26 +35,46 @@
|
|||||||
throw new Error('no current user');
|
throw new Error('no current user');
|
||||||
}
|
}
|
||||||
if (!validate(address)) {
|
if (!validate(address)) {
|
||||||
throw new Error("invalid bitcoin address")
|
throw new Error('invalid bitcoin address');
|
||||||
}
|
}
|
||||||
let event = new NDKEvent($ndk)
|
let event = new NDKEvent($ndk);
|
||||||
event.kind = 1413
|
event.kind = 1413;
|
||||||
event.tags.push(["onchain", address])
|
event.tags.push(['onchain', address]);
|
||||||
//todo: let user specify a rocket
|
//todo: let user specify a rocket
|
||||||
console.log("todo: let user specify a rocket")
|
console.log('todo: let user specify a rocket');
|
||||||
event.publish().then((x) => {
|
event
|
||||||
|
.publish()
|
||||||
|
.then((x) => {
|
||||||
console.log(x);
|
console.log(x);
|
||||||
}).catch(()=>{ console.log("failed to publish", event.rawEvent())});
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log('failed to publish', event.rawEvent());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Heading title="Sponsor a Contributor" />
|
<Heading title="Sponsor a Contributor" />
|
||||||
|
Contributors who need Sats are able to list their Merits for sale, to sponsor them simply buy some of
|
||||||
|
their Merits.
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header><Card.Title>Associate Bitcoin Address</Card.Title></Card.Header>
|
<Card.Header><Card.Title>Your Bitcoin Addresses</Card.Title></Card.Header>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<div class="m-2 flex">
|
<div class="m-2 flex">
|
||||||
You must associate at least one Bitcoin address with your npub before you can pay a Contributor. Merit purchases from this address will be associated with your pubkey.
|
Merit purchases must be conducted with a Bitcoin address that is associated with your pubkey,
|
||||||
|
otherwise you will not recieve the Merits upon payment.
|
||||||
|
</div>
|
||||||
|
{#if $associatedAddresses.size == 0}You do not have any registered addresses{:else}
|
||||||
|
Your registered addresses:
|
||||||
|
<ul class="m-2 flex flex-col">
|
||||||
|
{#each $associatedAddresses as address}<li class="list-item list-disc">{address}</li>{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
Add a new address now
|
||||||
|
<div class="flex">
|
||||||
|
<InputBitcoinAddress bind:bitcoinAddress /><Button
|
||||||
|
on:click={() => publish(bitcoinAddress)}
|
||||||
|
class="mt-3 max-w-xs">Publish</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex"><InputBitcoinAddress bind:bitcoinAddress /><Button on:click={()=>publish(bitcoinAddress)} class="mt-3 max-w-xs">Publish</Button></div>
|
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
@@ -21,17 +21,24 @@
|
|||||||
_associationRequests?.unsubscribe();
|
_associationRequests?.unsubscribe();
|
||||||
});
|
});
|
||||||
|
|
||||||
let addresses = new Map<string, BitcoinAssociation>()
|
let addresses = new Map<string, BitcoinAssociation>();
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
addresses = rocket.BitcoinAssociations()
|
addresses = rocket.BitcoinAssociations();
|
||||||
addresses.forEach(a => {
|
addresses.forEach((a) => {
|
||||||
if (a.Address) {
|
if (a.Address) {
|
||||||
getBalance(a.Address).then(v=>{a.Balance = v; addresses.set(a.Pubkey, a); addresses = addresses}).catch(err=>{console.log(err)})
|
getBalance(a.Address)
|
||||||
|
.then((v) => {
|
||||||
|
a.Balance = v;
|
||||||
|
addresses.set(a.Address!, a);
|
||||||
|
addresses = addresses;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card.Root class="sm:col-span-3">
|
<Card.Root class="sm:col-span-3">
|
||||||
@@ -51,18 +58,18 @@
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{#each addresses as [pubkey, ba], _ (pubkey)}
|
{#each addresses as [address, ba], _ (address)}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div class="flex flex-nowrap">
|
<div class="flex flex-nowrap">
|
||||||
<Avatar
|
<Avatar
|
||||||
ndk={$ndk}
|
ndk={$ndk}
|
||||||
pubkey={pubkey}
|
pubkey={ba.Pubkey}
|
||||||
class="h-10 w-10 flex-none rounded-full object-cover"
|
class="h-10 w-10 flex-none rounded-full object-cover"
|
||||||
/>
|
/>
|
||||||
<Name
|
<Name
|
||||||
ndk={$ndk}
|
ndk={$ndk}
|
||||||
pubkey={pubkey}
|
pubkey={ba.Pubkey}
|
||||||
class="hidden max-w-32 truncate p-2 md:inline-block"
|
class="hidden max-w-32 truncate p-2 md:inline-block"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +78,6 @@
|
|||||||
{ba.Balance.toLocaleString()}
|
{ba.Balance.toLocaleString()}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell class="table-cell">{ba.Address}</Table.Cell>
|
<Table.Cell class="table-cell">{ba.Address}</Table.Cell>
|
||||||
|
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
{/each}
|
{/each}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
|
|||||||
71
src/components/BuyAMR.svelte
Normal file
71
src/components/BuyAMR.svelte
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, buttonVariants } from '$lib/components/ui/button/index.js';
|
||||||
|
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||||
|
import * as Alert from '@/components/ui/alert';
|
||||||
|
import type { AMRAuction } from '@/event_helpers/rockets';
|
||||||
|
import { ndk } from '@/ndk';
|
||||||
|
import { currentUser } from '@/stores/session';
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
import type NDKSvelte from '@nostr-dev-kit/ndk-svelte';
|
||||||
|
import { Name } from '@nostr-dev-kit/ndk-svelte-components';
|
||||||
|
import { Terminal } from 'lucide-svelte';
|
||||||
|
|
||||||
|
export let auction: AMRAuction;
|
||||||
|
let o = false;
|
||||||
|
|
||||||
|
function publish(ndk: NDKSvelte) {
|
||||||
|
if (!ndk.signer) {
|
||||||
|
throw new Error('no ndk signer found');
|
||||||
|
}
|
||||||
|
let e = new NDKEvent(ndk);
|
||||||
|
let author = $currentUser;
|
||||||
|
if (!author) {
|
||||||
|
throw new Error('no current user');
|
||||||
|
}
|
||||||
|
e.author = author;
|
||||||
|
e.kind = 1216;
|
||||||
|
e.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
|
//todo validate d tag
|
||||||
|
|
||||||
|
// e.publish().then((x) => {
|
||||||
|
// console.log(x);
|
||||||
|
// o = false;
|
||||||
|
// goto(`${base}/rockets/${getRocketURL(rocketEvent)}`);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Root bind:open={o}>
|
||||||
|
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}>Buy Now</Dialog.Trigger>
|
||||||
|
<Dialog.Content class="sm:max-w-[425px]">
|
||||||
|
{#if !currentUser}
|
||||||
|
<Alert.Root>
|
||||||
|
<Terminal class="h-4 w-4" />
|
||||||
|
<Alert.Title>Heads up!</Alert.Title>
|
||||||
|
<Alert.Description>You need a nostr signing extension to use Nostrocket!</Alert.Description>
|
||||||
|
</Alert.Root>
|
||||||
|
{:else}
|
||||||
|
<Dialog.Header>
|
||||||
|
<Dialog.Title>Buy Merits from <Name pubkey={auction.Owner} /></Dialog.Title>
|
||||||
|
</Dialog.Header>
|
||||||
|
<p>
|
||||||
|
To buy these merits you MUST send {auction.Merits / 100000000} BTC from one of your registered
|
||||||
|
addresses to {auction.RxAddress}.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Once the transaction has 2 confirmations the Merits will automatically be transferred to
|
||||||
|
your npub.
|
||||||
|
</p>
|
||||||
|
<Dialog.Footer>
|
||||||
|
Todo: ask user to publish an event before making transaction so that multiple people don't
|
||||||
|
pay for the same Merits.
|
||||||
|
<!-- <Button
|
||||||
|
on:click={() => {
|
||||||
|
publish($ndk);
|
||||||
|
}}
|
||||||
|
type="submit">Publish</Button
|
||||||
|
> -->
|
||||||
|
</Dialog.Footer>
|
||||||
|
{/if}
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
@@ -84,22 +84,25 @@
|
|||||||
variant="nostr"
|
variant="nostr"
|
||||||
class="flex h-8 shrink-0 items-center justify-center rounded-sm"
|
class="flex h-8 shrink-0 items-center justify-center rounded-sm"
|
||||||
>
|
>
|
||||||
Tell me via DM when there are updates
|
Nostrocket is totally not ready yet but whatever
|
||||||
</Badge>
|
</Badge>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Content>
|
<Dialog.Content>
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
<Dialog.Title>Subscribe for Updates</Dialog.Title>
|
<Dialog.Title>Subscribe for Updates</Dialog.Title>
|
||||||
<Dialog.Description>
|
<Dialog.Description>
|
||||||
Receive notifications about Nostrocket updates via Nostr DM or email.
|
Subscribe now and we'll ping you when there are new releases/features
|
||||||
</Dialog.Description>
|
</Dialog.Description>
|
||||||
<div class="flex flex-col gap-4 py-4">
|
<div class="flex flex-col gap-4 py-4">
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
<Button on:click={Subscribe}>Receive DM</Button>
|
<Button on:click={Subscribe}>DM me with updates</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<Login />
|
<Login />
|
||||||
{/if}
|
{/if}
|
||||||
<Separator />
|
<Separator />
|
||||||
|
<span class="ml-auto mr-auto flex"
|
||||||
|
>If you don't use nostr, you can subscribe to updates with an email address instead</span
|
||||||
|
>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Label for="email" class="text-right">Email</Label>
|
<Label for="email" class="text-right">Email</Label>
|
||||||
<Input bind:value={email} id="email" placeholder="Your email" class="col-span-3" />
|
<Input bind:value={email} id="email" placeholder="Your email" class="col-span-3" />
|
||||||
@@ -108,7 +111,9 @@
|
|||||||
<div class="ml-4 p-0 text-sm text-red-500">{emailError}</div>
|
<div class="ml-4 p-0 text-sm text-red-500">{emailError}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<Button disabled={emailInValid} on:click={SubmitEmailAndSubscribe}>Receive Email</Button>
|
<Button disabled={emailInValid} on:click={SubmitEmailAndSubscribe}
|
||||||
|
>Please email me with updates</Button
|
||||||
|
>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class MeritRequest {
|
|||||||
}
|
}
|
||||||
constructor(request: NDKEvent | string) {
|
constructor(request: NDKEvent | string) {
|
||||||
if (typeof request == 'string') {
|
if (typeof request == 'string') {
|
||||||
console.log(69);
|
throw new Error('implement me');
|
||||||
} else {
|
} else {
|
||||||
this.LeadTime = 0;
|
this.LeadTime = 0;
|
||||||
this.LastLTUpdate = 0;
|
this.LastLTUpdate = 0;
|
||||||
|
|||||||
@@ -1,15 +1,40 @@
|
|||||||
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
|
|
||||||
import { MapOfVotes, MeritRequest, Votes } from './merits';
|
|
||||||
import { getAuthorizedZapper } from '@/helpers';
|
import { getAuthorizedZapper } from '@/helpers';
|
||||||
|
import { BitcoinTipTag, txo, txs } from '@/stores/bitcoin';
|
||||||
|
import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk';
|
||||||
import validate from 'bitcoin-address-validation';
|
import validate from 'bitcoin-address-validation';
|
||||||
import { BitcoinTipTag, bitcoinTip, txs } from '@/stores/bitcoin';
|
import { sha256 } from 'js-sha256';
|
||||||
|
import { MapOfVotes, MeritRequest, Votes } from './merits';
|
||||||
|
import * as immutable from 'immutable';
|
||||||
|
import { BloomFilter } from 'bloomfilter';
|
||||||
|
|
||||||
export class Rocket {
|
export class Rocket {
|
||||||
UpsertBitcoinAssociation(association: BitcoinAssociation): NDKEvent {
|
Event: NDKEvent;
|
||||||
|
private Bloom(): BloomFilter {
|
||||||
|
let b = new BloomFilter(
|
||||||
|
64 * 256, // bits to allocate.
|
||||||
|
32 // number of hashes
|
||||||
|
);
|
||||||
|
let existing = this.Event.getMatchingTags('bloom');
|
||||||
|
if (existing.length == 1) {
|
||||||
|
b = new BloomFilter(JSON.parse(existing[0][existing[0].length - 1]), 32);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
private AppendEventToBloom(id: string) {
|
||||||
|
let existing = this.Bloom();
|
||||||
|
existing.add(id);
|
||||||
|
this.Event.removeTag('bloom');
|
||||||
|
this.Event.tags.push(['bloom', '32', JSON.stringify([].slice.call(existing.buckets))]);
|
||||||
|
}
|
||||||
|
Included(id: string): boolean {
|
||||||
|
return this.Bloom().test(id);
|
||||||
|
}
|
||||||
|
UpsertBitcoinAssociation(association: BitcoinAssociation): NDKEvent | undefined {
|
||||||
let event: NDKEvent | undefined = undefined;
|
let event: NDKEvent | undefined = undefined;
|
||||||
if (true) {
|
if (association.Validate() && association.Event && !this.Included(association.Event.id)) {
|
||||||
//todo: check if exists
|
let existing = this.BitcoinAssociations().get(association.Address!);
|
||||||
this.PrepareForUpdate();
|
if ((existing && existing.Pubkey != association.Pubkey) || !existing) {
|
||||||
|
this.PrepareForUpdate(association.Event.id);
|
||||||
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
||||||
event.created_at = Math.floor(new Date().getTime() / 1000);
|
event.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
event.tags.push(['address', `${association.Pubkey}:${association.Address}`]);
|
event.tags.push(['address', `${association.Pubkey}:${association.Address}`]);
|
||||||
@@ -17,6 +42,7 @@ export class Rocket {
|
|||||||
updateIgnitionAndParentTag(event);
|
updateIgnitionAndParentTag(event);
|
||||||
updateBitcoinTip(event);
|
updateBitcoinTip(event);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
BitcoinAssociations(): Map<string, BitcoinAssociation> {
|
BitcoinAssociations(): Map<string, BitcoinAssociation> {
|
||||||
@@ -29,14 +55,86 @@ export class Rocket {
|
|||||||
ba.Address = split[1];
|
ba.Address = split[1];
|
||||||
ba.Pubkey = split[0];
|
ba.Pubkey = split[0];
|
||||||
if (ba.Validate()) {
|
if (ba.Validate()) {
|
||||||
a.set(ba.Pubkey, ba);
|
a.set(ba.Address, ba);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
Event: NDKEvent;
|
UpsertLeadTime(event: NDKEvent): NDKEvent {
|
||||||
|
//todo: validate that there are no current auctions that include this AMR
|
||||||
|
return new NDKEvent();
|
||||||
|
}
|
||||||
|
UpsertMeritTransfer(request: MeritPurchase): NDKEvent | undefined {
|
||||||
|
let event: NDKEvent | undefined = undefined;
|
||||||
|
if (this.PendingAMRAuctionsMap().get(request.auction.ID())) {
|
||||||
|
this.PrepareForUpdate();
|
||||||
|
let _event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
||||||
|
_event.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
|
//delete the auction
|
||||||
|
let auctionID = request.auction.ID();
|
||||||
|
let existing = _event.getMatchingTags('amr_auction');
|
||||||
|
_event.removeTag('amr_auction');
|
||||||
|
for (let t of existing) {
|
||||||
|
let amr = AMRAuctionFromTag(t, this.Event);
|
||||||
|
if (amr.ID() != auctionID) {
|
||||||
|
_event.tags.push(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_event.tags.push(['proof_raw', `txid:${request.tx.ID}`]);
|
||||||
|
|
||||||
|
let modifiedMerits: Map<string, RocketAMR> = new Map();
|
||||||
|
for (let id of request.auction.AMRIDs) {
|
||||||
|
let amr = this.ApprovedMeritRequests().get(id);
|
||||||
|
if (!amr) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
if (amr.LeadTime > 0) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
amr.Pubkey = request.buyer;
|
||||||
|
modifiedMerits.set(amr.ID, amr);
|
||||||
|
}
|
||||||
|
let existingMerits = this.ApprovedMeritRequests();
|
||||||
|
for (let [id, m] of modifiedMerits) {
|
||||||
|
existingMerits.set(id, m);
|
||||||
|
}
|
||||||
|
_event.removeTag('merit');
|
||||||
|
for (let [id, m] of existingMerits) {
|
||||||
|
_event.tags.push(m.Tag());
|
||||||
|
}
|
||||||
|
_event.tags.push([
|
||||||
|
'swap',
|
||||||
|
`${request.auction.Merits}:${request.tx.Amount}:${Math.floor(new Date().getTime() / 1000)}`
|
||||||
|
]);
|
||||||
|
let existingAssociation = this.BitcoinAssociations().get(request.tx.From);
|
||||||
|
if (
|
||||||
|
!existingAssociation ||
|
||||||
|
(existingAssociation && existingAssociation.Pubkey != request.buyer)
|
||||||
|
) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
if (request.tx.Change) {
|
||||||
|
let existingAssociations = this.BitcoinAssociations();
|
||||||
|
_event.removeTag('address');
|
||||||
|
for (let [_, ba] of existingAssociations) {
|
||||||
|
if (ba.Address != request.tx.From) {
|
||||||
|
_event.tags.push(ba.Tag());
|
||||||
|
}
|
||||||
|
if (ba.Address == request.tx.From) {
|
||||||
|
ba.Address = request.tx.Change;
|
||||||
|
_event.tags.push(ba.Tag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIgnitionAndParentTag(_event);
|
||||||
|
updateBitcoinTip(_event);
|
||||||
|
event = _event;
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
URL(): string {
|
URL(): string {
|
||||||
let ignitionID = undefined;
|
let ignitionID = undefined;
|
||||||
@@ -181,47 +279,38 @@ export class Rocket {
|
|||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
PendingAMRAuctions(): AMRAuction[] {
|
PendingAMRAuctionsMap(): Map<string, AMRAuction> {
|
||||||
let auctions: AMRAuction[] = [];
|
let m = new Map<string, AMRAuction>();
|
||||||
for (let t of this.Event.getMatchingTags('amr_auction')) {
|
for (let t of this.Event.getMatchingTags('amr_auction')) {
|
||||||
if (t.length == 2) {
|
let auction = AMRAuctionFromTag(t, this.Event);
|
||||||
let items = t[1].split(':');
|
if (auction.Validate()) {
|
||||||
if (items.length == 6) {
|
|
||||||
let a = new AMRAuction(this.Event);
|
|
||||||
a.RxAddress = items[0];
|
|
||||||
a.StartPrice = parseInt(items[2], 10);
|
|
||||||
a.EndPrice = parseInt(items[3], 10);
|
|
||||||
a.Merits = parseInt(items[4], 10);
|
|
||||||
|
|
||||||
let ids = items[5].match(/.{1,64}/g);
|
|
||||||
if (ids) {
|
|
||||||
for (let id of ids) {
|
|
||||||
a.AMRIDs.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let amrs = this.ApprovedMeritRequests();
|
let amrs = this.ApprovedMeritRequests();
|
||||||
let failed = false;
|
let failed = false;
|
||||||
for (let id of a.AMRIDs) {
|
for (let id of auction.AMRIDs) {
|
||||||
let amr = amrs.get(id);
|
let amr = amrs.get(id);
|
||||||
if (!amr) {
|
if (!amr) {
|
||||||
failed = true;
|
failed = true;
|
||||||
} else {
|
} else {
|
||||||
if (!a.Owner) {
|
if (!auction.Owner) {
|
||||||
a.Owner = amr.Pubkey;
|
auction.Owner = amr.Pubkey;
|
||||||
} else if (a.Owner != amr.Pubkey) {
|
} else if (auction.Owner != amr.Pubkey) {
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!failed) {
|
if (!failed) {
|
||||||
auctions.push(a);
|
m.set(auction.ID(), auction);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('this should not happen, bug!');
|
throw new Error('this should not happen, bug!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
return auctions;
|
PendingAMRAuctions(): AMRAuction[] {
|
||||||
|
return Array.from(this.PendingAMRAuctionsMap(), ([_, amr]) => {
|
||||||
|
return amr;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
CanThisAMRBeSold(amr: string): boolean {
|
CanThisAMRBeSold(amr: string): boolean {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
@@ -280,7 +369,7 @@ export class Rocket {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
UpsertProduct(id: string, price: number, maxSales?: number): NDKEvent {
|
UpsertProduct(id: string, price: number, maxSales?: number): NDKEvent {
|
||||||
this.PrepareForUpdate();
|
this.PrepareForUpdate(id);
|
||||||
let event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
let event = new NDKEvent(this.Event.ndk, this.Event.rawEvent());
|
||||||
event.created_at = Math.floor(new Date().getTime() / 1000);
|
event.created_at = Math.floor(new Date().getTime() / 1000);
|
||||||
let existingProducts = this.CurrentProducts();
|
let existingProducts = this.CurrentProducts();
|
||||||
@@ -353,9 +442,12 @@ export class Rocket {
|
|||||||
}
|
}
|
||||||
this.Event.tags = newTags;
|
this.Event.tags = newTags;
|
||||||
}
|
}
|
||||||
PrepareForUpdate() {
|
PrepareForUpdate(id?: string) {
|
||||||
this.RemoveDuplicateTags();
|
this.RemoveDuplicateTags();
|
||||||
this.RemoveProofs();
|
this.RemoveProofs();
|
||||||
|
if (id) {
|
||||||
|
this.AppendEventToBloom(id);
|
||||||
|
}
|
||||||
this.Event.sig = undefined;
|
this.Event.sig = undefined;
|
||||||
}
|
}
|
||||||
constructor(event: NDKEvent) {
|
constructor(event: NDKEvent) {
|
||||||
@@ -420,7 +512,14 @@ export class RocketAMR {
|
|||||||
LeadTimeUpdate: number;
|
LeadTimeUpdate: number;
|
||||||
Merits: number;
|
Merits: number;
|
||||||
Extra: { eventAMR: AMRAuction };
|
Extra: { eventAMR: AMRAuction };
|
||||||
|
Tag(): NDKTag {
|
||||||
|
return [
|
||||||
|
'merit',
|
||||||
|
`${this.Pubkey}:${this.ID}:${this.LeadTime}:${this.LeadTimeUpdate}:${this.Merits}`
|
||||||
|
];
|
||||||
|
}
|
||||||
SatsOwed(): number {
|
SatsOwed(): number {
|
||||||
|
//if rocket creator is acting as custodian instead of using a cashu mint
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
SatsPaid(): number {
|
SatsPaid(): number {
|
||||||
@@ -619,7 +718,12 @@ export async function ValidateZapPublisher(rocket: NDKEvent, zap: NDKEvent): Pro
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type AMRAuctionStatus = 'PENDING' | 'OPEN' | 'TX DETECTED' | 'SOLD & PENDING RATIFICATION' | 'CHECKING MEMPOOL';
|
type AMRAuctionStatus =
|
||||||
|
| 'PENDING'
|
||||||
|
| 'OPEN'
|
||||||
|
| 'TX DETECTED'
|
||||||
|
| 'SOLD & PENDING RATIFICATION'
|
||||||
|
| 'CHECKING MEMPOOL';
|
||||||
|
|
||||||
export class AMRAuction {
|
export class AMRAuction {
|
||||||
AMRIDs: string[];
|
AMRIDs: string[];
|
||||||
@@ -630,14 +734,14 @@ export class AMRAuction {
|
|||||||
RocketD: string;
|
RocketD: string;
|
||||||
RocketP: string;
|
RocketP: string;
|
||||||
Merits: number;
|
Merits: number;
|
||||||
Event: NDKEvent;
|
Event: NDKEvent | undefined;
|
||||||
Extra: { rocket: Rocket };
|
Extra: { rocket: Rocket };
|
||||||
Status(
|
ID(): string {
|
||||||
rocket: Rocket,
|
this.AMRIDs.sort();
|
||||||
bitcoinTip: number,
|
return sha256(''.concat(...this.AMRIDs).trim());
|
||||||
transactions?: txs
|
}
|
||||||
): AMRAuctionStatus {
|
Status(rocket: Rocket, bitcoinTip: number, transactions?: txs): AMRAuctionStatus {
|
||||||
let status:AMRAuctionStatus = "PENDING"
|
let status: AMRAuctionStatus = 'PENDING';
|
||||||
if (transactions && transactions.Address != this.RxAddress) {
|
if (transactions && transactions.Address != this.RxAddress) {
|
||||||
throw new Error('invalid address');
|
throw new Error('invalid address');
|
||||||
}
|
}
|
||||||
@@ -663,17 +767,14 @@ export class AMRAuction {
|
|||||||
pending.RxAddress == this.RxAddress &&
|
pending.RxAddress == this.RxAddress &&
|
||||||
pending.AMRIDs[0] == this.AMRIDs[0] //todo: check whole array
|
pending.AMRIDs[0] == this.AMRIDs[0] //todo: check whole array
|
||||||
) {
|
) {
|
||||||
found = true
|
found = true;
|
||||||
if (status == "CHECKING MEMPOOL") {
|
if (status == 'CHECKING MEMPOOL') {
|
||||||
if (
|
if (Math.floor(new Date().getTime() / 1000) < transactions.LastUpdate + 60000) {
|
||||||
Math.floor(new Date().getTime() / 1000) < transactions.LastUpdate + 60000
|
|
||||||
) {
|
|
||||||
status = 'OPEN';
|
status = 'OPEN';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -720,12 +821,14 @@ export class AMRAuction {
|
|||||||
Validate(): boolean {
|
Validate(): boolean {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
if (
|
if (
|
||||||
this.Owner?.length != 64 ||
|
(this.Owner && this.Owner.length != 64) ||
|
||||||
!this.StartPrice ||
|
!this.StartPrice ||
|
||||||
!this.EndPrice ||
|
!this.EndPrice ||
|
||||||
!validate(this.RxAddress) ||
|
!validate(this.RxAddress) ||
|
||||||
this.RocketP.length != 64
|
this.RocketP.length != 64 ||
|
||||||
|
!this.Merits
|
||||||
) {
|
) {
|
||||||
|
//console.log(780, this, (this.Owner && this.Owner.length != 64), !this.StartPrice, !this.EndPrice, !validate(this.RxAddress), this.RocketP.length != 64, !this.Merits)
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
for (let id of this.AMRIDs) {
|
for (let id of this.AMRIDs) {
|
||||||
@@ -804,6 +907,9 @@ export class BitcoinAssociation {
|
|||||||
Address: string | undefined;
|
Address: string | undefined;
|
||||||
Event: NDKEvent;
|
Event: NDKEvent;
|
||||||
Balance: number;
|
Balance: number;
|
||||||
|
Tag(): NDKTag {
|
||||||
|
return ['address', `${this.Pubkey}:${this.Address}`];
|
||||||
|
}
|
||||||
Validate(): boolean {
|
Validate(): boolean {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
if (this.Pubkey.length != 64) {
|
if (this.Pubkey.length != 64) {
|
||||||
@@ -891,3 +997,41 @@ export class Product {
|
|||||||
this.Event = event;
|
this.Event = event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MeritPurchase {
|
||||||
|
auction: AMRAuction;
|
||||||
|
buyer: string;
|
||||||
|
tx: txo;
|
||||||
|
rocket: Rocket;
|
||||||
|
Validate(): boolean {
|
||||||
|
//todo: at least validate the utxo format
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
constructor(rocket: Rocket, auction: AMRAuction, buyer: string, tx: txo) {
|
||||||
|
this.rocket = rocket;
|
||||||
|
this.auction = auction;
|
||||||
|
this.buyer = buyer;
|
||||||
|
this.tx = tx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AMRAuctionFromTag(t: NDKTag, rocket: NDKEvent): AMRAuction {
|
||||||
|
let a = new AMRAuction(rocket);
|
||||||
|
if (t.length == 2) {
|
||||||
|
let items = t[1].split(':');
|
||||||
|
if (items.length == 6) {
|
||||||
|
a.RxAddress = items[0];
|
||||||
|
a.StartPrice = parseInt(items[2], 10);
|
||||||
|
a.EndPrice = parseInt(items[3], 10);
|
||||||
|
a.Merits = parseInt(items[4], 10);
|
||||||
|
|
||||||
|
let ids = items[5].match(/.{1,64}/g);
|
||||||
|
if (ids) {
|
||||||
|
for (let id of ids) {
|
||||||
|
a.AMRIDs.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export class txs {
|
|||||||
let amount = 0;
|
let amount = 0;
|
||||||
let height = tx.status.block_height ? tx.status.block_height : 0;
|
let height = tx.status.block_height ? tx.status.block_height : 0;
|
||||||
let txid = tx.txid;
|
let txid = tx.txid;
|
||||||
|
let change: string[] = [];
|
||||||
for (let vout of tx.vout) {
|
for (let vout of tx.vout) {
|
||||||
let address = vout.scriptpubkey_address;
|
let address = vout.scriptpubkey_address;
|
||||||
if (address && address.trim() == this.Address) {
|
if (address && address.trim() == this.Address) {
|
||||||
@@ -141,8 +142,11 @@ export class txs {
|
|||||||
if (value) {
|
if (value) {
|
||||||
amount += parseInt(value, 10);
|
amount += parseInt(value, 10);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
change.push(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let vin of tx.vin) {
|
for (let vin of tx.vin) {
|
||||||
let address = vin.prevout.scriptpubkey_address;
|
let address = vin.prevout.scriptpubkey_address;
|
||||||
if (address && validate(address)) {
|
if (address && validate(address)) {
|
||||||
@@ -152,9 +156,12 @@ export class txs {
|
|||||||
t.From = address;
|
t.From = address;
|
||||||
t.To = this.Address;
|
t.To = this.Address;
|
||||||
t.ID = txid;
|
t.ID = txid;
|
||||||
|
if (change.length == 1) {
|
||||||
|
t.Change = change[0];
|
||||||
|
}
|
||||||
possibles.set(address, t);
|
possibles.set(address, t);
|
||||||
} else {
|
} else {
|
||||||
console.log(156, vin)
|
console.log(156, vin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,5 +181,6 @@ export class txo {
|
|||||||
To: string;
|
To: string;
|
||||||
Amount: number;
|
Amount: number;
|
||||||
Height: number;
|
Height: number;
|
||||||
|
Change: string;
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
|
||||||
import * as Table from '@/components/ui/table';
|
import * as Table from '@/components/ui/table';
|
||||||
import { AMRAuction, Rocket } from '@/event_helpers/rockets';
|
import { AMRAuction, MeritPurchase, Rocket } from '@/event_helpers/rockets';
|
||||||
import { ndk } from '@/ndk';
|
import { ndk } from '@/ndk';
|
||||||
import { bitcoinTip, getIncomingTransactions, txs } from '@/stores/bitcoin';
|
import { bitcoinTip, getIncomingTransactions, txs } from '@/stores/bitcoin';
|
||||||
import { currentUser } from '@/stores/session';
|
import { currentUser } from '@/stores/session';
|
||||||
@@ -13,6 +12,9 @@
|
|||||||
import Heading from '../../components/Heading.svelte';
|
import Heading from '../../components/Heading.svelte';
|
||||||
import Login from '../../components/Login.svelte';
|
import Login from '../../components/Login.svelte';
|
||||||
import MeritAuctions from '../../stateupdaters/MeritAuctions.svelte';
|
import MeritAuctions from '../../stateupdaters/MeritAuctions.svelte';
|
||||||
|
import BuyAmr from '../../components/BuyAMR.svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { base } from '$app/paths';
|
||||||
let rocketEvents = $ndk.storeSubscribe([{ kinds: [31108 as number] }], { subId: 'all_rockets' });
|
let rocketEvents = $ndk.storeSubscribe([{ kinds: [31108 as number] }], { subId: 'all_rockets' });
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
rocketEvents?.unsubscribe();
|
rocketEvents?.unsubscribe();
|
||||||
@@ -89,12 +91,7 @@
|
|||||||
for (let [address, txo] of txs.From()) {
|
for (let [address, txo] of txs.From()) {
|
||||||
for (let [_, ba] of r.BitcoinAssociations()) {
|
for (let [_, ba] of r.BitcoinAssociations()) {
|
||||||
if (ba.Address == txo.From) {
|
if (ba.Address == txo.From) {
|
||||||
return {
|
return new MeritPurchase(r, amrAuction, ba.Pubkey, txo);
|
||||||
auction: amrAuction,
|
|
||||||
buyer: ba.Pubkey,
|
|
||||||
txid: txo.ID,
|
|
||||||
sats: txo.Amount
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,29 +105,39 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
nextSoldButNotInState.subscribe((t) => {
|
nextSoldButNotInState.subscribe((t) => {
|
||||||
if (t) console.log(t);
|
if (t) {
|
||||||
|
//console.log(t.rocket.UpsertMeritTransfer(t)?.rawEvent());
|
||||||
|
let e = t.rocket.UpsertMeritTransfer(t);
|
||||||
|
if (e) {
|
||||||
|
e.publish().then((x) => {
|
||||||
|
console.log(goto(`${base}/${new Rocket(e).URL()}`));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//t.rocket.UpsertMeritTransfer(t)?.publish()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let nostrocket = derived(rockets, ($rockets) => {
|
||||||
|
let rocket: Rocket | undefined = undefined;
|
||||||
|
for (let r of $rockets) {
|
||||||
|
if (
|
||||||
|
r.Name() == 'NOSTROCKET' &&
|
||||||
|
r.Event.pubkey == 'd91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075'
|
||||||
|
) {
|
||||||
|
//we consume the current list of bitcoin addresses from Nostrocket as a service so that users don't need to add a new address for every rocket
|
||||||
|
//todo: make this dependent on votepower not my pubkey
|
||||||
|
//todo: also allow rockets to have their own list of addresses so they can be used without nostrocket
|
||||||
|
rocket = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rocket;
|
||||||
});
|
});
|
||||||
|
|
||||||
transactions.subscribe((t) => {});
|
transactions.subscribe((t) => {});
|
||||||
|
|
||||||
let noAssociatedBitcoinAddress = derived(
|
|
||||||
[currentUser, pendingSales],
|
|
||||||
([$currentUser, $pendingSales]) => {
|
|
||||||
let show = false;
|
|
||||||
if ($currentUser) {
|
|
||||||
for (let [r, a] of $pendingSales) {
|
|
||||||
if (a.length > 0 && !r.BitcoinAssociations().get($currentUser.pubkey)) {
|
|
||||||
console.log($currentUser.pubkey, r.Name());
|
|
||||||
show = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return show;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $noAssociatedBitcoinAddress}<AssociateBitcoinAddress />{/if}
|
{#if $nostrocket}<AssociateBitcoinAddress rocket={$nostrocket} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
{#each $pendingSales as [rocket, amr] (rocket.Event.id)}
|
{#each $pendingSales as [rocket, amr] (rocket.Event.id)}
|
||||||
@@ -172,9 +179,9 @@
|
|||||||
}}>{p.RxAddress}</Table.Cell
|
}}>{p.RxAddress}</Table.Cell
|
||||||
>
|
>
|
||||||
<Table.Cell
|
<Table.Cell
|
||||||
>{#if p.Status(rocket, $bitcoinTip.height, $transactions.get(p.RxAddress)) == 'OPEN'}<Button
|
>{#if p.Status(rocket, $bitcoinTip.height, $transactions.get(p.RxAddress)) == 'OPEN'}<BuyAmr
|
||||||
>BUY NOW</Button
|
auction={p}
|
||||||
>{/if}</Table.Cell
|
/>{/if}</Table.Cell
|
||||||
>
|
>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user