From 47f2e1666cb88bd1130ed8bb623dd3a83e9320ff Mon Sep 17 00:00:00 2001 From: gsovereignty Date: Thu, 1 Aug 2024 18:27:41 +0800 Subject: [PATCH] problem: can't list amrs for sale --- package-lock.json | 29 +++ package.json | 1 + src/components/CalculateSats.svelte | 12 +- src/components/Login.svelte | 5 + src/components/MeritRequestDashboard.svelte | 3 +- src/components/MeritRequests.svelte | 10 +- src/components/MeritSummaryCard.svelte | 12 +- src/components/MeritTrades.svelte | 43 +++++ src/components/RocketDashboard.svelte | 3 +- src/lib/event_helpers/merits.ts | 1 + src/lib/event_helpers/rockets.ts | 147 +++++++++++++++- src/route_helpers/Rocket.svelte | 54 ++++++ src/routes/buymerits/+page.svelte | 166 +++++++++++++++++ src/routes/rockets/[ignition]/+page.svelte | 62 ++----- src/routes/sellmerits/+page.svelte | 186 ++++++++++++++++++++ src/stateupdaters/MeritAuctions.svelte | 43 +++++ 16 files changed, 704 insertions(+), 73 deletions(-) create mode 100644 src/components/MeritTrades.svelte create mode 100644 src/route_helpers/Rocket.svelte create mode 100644 src/routes/buymerits/+page.svelte create mode 100644 src/routes/sellmerits/+page.svelte create mode 100644 src/stateupdaters/MeritAuctions.svelte diff --git a/package-lock.json b/package-lock.json index 57bcec7..27cff77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@nostr-dev-kit/ndk-svelte": "^2.2.15", "@nostr-dev-kit/ndk-svelte-components": "^2.2.16", "@sveltejs/adapter-static": "^3.0.1", + "bitcoin-address-validation": "^2.2.3", "bits-ui": "^0.21.10", "clsx": "^2.1.1", "cmdk-sv": "^0.0.17", @@ -1676,6 +1677,19 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base58-js": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/base58-js/-/base58-js-1.0.5.tgz", + "integrity": "sha512-LkkAPP8Zu+c0SVNRTRVDyMfKVORThX+rCViget00xdgLRrKkClCTz1T7cIrpr69ShwV5XJuuoZvMvJ43yURwkA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1687,6 +1701,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bitcoin-address-validation": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bitcoin-address-validation/-/bitcoin-address-validation-2.2.3.tgz", + "integrity": "sha512-1uGCGl26Ye8JG5qcExtFLQfuib6qEZWNDo1ZlLlwp/z7ygUFby3IxolgEfgMGaC+LG9csbVASLcH8fRLv7DIOg==", + "dependencies": { + "base58-js": "^1.0.0", + "bech32": "^2.0.0", + "sha256-uint8array": "^0.10.3" + } + }, "node_modules/bits-ui": { "version": "0.21.10", "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.10.tgz", @@ -4492,6 +4516,11 @@ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" }, + "node_modules/sha256-uint8array": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/sha256-uint8array/-/sha256-uint8array-0.10.7.tgz", + "integrity": "sha512-1Q6JQU4tX9NqsDGodej6pkrUVQVNapLZnvkwIhddH/JqzBZF1fSaxSWNY6sziXBE8aEa2twtGkXUrwzGeZCMpQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index cba0737..4f66cf2 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@nostr-dev-kit/ndk-svelte": "^2.2.15", "@nostr-dev-kit/ndk-svelte-components": "^2.2.16", "@sveltejs/adapter-static": "^3.0.1", + "bitcoin-address-validation": "^2.2.3", "bits-ui": "^0.21.10", "clsx": "^2.1.1", "cmdk-sv": "^0.0.17", diff --git a/src/components/CalculateSats.svelte b/src/components/CalculateSats.svelte index 15be69d..7e1457e 100644 --- a/src/components/CalculateSats.svelte +++ b/src/components/CalculateSats.svelte @@ -44,15 +44,15 @@ > - Calculate the sats + How much should you request? Calculate the sats of the request according to your hour rate.Use this form to calculate how many Sats you should claim. This information will be public and permanent. - - - - + + + + {#if calcSats}
Result: {calcSats.toFixed(0)} sats
{/if} diff --git a/src/components/Login.svelte b/src/components/Login.svelte index 3d26654..4426046 100644 --- a/src/components/Login.svelte +++ b/src/components/Login.svelte @@ -5,6 +5,8 @@ import { Avatar } from '@nostr-dev-kit/ndk-svelte-components'; import { NDKNip07Signer } from '@nostr-dev-kit/ndk'; import { onMount } from 'svelte'; + import { goto } from '$app/navigation'; + import { base } from '$app/paths'; onMount(() => { if (localStorage.getItem('signed-in')) { @@ -48,6 +50,9 @@ My Account + {goto(`${base}/sellmerits`)}}>Sell Merits + Buy Merits + Settings Support diff --git a/src/components/MeritRequestDashboard.svelte b/src/components/MeritRequestDashboard.svelte index 359bced..f99776b 100644 --- a/src/components/MeritRequestDashboard.svelte +++ b/src/components/MeritRequestDashboard.svelte @@ -5,6 +5,7 @@ import { getRocketURL, parseProblem } from '@/helpers'; import type { NDKEvent } from '@nostr-dev-kit/ndk'; import MeritSummaryCard from './MeritSummaryCard.svelte'; + import { Rocket } from '@/event_helpers/rockets'; export let rocket: NDKEvent; export let merit: MeritRequest; @@ -37,6 +38,6 @@
- +
diff --git a/src/components/MeritRequests.svelte b/src/components/MeritRequests.svelte index 57a11f2..8eacb60 100644 --- a/src/components/MeritRequests.svelte +++ b/src/components/MeritRequests.svelte @@ -12,17 +12,17 @@ import { onDestroy } from 'svelte'; import { derived } from 'svelte/store'; - export let rocket: NDKEvent; - let parsedRocket = new Rocket(rocket); + //export let rocket: NDKEvent; + export let parsedRocket:Rocket;// = new Rocket(rocket); let _merits = $ndk.storeSubscribe( - [{ '#a': [`31108:${rocket.author.pubkey}:${rocket.dTag}`], kinds: [1409 as NDKKind] }], + [{ '#a': [`31108:${parsedRocket.Event.author.pubkey}:${parsedRocket.Name()}`], kinds: [1409 as NDKKind] }], { - subId: `${rocket.dTag}_merits` + subId: `${parsedRocket.Name()}_merits` } ); - let _votes = $ndk.storeSubscribe({ '#a': [RocketATagFilter(rocket)], kinds: [1410 as NDKKind] }); + let _votes = $ndk.storeSubscribe({ '#a': [RocketATagFilter(parsedRocket.Event)], kinds: [1410 as NDKKind] }); onDestroy(() => { _merits?.unsubscribe(); diff --git a/src/components/MeritSummaryCard.svelte b/src/components/MeritSummaryCard.svelte index c9e42a3..296d1ca 100644 --- a/src/components/MeritSummaryCard.svelte +++ b/src/components/MeritSummaryCard.svelte @@ -21,15 +21,15 @@ import MeritComment from './MeritComment.svelte'; export let merit: MeritRequest; - export let rocket: NDKEvent; + //export let rocket: NDKEvent; let cuckPrice: number | undefined = undefined; let result: VoteDirection | undefined; - let parsedRocket = new Rocket(rocket); + export let parsedRocket:Rocket; let _votes = $ndk.storeSubscribe( - { '#a': [RocketATagFilter(rocket)], '#e': [merit.ID], kinds: [1410 as NDKKind] }, + { '#a': [RocketATagFilter(parsedRocket.Event)], '#e': [merit.ID], kinds: [1410 as NDKKind] }, { subId: merit.ID } @@ -80,7 +80,7 @@ updatedRocket.ndk = $ndk; updatedRocket.sign().then(() => { updatedRocket.publish().then(() => { - goto(`${base}/rockets/${getRocketURL(rocket)}`); + goto(`${base}/rockets/${getRocketURL(parsedRocket.Event)}`); }); }); } @@ -170,7 +170,7 @@
Votes
{#if $votes.size == 0}Waiting for existing {new Rocket(rocket).Name()} Merit + >Waiting for existing {parsedRocket.Name()} Merit holders to vote {/if} @@ -219,7 +219,7 @@ {:else if result == 'blackball'} REJECTED {:else if !result} - + {/if} diff --git a/src/components/MeritTrades.svelte b/src/components/MeritTrades.svelte new file mode 100644 index 0000000..8725501 --- /dev/null +++ b/src/components/MeritTrades.svelte @@ -0,0 +1,43 @@ + + +
+
+ + + + {rocket.getMatchingTags('d')[0][1]} + + + + Merit Requests + + + + + Trade + + + + +
+
+ Trade {parsedRocket.Name()} Merits +
+
diff --git a/src/components/RocketDashboard.svelte b/src/components/RocketDashboard.svelte index f7b23ae..003cdef 100644 --- a/src/components/RocketDashboard.svelte +++ b/src/components/RocketDashboard.svelte @@ -11,6 +11,7 @@ import ProposedProducts from './ProposedProducts.svelte'; import Todo from './Todo.svelte'; import UpdateMission from './UpdateMission.svelte'; + import { Rocket } from '@/event_helpers/rockets'; export let rocket: NDKEvent; @@ -40,7 +41,7 @@ - + Actions diff --git a/src/lib/event_helpers/merits.ts b/src/lib/event_helpers/merits.ts index 02b11da..17090d5 100644 --- a/src/lib/event_helpers/merits.ts +++ b/src/lib/event_helpers/merits.ts @@ -36,6 +36,7 @@ export class MeritRequest { } IncludedInRocketState(rocket: Rocket): boolean { let included = rocket.ApprovedMeritRequests(); + console.log(39, rocket, included.get(this.ID)) return Boolean(included.get(this.ID)); } BasicValidation(): boolean { diff --git a/src/lib/event_helpers/rockets.ts b/src/lib/event_helpers/rockets.ts index ba2d297..99289fb 100644 --- a/src/lib/event_helpers/rockets.ts +++ b/src/lib/event_helpers/rockets.ts @@ -1,6 +1,7 @@ import { NDKEvent, type NDKTag } from '@nostr-dev-kit/ndk'; import { MapOfVotes, MeritRequest, Votes } from './merits'; import { getAuthorizedZapper } from '@/helpers'; +import validate from 'bitcoin-address-validation'; export class Rocket { Event: NDKEvent; @@ -37,12 +38,12 @@ export class Rocket { return amr; } TotalMerits(): number { - let total = 0 - let amr = this.ApprovedMeritRequests() + let total = 0; + let amr = this.ApprovedMeritRequests(); for (let [_, _amr] of amr) { - total += _amr.Merits + total += _amr.Merits; } - return total + return total; } ValidateAMRProof(amrProof: NDKEvent): boolean { let result = false; @@ -114,7 +115,19 @@ export class Rocket { event = new NDKEvent(this.Event.ndk, this.Event.rawEvent()); event.created_at = Math.floor(new Date().getTime() / 1000); event.tags.push(['merit', `${request.Pubkey}:${request.ID}:0:0:${request.Merits}`]); - event.tags.push(['proof_full', JSON.stringify(signedProof)]); + event.tags.push(['proof_full', JSON.stringify(signedProof.rawEvent())]); + updateIgnitionAndParentTag(event); + } + return event; + } + UpsertAMRAuction(request: AMRAuction): NDKEvent | undefined { + let event: NDKEvent | undefined = undefined; + if (request.ValidateAgainstRocket(this)) { + this.PrepareForUpdate(); + event = new NDKEvent(this.Event.ndk, this.Event.rawEvent()); + event.created_at = Math.floor(new Date().getTime() / 1000); + + event.tags.push(['proof_full', JSON.stringify(request.Event!.rawEvent())]); updateIgnitionAndParentTag(event); } return event; @@ -238,6 +251,7 @@ export class RocketAMR { LeadTime: number; LeadTimeUpdate: number; Merits: number; + Extra: {}; SatsOwed(): number { return 0; } @@ -436,3 +450,126 @@ export async function ValidateZapPublisher(rocket: NDKEvent, zap: NDKEvent): Pro // }).catch(()=>{reject(false)}) }); } + +export class AMRAuction { + AMRIDs: string[]; + Owner: string|undefined; + StartPrice: number; + EndPrice: number; + RxAddress: string; + RocketD: string; + RocketP: string; + Merits: number; + Event: NDKEvent; + GenerateEvent(): NDKEvent { + let e = new NDKEvent(); + e.kind = 1412; + e.created_at = Math.floor(new Date().getTime() / 1000); + for (let id of this.AMRIDs) { + e.tags.push(["request", id]) + } + e.tags.push(['a', `31108:${this.RocketP}:${this.RocketD}`]); + //todo: allow user to set start and end auction price + + e.tags.push(['price', this.StartPrice + ":" + this.EndPrice]); + e.tags.push(["onchain", this.RxAddress]) + return e + } + Push(amr: RocketAMR) { + if (this.Owner && amr.Pubkey != this.Owner) { + throw new Error("invalid pubkey") + } + this.Owner = amr.Pubkey + this.AMRIDs.push(amr.ID); + this.StartPrice += amr.Merits; + this.EndPrice += amr.Merits; + this.Merits += amr.Merits; + } + Pop(amr: RocketAMR) { + if (this.AMRIDs.includes(amr.ID) && amr.Pubkey == this.Owner) { + let n:string[] = [] + for (let id of this.AMRIDs) { + if (id != amr.ID) { + n.push(id) + } + } + this.AMRIDs = n; + //todo: allow user to set start/end price + this.StartPrice -= amr.Merits + this.EndPrice -= amr.Merits + this.Merits -= amr.Merits + } + } + Validate(): boolean { + let valid = true; + if ( + this.Owner?.length != 64 || + !this.StartPrice || + !this.EndPrice || + !validate(this.RxAddress) || + this.RocketP.length != 64 + ) { + valid = false; + } + for (let id of this.AMRIDs) { + if (id.length != 64) { + valid = false; + } + } + return valid; + } + ValidateAgainstRocket(rocket: Rocket): boolean { + let valid = true; + for (let id of this.AMRIDs) { + let rocketAMR = rocket.ApprovedMeritRequests().get(id); + if (!rocketAMR || (rocketAMR && rocketAMR.Pubkey != this.Owner) || rocketAMR.LeadTime > 0) { + valid = false; + } + } + + //todo: check if this AMR is already listed for sale + return valid; + } + constructor(rocket?: NDKEvent, event?: NDKEvent, address?: string) { + this.AMRIDs = []; + this.Merits = 0 + this.EndPrice = 0 + this.StartPrice = 0 + if (rocket && !event) { + this.RxAddress = address?address:"" + this.RocketD = rocket.dTag!; + this.RocketP = rocket.author.pubkey; + } + if (event && !rocket) { + this.Event = event + for (let id of event.getMatchingTags("request")) { + if (id && id.length == 2 && id[1].length == 64) { + this.AMRIDs.push(id[1]) + } + } + this.Owner = event.author.pubkey; + let price = event.tagValue('price'); + if (price) { + let _start = price.split(':')[0]; + let _end = price.split(':')[1]; + let start = parseInt(_start, 10); + let end = parseInt(_end, 10); + this.StartPrice = start; + this.EndPrice = end; + } + let address = event.tagValue('onchain'); + if (address) { + if (validate(address)) { + this.RxAddress = address; + } + } + let _rocket = event.tagValue('a'); + if (_rocket) { + if (_rocket.split(':').length == 3) { + this.RocketP = _rocket.split(':')[1]; + this.RocketD = _rocket.split(':')[2]; + } + } + } + } +} diff --git a/src/route_helpers/Rocket.svelte b/src/route_helpers/Rocket.svelte new file mode 100644 index 0000000..cf0fb6a --- /dev/null +++ b/src/route_helpers/Rocket.svelte @@ -0,0 +1,54 @@ + diff --git a/src/routes/buymerits/+page.svelte b/src/routes/buymerits/+page.svelte new file mode 100644 index 0000000..1d48724 --- /dev/null +++ b/src/routes/buymerits/+page.svelte @@ -0,0 +1,166 @@ + + +

Trade your Merits for Sats

+ +{#if $currentUser} + {#each $myMeritRequests as [rocket, amr]} + {#if amr.length > 0} +

ROCKET: {rocket.Name()}

+ + + + + Selected + AMR + Elegible + Merits + Sats (approx) + + + + {#each amr as a, id (a.ID)} + + { + toggleSelected(a, rocket); + }} + /> + { + goto(`${base}/rockets/merits/${a.ID}`); + }}>{a.ID.substring(0, 6)} + {a.LeadTime == 0} + {a.Merits} + {a.Merits} + + {/each} + + + {#if selected.size > 0} +
You are selling {getTotal(getAllForRocket(rocket, selected))} Merits
+
+ + +
+ {/if} + {/if} + {/each} +{:else}{/if} diff --git a/src/routes/rockets/[ignition]/+page.svelte b/src/routes/rockets/[ignition]/+page.svelte index d74620b..b0a65ba 100644 --- a/src/routes/rockets/[ignition]/+page.svelte +++ b/src/routes/rockets/[ignition]/+page.svelte @@ -1,70 +1,31 @@ +{#if dTag && pubkey} + +{/if} {#if latestRocketEvent && $latestRocketEvent} {:else} IGNITION: {rIgnitionOrActual}
- NAME: {rName}
- PUBKEY: {rPubkey}
+ NAME: {dTag}
+ PUBKEY: {pubkey}
{/if} \ No newline at end of file diff --git a/src/routes/sellmerits/+page.svelte b/src/routes/sellmerits/+page.svelte new file mode 100644 index 0000000..06c4796 --- /dev/null +++ b/src/routes/sellmerits/+page.svelte @@ -0,0 +1,186 @@ + + +

Trade your Merits for Sats

+ +{#if $currentUser} + {#each $myMeritRequests as [rocket, amr]} + {#if amr.length > 0} +

ROCKET: {rocket.Name()}

+ + + + + Selected + AMR + Elegible + Merits + Sats (approx) + + + + {#each amr as a, id (a.ID)} + + { + toggleSelected(a, rocket); + }} + /> + { + goto(`${base}/rockets/merits/${a.ID}`); + }}>{a.ID.substring(0, 6)} + {a.LeadTime == 0} + {a.Merits} + {a.Merits} + + {/each} + + + {#if selected_amrs.get(rocket.Event.id)} +
You are selling {selected_amrs.get(rocket.Event.id)?.Merits} Merits
+
+ + +
+ {/if} + {/if} + {/each} +{:else}{/if} + \ No newline at end of file diff --git a/src/stateupdaters/MeritAuctions.svelte b/src/stateupdaters/MeritAuctions.svelte new file mode 100644 index 0000000..b2aeb31 --- /dev/null +++ b/src/stateupdaters/MeritAuctions.svelte @@ -0,0 +1,43 @@ + + +{#each $validAuctionRequests as [_, a]}{console.log(a)}}>{a.Event.id}{/each} \ No newline at end of file