diff --git a/src/lib/event_helpers/merits.ts b/src/lib/event_helpers/merits.ts index 17090d5..02b11da 100644 --- a/src/lib/event_helpers/merits.ts +++ b/src/lib/event_helpers/merits.ts @@ -36,7 +36,6 @@ 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 99289fb..a7d8ce0 100644 --- a/src/lib/event_helpers/rockets.ts +++ b/src/lib/event_helpers/rockets.ts @@ -120,16 +120,82 @@ export class Rocket { } return event; } + PendingAMRAuctions(): AMRAuction[] { + let auctions:AMRAuction[] = []; + for (let t of this.Event.getMatchingTags('amr_auction')) { + if (t.length == 2) { + let items = t[1].split(':'); + 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) + } + } + auctions.push(a) + } + } + } + return auctions + } + CanThisAMRBeSold(amr:string):boolean { + let valid = true + let existing = this.ApprovedMeritRequests().get(amr) + if (!existing) { + valid = false + } + if (existing && existing.LeadTime > 0) { + valid = false + } + let pending = this.PendingAMRAuctions() + for (let p of pending) { + if (p.AMRIDs.includes(amr)) { + valid = false + } + } + return valid + } UpsertAMRAuction(request: AMRAuction): NDKEvent | undefined { + //todo: validate that all items in the request exist and the total amount is correct, from same pubkey let event: NDKEvent | undefined = undefined; + let invalid = false; if (request.ValidateAgainstRocket(this)) { this.PrepareForUpdate(); event = new NDKEvent(this.Event.ndk, this.Event.rawEvent()); event.created_at = Math.floor(new Date().getTime() / 1000); - + let totalMerits = 0; + let requestIDs: string = ''; + for (let id of request.AMRIDs) { + let amr = this.ApprovedMeritRequests().get(id); + if (!amr) { + invalid = true; + } else { + if (amr.LeadTime > 0 || amr.Pubkey != request.Owner) { + invalid = true; + } else { + totalMerits += amr.Merits; + requestIDs += id; + } + } + } + if (totalMerits != request.Merits) { + invalid = true; + } + event.tags.push([ + 'amr_auction', + `${request.RxAddress}:${0}:${request.StartPrice}:${request.EndPrice}:${request.Merits}:${requestIDs}` + ]); // event.tags.push(['proof_full', JSON.stringify(request.Event!.rawEvent())]); updateIgnitionAndParentTag(event); } + if (invalid) { + event = undefined; + } return event; } UpsertProduct(id: string, price: number, maxSales?: number): NDKEvent { @@ -453,7 +519,7 @@ export async function ValidateZapPublisher(rocket: NDKEvent, zap: NDKEvent): Pro export class AMRAuction { AMRIDs: string[]; - Owner: string|undefined; + Owner: string | undefined; StartPrice: number; EndPrice: number; RxAddress: string; @@ -461,25 +527,27 @@ export class AMRAuction { RocketP: string; Merits: number; Event: NDKEvent; + Extra: { rocket: Rocket }; 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(['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 + + e.tags.push(['price', this.StartPrice + ':' + this.EndPrice]); + e.tags.push(['merits', this.Merits.toString()]); + e.tags.push(['onchain', this.RxAddress]); + return e; } Push(amr: RocketAMR) { if (this.Owner && amr.Pubkey != this.Owner) { - throw new Error("invalid pubkey") + throw new Error('invalid pubkey'); } - this.Owner = amr.Pubkey + this.Owner = amr.Pubkey; this.AMRIDs.push(amr.ID); this.StartPrice += amr.Merits; this.EndPrice += amr.Merits; @@ -487,17 +555,17 @@ export class AMRAuction { } Pop(amr: RocketAMR) { if (this.AMRIDs.includes(amr.ID) && amr.Pubkey == this.Owner) { - let n:string[] = [] + let n: string[] = []; for (let id of this.AMRIDs) { if (id != amr.ID) { - n.push(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 + this.StartPrice -= amr.Merits; + this.EndPrice -= amr.Merits; + this.Merits -= amr.Merits; } } Validate(): boolean { @@ -525,26 +593,30 @@ export class AMRAuction { if (!rocketAMR || (rocketAMR && rocketAMR.Pubkey != this.Owner) || rocketAMR.LeadTime > 0) { valid = false; } + for (let a of rocket.PendingAMRAuctions()) { + if (a.AMRIDs.includes(id)) { + 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 + this.Merits = 0; + this.EndPrice = 0; + this.StartPrice = 0; if (rocket && !event) { - this.RxAddress = address?address:"" + 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")) { + 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.AMRIDs.push(id[1]); } } this.Owner = event.author.pubkey; @@ -557,6 +629,11 @@ export class AMRAuction { this.StartPrice = start; this.EndPrice = end; } + let merits = event.tagValue('merits'); + if (merits) { + let int = parseInt(merits, 10); + this.Merits = int; + } let address = event.tagValue('onchain'); if (address) { if (validate(address)) { diff --git a/src/lib/ndk.ts b/src/lib/ndk.ts index 28d008d..250f1da 100644 --- a/src/lib/ndk.ts +++ b/src/lib/ndk.ts @@ -19,8 +19,8 @@ const _ndk = new NDKSvelte({ }); //we need to check for browser environment before calling window because svelte is slightly retarded when used client side only -if (browser && window.indexedDB) { - _ndk.cacheAdapter = new NDKCacheAdapterDexie({ dbName: 'gulag' }); -} +// if (browser && window.indexedDB) { +// _ndk.cacheAdapter = new NDKCacheAdapterDexie({ dbName: 'gulag' }); +// } export const ndk = writable(_ndk); diff --git a/src/routes/sellmerits/+page.svelte b/src/routes/sellmerits/+page.svelte index 06c4796..94b6569 100644 --- a/src/routes/sellmerits/+page.svelte +++ b/src/routes/sellmerits/+page.svelte @@ -30,21 +30,21 @@ } m.set(e.pubkey + e.dTag, e); } - return Array.from(m, ([_, e]) => e); + return Array.from(m, ([_, e]) => new Rocket(e)); }); let myMeritRequests = derived([currentUser, rockets], ([$currentUser, $rockets]) => { let merits = new Map(); if ($currentUser) { for (let r of $rockets) { - let parsedRocket = new Rocket(r); + //let parsedRocket = new Rocket(r); let _merits: RocketAMR[] = []; - for (let [_, amr] of parsedRocket.ApprovedMeritRequests()) { + for (let [_, amr] of r.ApprovedMeritRequests()) { if (amr.Pubkey == $currentUser.pubkey) { _merits.push(amr); } } - merits.set(parsedRocket, _merits); + merits.set(r, _merits); } } return merits; @@ -53,71 +53,73 @@ let selected_amrs = new Map(); function toggleSelected(amr: RocketAMR, rocket: Rocket) { if (!selected_amrs.has(rocket.Event.id)) { - selected_amrs.set(rocket.Event.id, new AMRAuction(rocket.Event)) + selected_amrs.set(rocket.Event.id, new AMRAuction(rocket.Event)); } - let existing = selected_amrs.get(rocket.Event.id)! + let existing = selected_amrs.get(rocket.Event.id)!; if (existing.AMRIDs.includes(amr.ID)) { - existing.Pop(amr) + existing.Pop(amr); } else { - existing.Push(amr) + existing.Push(amr); } - selected_amrs.set(rocket.Event.id, existing) + selected_amrs.set(rocket.Event.id, existing); selected_amrs = selected_amrs; } - function getSelectedStatus(rocket: string, id:string, data: Map):boolean { - let has = false + function getSelectedStatus(rocket: string, id: string, data: Map): boolean { + let has = false; let amr = data.get(rocket); if (amr) { - has = amr.AMRIDs.includes(id) + has = amr.AMRIDs.includes(id); } - return has + return has; } - function getMerits(rocket:string, data:Map):number { - let m = data.get(rocket) - console.log(m) - let merits = 0 + function getMerits(rocket: string, data: Map): number { + let m = data.get(rocket); + console.log(m); + let merits = 0; if (m && m.Merits) { - merits = m.Merits + merits = m.Merits; } - return merits + return merits; } - - - // function getTotal(auction:AMRAuction):number { - // let total = 0 - // for (let [_, amr] of list) { - // total += amr.Merits - // } - // return total - // } + // function getTotal(auction:AMRAuction):number { + // let total = 0 + // for (let [_, amr] of list) { + // total += amr.Merits + // } + // return total + // } // function getAllForRocket(rocket:Rocket, selected:Map):Map { - // let thisRocket = new Map() - // for (let [_, amr] of selected) { - // if (amr.RocketD == rocket.Name() && amr.RocketP == rocket.Event.author.pubkey) { - // thisRocket.set(amr.AMRID, amr) - // } - // } + // let thisRocket = new Map() + // for (let [_, amr] of selected) { + // if (amr.RocketD == rocket.Name() && amr.RocketP == rocket.Event.author.pubkey) { + // thisRocket.set(amr.AMRID, amr) + // } + // } // return thisRocket - // } + // } - function publish(amr:AMRAuction, rocket:Rocket) { + function publish(amr: AMRAuction, rocket: Rocket) { if (!$ndk.signer) { throw new Error('no ndk signer found'); } - + let author = $currentUser; if (!author) { throw new Error('no current user'); } - amr.RxAddress = bitcoinAddress - if (!amr.Validate()) {throw new Error("invalid")} - if (!amr.ValidateAgainstRocket(rocket)) {throw new Error("invalid against rocket")} - let e = amr.GenerateEvent() - e.ndk = $ndk + amr.RxAddress = bitcoinAddress; + if (!amr.Validate()) { + throw new Error('invalid'); + } + if (!amr.ValidateAgainstRocket(rocket)) { + throw new Error('invalid against rocket'); + } + let e = amr.GenerateEvent(); + e.ndk = $ndk; e.author = author; e.publish().then((x) => { console.log(x, e); @@ -126,7 +128,7 @@ }); } - let bitcoinAddress:string = "" + let bitcoinAddress: string = '';

Trade your Merits for Sats

@@ -141,46 +143,77 @@ Selected AMR - Elegible Merits + Status + Receiving Address 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 rocket.PendingAMRAuctions() as p} + + + {p.AMRIDs.length > 1?"multiple":p.AMRIDs[0]} + {p.Merits} + Pending + {p.RxAddress} + {p.Merits} {/each} + + {#each amr as a, id (a.ID)} + {#if rocket.CanThisAMRBeSold(a.ID)} + + { + toggleSelected(a, rocket); + }} + /> + { + goto(`${base}/rockets/merits/${a.ID}`); + }}>{a.ID.substring(0, 6)} + {a.Merits} + Elegible + + {a.Merits} + + {/if} + {/each} {#if selected_amrs.get(rocket.Event.id)} -
You are selling {selected_amrs.get(rocket.Event.id)?.Merits} Merits
- - + 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 index b2aeb31..87b4b20 100644 --- a/src/stateupdaters/MeritAuctions.svelte +++ b/src/stateupdaters/MeritAuctions.svelte @@ -1,43 +1,58 @@ -{#each $validAuctionRequests as [_, a]}{console.log(a)}}>{a.Event.id}{/each} \ No newline at end of file +