diff --git a/desk/bare/app/fund.hoon b/desk/bare/app/fund.hoon index e312616..1c643e1 100644 --- a/desk/bare/app/fund.hoon +++ b/desk/bare/app/fund.hoon @@ -1,6 +1,6 @@ /- f=fund, p=pals /- fd=fund-data, fd-1=fund-data-1, fd-0=fund-data-0 -/+ fh=fund-http, fc=fund-chain, fj=fund-proj, fp=fund-prof +/+ fh=fund-http, fc=fund-chain, fj=fund-proj, fp=fund-prof, fx=fund-xtra /+ config, default-agent, rudder, *sss /+ dbug, verb, tonic, vita-client /~ pagz page:fd /web/fund/page @@ -340,7 +340,7 @@ ?~ p.syn cor ((slog u.p.syn) cor) :: scan response :: - [%scan typ=@ ~] + [%scan typ=@ sob=@ tob=@ ~] ?> =(our.bol sip) ?> ?=(xfer:f typ.pat.pat) ?+ -.syn cor @@ -364,11 +364,18 @@ =/ loz=loglist:fc ?+(-.dif loglist.dif %disavow ~) =* por (pj-abed:pj-core sip nam) =/ pro=proj:proj:f pro:por + :: NOTE: Worker is assumed to initiate all withdrawal transactions + =/ wad=addr:f work:(need contract.pro) + :: NOTE: Unused for now, but may be useful for notifications later + =/ [sob=(unit bloq:f) tob=(unit bloq:f)] + =+ par=|=(v=@ `(unit bloq:f)`?:(=(%$ v) ~ `(slav %ud v))) + [(par sob.pat.pat) (par tob.pat.pat)] :: NOTE: We group by transaction for multisend cases (which :: always occur for withdrawals) + :: NOTE: We assume that all incoming transactions are single send =/ xap=(map xact:f (list [addr:f @])) %+ roll loz - |= [nex=event-log:rpc:ethereum:fc acc=(map xact:f (list [addr:f cash:f]))] + |= [nex=event-log:rpc:ethereum:fc acc=(map xact:f (list [addr:f @]))] =/ act=xact:f [block-number transaction-hash]:(need mined.nex) =/ who=addr:f (snag ?-(typ.pat.pat %depo 1, %with 2) `(list @ux)`topics.nex) :: NOTE: If there are 4 topics, then this is an NFT transfer, for @@ -387,12 +394,16 @@ %- ~(rep by xap) |= [[act=xact:f fez=(list [addr:f @])] acc=(list card)] :_ acc + =/ fom=addr:f ?-(typ.pat.pat %depo -:(head fez), %with wad) =/ nfz=(list [@ tape]) (turn fez |=([a=addr:f i=@] [i (uri.payment.pro i)])) - :* %pass `path`(welp pat /(scot %ud p.act)/(scot %ux q.act)) + :* %pass `path`(welp pat /(scot %ud p.act)/(scot %ux q.act)/(scot %ux fom)) %arvo %k %fard q.byk.bol %nfz-okay noun+!>([~ nfz limits.payment.pro ~]) == - %- turn :_ |=([x=xact:f c=cash:f a=(set addr:f)] (pj-mk-pruf:por x c typ.pat.pat)) + %- turn :_ + |= [act=xact:f cas=cash:f adz=(set addr:f)] + =/ fom=addr:f ?-(typ.pat.pat %depo (head ~(tap in adz)), %with wad) + (pj-mk-pruf:por act fom cas typ.pat.pat) ^- (list [xact:f cash:f (set addr:f)]) %- ~(rep by xap) |= [[act=xact:f fez=(list [addr:f cash:f])] acc=(list [xact:f cash:f (set addr:f)])] @@ -417,24 +428,28 @@ renew-surl:action == :: - [%fund %proj sip=@ nam=@ %scan typ=@ boq=@ adr=@ ~] + [%fund %proj sip=@ nam=@ %scan typ=@ sob=@ tob=@ boq=@ hax=@ fom=@ ~] ?+ syn cor [%khan %arow *] ?: ?=(%.n -.p.syn) ((slog leaf+ ~) cor) =/ lag=flag:f [(slav %p sip.pat) (slav %tas nam.pat)] =/ typ=xfer:f ;;(xfer:f typ.pat) - =/ act=xact:f [(slav %ud boq.pat) (slav %ux adr.pat)] + =/ act=xact:f [(slav %ud boq.pat) (slav %ux hax.pat)] + =/ fom=addr:f (slav %ux fom.pat) =+ !<(gud=(set @) q.p.p.syn) - (emit (pj-mk-pruf:(pj-abed:pj-core lag) act ~(wyt in gud) typ)) + (emit (pj-mk-pruf:(pj-abed:pj-core lag) act fom ~(wyt in gud) typ)) == == :: ++ open ^+ cor - =/ old=? !(~(has by pf-myn) our.bol) + =/ kiq=? + ?| !(~(has by pf-myn) our.bol) :: pre-v1.1 need any watch paths + =>(scan-vold:watch:audit ?=(^ .)) :: pre-%6 %proj need new-style watch paths + == =. cor renew-surl:action =. cor watch-pals:action - =. cor (renew-projs:action old) + =. cor (renew-projs:action kiq) cor ++ action |% @@ -471,7 +486,7 @@ ?=(^ (find /gall/use/fund pat)) == ++ renew-projs :: invoke project level triggers - |= kiq=? + |= kiq=bean ^+ cor =/ lis=(list flag:f) ~(tap in ~(key by pj-our)) |- @@ -481,7 +496,7 @@ :: cor ?. &(kiq =(p.i.lis our.bol)) pj-abet:(pj-abed:pj-core i.lis) - pj-abet:(pj-push:(pj-abed:pj-core i.lis) [%redo ~]) + pj-abet:(pj-push:(pj-abed:pj-core i.lis) [%redo ~ ~]) == ++ toggle-profs :: toggle profile follow status |= [ahn=? siz=(set @p)] @@ -493,6 +508,47 @@ $(cor pf-abet:(pf-push:(pf-abed:pf-core i.lis) pod), lis t.lis) -- :: +++ audit + |% + ++ watch + |% + ++ boat :: filtered outgoing watch paths + |= fil=$-([wire @p @tas] ?) ~+ + ^- (set path) + %- ~(rep in ~(key by wex.bol)) + |= [[wyr=(pole knot) sip=@p dap=@tas] acc=(set path)] + ?.((fil wyr sip dap) acc (~(put in acc) wyr)) + ++ scan-vold :: %fund-watcher old-style watch paths + ~+ + ^- (set path) + %- boat + |= [wyr=(pole knot) sip=@p dap=@tas] + ?& =(sip our.bol) + =(dap %fund-watcher) + ?=([%fund %proj sip=@ nam=@ %scan typ=@ ~] wyr) + ?=(^ (slaw %p sip.wyr)) + ?=(xfer:f typ.wyr) + == + ++ scan-vnow :: %fund-watcher new-style watch paths + ~+ + ^- (set path) + %- boat + |= [wyr=(pole knot) sip=@p dap=@tas] + ^- bean + ?& =(sip our.bol) + =(dap %fund-watcher) + ?=([%fund %proj sip=@ nam=@ %scan typ=@ sob=@ tob=@ ~] wyr) + ?=(^ (slaw %p sip.wyr)) + ?=(xfer:f typ.wyr) + ?=(%$ tob.wyr) :: NOTE: empty end indicates ongoing watch path + == + ++ scan-vany :: %fund-watcher any-style watch paths + ~+ + ^- (set path) + (~(uni in scan-vold) scan-vnow) + -- +-- +:: ++ pj-core |_ [lag=flag:f pro=proj:proj:f gon=_|] ++ pj-core . @@ -534,9 +590,13 @@ ++ pj-pj-bloq ^- @ =/ pre=path (en-beam [our.bol %fund-watcher da+now.bol] /) - =/ pat=path (welp pj-pa-pub /scan/depo) + =- ?~(pat 0 .^(@ %gx :(welp pre /block u.pat /atom))) + ^- pat=(unit path) =+ .^(pap=(map path *) %gx (welp pre /dogs/configs/noun)) - ?.((~(has by pap) pat) 0 .^(@ %gx :(welp pre /block pat /atom))) + %+ find:fx ~(tap in ~(key by pap)) + =/ pre=path (welp pj-pa-pub /scan/depo) + =/ pen=@ud (lent pre) + |=(n=path &(=(pre (scag pen n)) =(%$ (rear n)))) ++ pj-pj-push |= pod=prod:proj:f ^+ pj-core @@ -557,26 +617,31 @@ %poke fund-poke+!>([%proj lag pod]) == ++ pj-mk-pruf - |= [act=xact:f amo=cash:f dir=xfer:f] + |= [act=xact:f fom=addr:f amo=cash:f dir=xfer:f] ^- card - =* con (need contract:pro) - :: FIXME: Assumes that all withdrawals are prompted by the worker - =/ adr=addr:f ?-(dir %depo safe:con, %with work:con) - (pj-mk-card p.lag [%mula %pruf ship=~ cash=amo when=[act adr] note=dir]) + (pj-mk-card p.lag [%mula %pruf ship=~ cash=amo when=[act fom] note=dir]) ++ pj-mk-scan - |= oat=(unit oath:f) + |= [oat=(unit oath:f) sob=(unit bloq:f) tob=(unit bloq:f)] ^- (list card) =+ oaf=(fall (clap oat contract.pro head) *oath:f) - %- zing - %+ turn (scan-cfgz:fc oaf payment.pro) + %- zing %+ turn (scan-cfgz:fc oaf sob tob payment.pro) |= [suf=path cfg=config:fc] ^- (list card) =/ pat=path (welp pj-pa-pub suf) - =+ car=[%pass pat %agent [our.bol %fund-watcher] act=~] - ;: welp - ?.((~(has by wex.bol) pat our.bol %fund-watcher) ~ [car(act [%leave ~])]~) - [car(act [%poke %fund-watcher-poke !>([%watch pat cfg])])]~ - == + =/ pen=@ud (sub (lent pat) 2) :: path w/o start/end delimiters + =+ car=[%pass pat=~ %agent [our.bol %fund-watcher] act=~] + %- snoc :_ `card`car(pat pat, act [%poke %fund-watcher-poke !>([%watch pat cfg])]) + ^- (list card) + ?^ tob ~ + %- zing %+ turn + (skim ~(tap in scan-vany:watch:audit) |=(p=path =((scag pen pat) (scag pen p)))) + |= old=path + :- car(pat old, act [%leave ~]) :: clear out: + ?. ?| (~(has in scan-vold:watch:audit) old) :: - all old watch paths + &(!=(old pat) =(~ tob) =(%$ (rear old))) :: - all new indefinite paths + == + ~ + [car(pat old, act [%poke %fund-watcher-poke !>([%clear old])])]~ :: ++ pj-do-read |= pod=prod:proj:f @@ -638,7 +703,7 @@ (pj-pj-push pod(p.dif pj-pj-bloq)) :: %redo - =? cor !?=(?(%born %prop) ~(stat pj:fj pro)) (emil (pj-mk-scan ~)) + =? cor !?=(?(%born %prop) ~(stat pj:fj pro)) (emil (pj-mk-scan ~ +.pod)) (pj-pj-push pod) :: %mula @@ -673,7 +738,7 @@ :: %lock =- pj-core(cor (emil -)) - (pj-mk-scan oat.pod) + (pj-mk-scan oat.pod ~ ~) == == :: meta prods :: @@ -692,7 +757,7 @@ ?< ~|(bad-pj-push+mes (~(has by pj-our) pag)) ?: pj-is-myn pj-core =. cor (proj-push ~ (copy:pj-puz proj-subs pj-pa-sub wat)) - =. cor (emit [%pass wat %agent [our dap]:bol %poke fund-poke+!>([%proj pag %redo ~])]) + =. cor (emit [%pass wat %agent [our dap]:bol %poke fund-poke+!>([%proj pag %redo ~ ~])]) pj-core :: ?(%join %exit) diff --git a/desk/bare/cfg/base.hoon b/desk/bare/cfg/base.hoon index f10cc77..934b8e3 100644 --- a/desk/bare/cfg/base.hoon +++ b/desk/bare/cfg/base.hoon @@ -4,14 +4,19 @@ :: %- malt ^- (list [@tas vase]) +=/ alc=tape (flop ~['2' '2' 'B' 'W' '4' '-' 'c' 'W' 'F' 'F' 'B' 'J' 'n' 'b' 'b' '3' '1' 'r' 'L' 'n' 'V' 'W' 'F' 'i' 'j' 'C' 'l' 'G' 'u' '1' 'E' '3']) :~ [%debug !>(%|)] [%point !>(~tocwex)] [%sign-addr !>(0x78e6.603f.0393.3e0f.ebc6.88a7.734e.a8b4.b63f.42d0)] [%safe-addr !>(0x944a.5ba3.3bd9.5a5e.45e5.41c3.dd5a.0732.e90d.58e6)] [%uprl-herz !>(~h1)] - [%scan-herz !>(~s30)] + [%scan-herz !>(~m2)] [%scan-tout !>(~m1)] [%scan-bloq !>(10)] + [%alch-akey !>(`@t`(crip alc))] + [%rpce-ethe !>(`@t`(crip "https://eth-mainnet.g.alchemy.com/v2/{alc}"))] + [%rpce-sepo !>(`@t`(crip "https://eth-sepolia.g.alchemy.com/v2/{alc}"))] + [%feat-oraz !>(`(list @p)`~[~reb ~bitdeg ~roswet ~nisfeb ~hosdys ~ridlyd ~darlur ~mocbel ~posdeg ~dalten ~firbex ~moddux ~pandux ~fogbus])] [%meta-site !>('https://tocwexsyndicate.com')] [%meta-help !>('https://docs.tocwexsyndicate.com')] [%meta-tlon !>('https://tlon.network/lure/~tocwex/syndicate-public')] diff --git a/desk/bare/cfg/xtra/fake.hoon b/desk/bare/cfg/xtra/fake.hoon index 48da099..fbeaf59 100644 --- a/desk/bare/cfg/xtra/fake.hoon +++ b/desk/bare/cfg/xtra/fake.hoon @@ -2,12 +2,17 @@ :: %- malt ^- (list [@tas vase]) +=/ alc=tape (flop ~['E' 'f' 'd' 't' 'J' 'X' 'S' 'C' 'H' 'I' 'C' 'F' 'D' 'B' 'w' '4' '3' 'R' 'H' 'f' 'y' 'f' 'b' 'y' '1' 'S' 'A' 'E' 'E' 'E' 'R' '2']) :~ [%debug !>(%&)] [%point !>(~zod)] [%sign-addr !>(0xcbbd.2aab.5ee5.09e8.531a.b407.d48f.c93c.dc25.e1ad)] [%safe-addr !>(0xcbbd.2aab.5ee5.09e8.531a.b407.d48f.c93c.dc25.e1ad)] [%uprl-herz !>(~m1)] [%scan-herz !>(~s10)] - [%scan-tout !>(~s30)] + [%scan-tout !>(~s10)] [%scan-bloq !>(5)] + [%alch-akey !>(`@t`(crip alc))] + [%rpce-ethe !>(`@t`(crip "https://eth-mainnet.g.alchemy.com/v2/{alc}"))] + [%rpce-sepo !>(`@t`(crip "https://eth-sepolia.g.alchemy.com/v2/{alc}"))] + [%feat-oraz !>(`(list @p)`(gulf 0 255))] == diff --git a/desk/bare/desk.docket-0 b/desk/bare/desk.docket-0 index 6d0b825..e921ef4 100644 --- a/desk/bare/desk.docket-0 +++ b/desk/bare/desk.docket-0 @@ -2,7 +2,7 @@ info+'A sovereign P2P work completion funder/tracker with on-chain settlement.' color+0x11.1111 image+'https://sfo3.digitaloceanspaces.com/sarlev-sarsen/sarlev-sarsen/2024.4.29..23.58.31..2b02.0c49.ba5e.353f-IMG_4726.jpeg' - version+[1 3 0] + version+[1 4 0] website+'https://www.tocwexsyndicate.com/' license+'GNU GPLv3' base+'fund' diff --git a/desk/bare/lib/config.hoon b/desk/bare/lib/config.hoon index 064637a..4059721 100644 --- a/desk/bare/lib/config.hoon +++ b/desk/bare/lib/config.hoon @@ -23,12 +23,19 @@ =- "export const FUND_{var} = {dat};" ^- [var=tape dat=tape] :- (cuss (turn (trip key) |=(t=@tD ?.(=('-' t) t '_')))) - ?+ -.val !! - [%atom %f *] ?:(!<(bean val) "true" "false") - [%atom %ux *] "'{['0' 'x' ((x-co:co 40) !<(@ux val))]}'" - [%atom %ud *] "'{(a-co:co !<(@ val))}'" - [%atom %p *] "'{(scow %p !<(@ val))}'" - [%atom %dr *] "'{(a-co:co !<(@ val))}'" :: FIXME: Doesn't really work + ?+ -.val "undefined" + [%atom %f *] ?:(!<(bean val) "true" "false") + [%atom %ux *] "'{['0' 'x' ((x-co:co 40) !<(@ux val))]}'" + [%atom %ud *] "'{(a-co:co !<(@ val))}'" + [%atom %p *] "'{(scow %p !<(@ val))}'" + [%atom %dr *] "'{(a-co:co !<(@ val))}'" :: FIXME: Doesn't really work [%atom dim=@ ~] "'{(scow %tas !<(@ val))}'" + :: :: + :: [%hold *] + :: =/ typ=type ~(repo ut -.val) + :: ?+ typ "undefined" + :: :: [%hint *] + :: [%fork [%atom [%| %~ %0]] [%cell *]] "" + :: == == -- diff --git a/desk/bare/lib/fund/alien.hoon b/desk/bare/lib/fund/alien.hoon new file mode 100644 index 0000000..a53d62d --- /dev/null +++ b/desk/bare/lib/fund/alien.hoon @@ -0,0 +1,64 @@ +:: /lib/fund/alien/hoon: foreign/external interface helper functions for %fund +:: +/- cn=contacts, se=settings +/+ ff=fund-form +|_ bol=bowl:gall +:: +:: +contacts-rolo: %contacts app "rolodex" data (ship/info map) +:: +++ contacts-rolo + ~+ + ^- rolodex:cn + =/ pre=path (en-beam [our.bol %contacts da+now.bol] /) + ?. .^(? %gu (snoc pre %$)) *rolodex:cn + =+ .^(non=noun %gx (weld pre /all/noun)) + (fall ((soft rolodex:cn) non) *rolodex:cn) +:: +:: +contacts-ship: %contacts app ship data (index into "rolodex") +:: +++ contacts-ship + |= sip=@p ~+ + ^- (unit contact:cn) + =/ rol=rolodex:cn contacts-rolo + ?~ con=(~(get by rol) sip) ~ + ?@ for.u.con ~ + ?@ con.for.u.con ~ + `con.for.u.con +:: +:: +settings-calm: %settings app "calm engine" data (key/value map) +:: +++ settings-calm + ~+ + ^- bucket:se + =/ pre=path (en-beam [our.bol %settings da+now.bol] /) + ?. .^(? %gu (snoc pre %$)) *bucket:se + ?. .^(? %gx (weld pre /has-bucket/landscape/'calmEngine'/noun)) *bucket:se + =+ .^(non=noun %gx (weld pre /bucket/landscape/'calmEngine'/noun)) + =/ dat=data:se (fall ((soft data:se) non) [%bucket *bucket:se]) + ?.(?=(%bucket -.dat) *bucket:se bucket.dat) +:: +:: +ship-tytl: ship title, derived from %settings and %contacts +:: +++ ship-tytl + |= sip=@p ~+ + ^- tape + =/ dis=val:se (~(gut by settings-calm) 'disableNicknames' [%b |]) + =/ def=tape "{}" + ?: &(?=(%b -.dis) p.dis) def + ?~ con=(contacts-ship sip) def + ?: =(%$ nickname.u.con) def + (trip nickname.u.con) +:: +:: +ship-logo: ship logo (as url), derived from %settings and %contacts +:: +++ ship-logo + |= sip=@p ~+ + ^- tape + =/ dis=val:se (~(gut by settings-calm) 'disableAvatars' [%b |]) + =/ def=tape (surt:enrl:ff sip) + ?: &(?=(%b -.dis) p.dis) def + ?~ con=(contacts-ship sip) def + ?~ avatar.u.con def + ?: =(%$ u.avatar.u.con) def + (trip u.avatar.u.con) +-- diff --git a/desk/bare/lib/fund/chain.hoon b/desk/bare/lib/fund/chain.hoon index 90456cc..d2b8b05 100644 --- a/desk/bare/lib/fund/chain.hoon +++ b/desk/bare/lib/fund/chain.hoon @@ -6,8 +6,8 @@ ++ xlis :: chain metadata list ~+ ^- (list xeta) - :~ [1 %ethereum 'https://eth.drpc.org'] - [11.155.111 %sepolia 'https://sepolia.drpc.org'] + :~ [1 %ethereum !<(@t (slot:fund-config %rpce-ethe))] + [11.155.111 %sepolia !<(@t (slot:fund-config %rpce-sepo))] :: [10 %optimism ?] :: [8.453 %base ?] :: [42.161 %arbitrum ?] @@ -84,7 +84,7 @@ [`@`tag:(~(got by xmap) chain.swa) `@`symbol.swa] == ++ scan-cfgz - |= [oat=oath swa=swap] + |= [oat=oath sob=(unit bloq) tob=(unit bloq) swa=swap] ^- (list [path config]) ?: |(=(0x0 safe.oat) ?=(%chip -.swa)) ~ ?~ con=(~(get by smap) [chain addr]:swa) ~ @@ -92,13 +92,13 @@ %+ turn `(list @tas)`~[%depo %with] |= act=term =/ [src=@ux dst=@ux] ?:(?=(%depo act) [0x0 safe.oat] [safe.oat 0x0]) - :- /scan/[act] + :- /scan/[act]/[?~(sob %$ (scot %ud u.sob))]/[?~(tob %$ (scot %ud u.tob))] :* url=rpc.u.can eager=| refresh-rate=!<(@dr (slot:fund-config %scan-herz)) timeout-time=!<(@dr (slot:fund-config %scan-tout)) - from=p.xact.oat - to=~ + from=(fall sob p.xact.oat) + to=tob contracts=[addr.u.con]~ confirms=`!<(@ud (slot:fund-config %scan-bloq)) topics=?-(-.swa %coin ~[0x0 src dst], %enft ~[0x0 src dst 0x0]) diff --git a/desk/bare/lib/fund/core-0.hoon b/desk/bare/lib/fund/core/0.hoon similarity index 100% rename from desk/bare/lib/fund/core-0.hoon rename to desk/bare/lib/fund/core/0.hoon diff --git a/desk/bare/lib/fund/core-1.hoon b/desk/bare/lib/fund/core/1.hoon similarity index 100% rename from desk/bare/lib/fund/core-1.hoon rename to desk/bare/lib/fund/core/1.hoon diff --git a/desk/bare/lib/fund/form.hoon b/desk/bare/lib/fund/form.hoon index 45ab5f3..e460270 100644 --- a/desk/bare/lib/fund/form.hoon +++ b/desk/bare/lib/fund/form.hoon @@ -226,6 +226,25 @@ %meta [p.pok (trip -.q.pok)] %proj [p.pok "{(trip -.q.pok)}{?+(-.q.pok ~ ?(%bump %mula) ['-' (trip +<.q.pok)])}"] == + ++ swad :: [%coin 1 0x1 %wstr %wstr 6] => "Wrapped Star" + |= swa=^swap + ^- tape + =- "{tyt} ({typ})" + ^- [typ=tape tyt=tape] + :- ?-(-.swa %coin "Coin", %enft "NFT", %chip "Fiat") + ?+ symbol.swa (trip name.swa) + ?(%'USDC' %'fundUSDC') "USD Coin" + ?(%'WSTR' %'fundWSTR') "Wrapped Star" + ?(%'AZP' %'AZP-TEST') "Urbit Star" + == + ++ swap :: [%coin 1 0x1 %wstr %wstr 6] => "$WSTR" + |= swa=^swap + ^- tape + ?- -.swa + %coin (coin +.swa) + %enft (enft +.swa) + %chip (chip +.swa) + == ++ coin :: [1 0x1 %wstr %wstr 6] => "$WSTR" |= con=^coin ^- tape @@ -238,13 +257,11 @@ |= chi=^chip ^- tape "-{(cuss (trip symbol.chi))}" - ++ swap :: [%coin 1 0x1 %wstr %wstr 6] => "$WSTR" - |= swa=^swap + ++ xeta :: [1 %ethereum ''] => "Ethereum" + |= xet=^xeta ^- tape - ?- -.swa - %coin (coin +.swa) - %enft (enft +.swa) - %chip (chip +.swa) + ?+ tag.xet (caps:fx (trip tag.xet)) + %sepolia "Sepolia (Testnet)" == ++ role :: %orac => "oracle" |= rol=^role diff --git a/desk/bare/lib/fund/http.hoon b/desk/bare/lib/fund/http.hoon index 61eddd1..9b3f55b 100644 --- a/desk/bare/lib/fund/http.hoon +++ b/desk/bare/lib/fund/http.hoon @@ -1,7 +1,7 @@ :: /lib/fund/http/hoon: http data and helper functions for %fund :: -/- fd=fund-data, fm=fund-meta -/+ *fund-proj, fk=fund-core, ff=fund-form, fc=fund-chain, fx=fund-xtra +/- fd=fund-data +/+ *fund-proj, fk=fund-core, ff=fund-form, fc=fund-chain, fa=fund-alien, fx=fund-xtra /+ config, mu=manx-utils, rudder, tonic |% :: @@ -10,7 +10,7 @@ ++ auth |= bol=bowl:gall ~+ ^- bean - =+ peers=.^((map ship *) /ax/(scot %p our.bol)//(scot %da now.bol)/peers) + =+ peers=.^((map ship *) %ax (en-beam [our.bol %$ da+now.bol] /peers)) ?| !=(%pawn (clan:title src.bol)) (~(has by peers) src.bol) == @@ -20,9 +20,9 @@ ++ burl |= bol=bowl:gall ~+ ^- tape - =+ erl=.^((unit @t) %ex /(scot %p our.bol)//(scot %da now.bol)/eauth/url) + =+ erl=.^((unit @t) %ex (en-beam [our.bol %$ da+now.bol] /eauth/url)) ?^ erl (scaj:fx (lent "/~/eauth") (trip u.erl)) - (head:en-purl:html .^(hart:eyre %e /(scot %p our.bol)/host/(scot %da now.bol))) + (head:en-purl:html .^(hart:eyre %e (en-beam [our.bol %host da+now.bol] /))) :: :: +alix: al(pine-)i(fy) (man)x (search/replace non-@tas alpine tags) :: @@ -311,7 +311,7 @@ ;button =id "fund-agis" =class "fund-tipi inline-flex items-center p-1.5 gap-x-2 rounded-md hover:bg-primary-550" =x-init "initTippy($el)" - ;+ (ship-logo src.bol) + ;+ (ship-logo src.bol bol) ;span(class "hidden sm:block font-bold"): {} ;img@"{(aset:enrl:ff %ellipsis)}"(class "hidden sm:block h-full"); ;div#fund-agis-opts(class "hidden") @@ -323,8 +323,7 @@ == == ;a.fund-butn-de-m/"/~/login?eauth&redirect={(trip url)}"(target "_blank"): login ~ - ;button#fund-butn-wallet.fund-butn-co-m: …loading… - ;select#fund-nfts-wallet.hidden; + ;button#fund-butn-wallet.fund-butn-co-m(x-text "$store.wallet.status"); == == ++ foot @@ -355,12 +354,41 @@ == -- ++ ship-card :: summary card for user ship - :: TODO: Consider changing the signature to take a (unit addr) for - :: cases when the address isn't yet available (e.g. project in - :: draft form) - |= [sip=@p tyt=tape hep=tape hel=tape adr=addr cid=@ud buz=marl] + |= [sip=@p bol=bowl:gall rol=role cid=@ud adr=addr] ^- manx - =/ tid=tape "fund-help-ship-{(trip (asci:fx (crip tyt)))}" + =/ [tyt=tape lin=tape nun=tape hep=tape] + ?+ rol [~ ~ ~ ~] + %work + :* tyt="Project Worker" + lin="user-guides/project-workers" + nun="no payment address set" + ^= hep + ^- tape ^~ + %+ rip 3 + ''' + The project worker is the identity which does the work defined in + the project overview and milestones. When a milestone is completed + and approved by the trusted oracle, the project worker receives the + listed payout for that milestone. + ''' + == + :: + %orac + :* tyt="Trusted Oracle" + lin="user-guides/trusted-oracles" + nun="no approval keys set" + ^= hep + ^- tape ^~ + %+ rip 3 + ''' + The role of the trusted oracle is to assess completion of the scope + of work. When a milestone review is requested, the oracle can chose + to mark it as complete by providing a cryptographically signed + message, allowing the worker to withdraw funds. + ''' + == + == + =/ tid=tape "fund-help-ship-{(trip rol)}-{(bloq:enjs:ff `@`sip)}" ;div(class "flex flex-col p-3 rounded-md border-2 border-secondary-450 gap-1") ;div(class "inline-flex gap-1 items-center") ;h6(class "leading-none tracking-widest"): {tyt} @@ -371,27 +399,31 @@ ;p: {hep} ;p ; To learn more, - ;a.text-link/"{(trip !<(@t (slot:config %meta-help)))}/{hel}"(target "_blank") + ;a.text-link/"{(trip !<(@t (slot:config %meta-help)))}/{lin}"(target "_blank") ; read the docs. == == == == ;div(class "inline-flex self-stretch justify-start items-center gap-2") - ;+ (~(ship-logo ..$ "h-20") sip) + ;+ (~(ship-logo ..$ "h-20") sip bol) ;div(class "grow shrink basis-0 flex-col justify-start items-start inline-flex") ;div(class "inline-flex items-center gap-1") - ;h5.text-lg.font-bold.tracking-tight: {} + ;h5.text-lg.font-bold.tracking-tight + ;+ (ship-tytl sip bol) + == ;+ (copy-butn (ship:enjs:ff sip)) == ;div(class "inline-flex items-center gap-1") - ;a/"{(esat:enrl:ff adr cid)}" - =target "_blank" - =class "fund-addr fund-addr-ens hover:text-link" - =x-init "initENS($el, '{(addr:enjs:ff adr)}')" - …loading… - == - ;+ (copy-butn (addr:enjs:ff adr)) + ;* ?: =(0x0 adr) :_ ~ ;p: {nun} + :~ ;a/"{(esat:enrl:ff adr cid)}" + =target "_blank" + =class "fund-addr fund-addr-ens hover:text-link" + =x-init "initENS($el, '{(addr:enjs:ff adr)}')" + …loading… + == + (copy-butn (addr:enjs:ff adr)) + == == ;div(class "self-stretch justify-between items-center inline-flex") ;div(class "inline-flex items-center gap-1") @@ -403,7 +435,9 @@ ;+ (copy-butn (bloq:enjs:ff `@`sip)) == ;div(class "inline-flex gap-1") - ;* buz + ;* ?: |(!=(our src):bol =(sip src.bol)) ~ + :_ ~ + ;a.fund-butn-ac-s/"{(chat:enrl:ff sip)}"(target "_blank"): 💬 == == == @@ -415,7 +449,7 @@ :: FIXME: This is hacky and wasteful, but attempting to use Alpine.js to :: just edit in/out the 'line-clamp-5' class doesn't work due to :: collisions with Twind.js. - ;div(class "w-full", x-data "\{expanded: {(trip ?:(?=(%verb typ) 'true' 'false'))}}") + ;div(class "w-full", x-data "\{expanded: {(bool:enjs:ff ?=(%verb typ))}}") :: FIXME: Attempting to apply a "to transparent" graident, but :: it's not working... :: after:bg-gradient-to-b after:from-inherit @@ -432,6 +466,9 @@ ;div(class "flex justify-center pt-2") ;* %+ turn ~[["!expanded" "true" "show more +"] ["expanded" "false" "show less -"]] |= [sow=tape qiq=tape txt=tape] + :: FIXME: Attempt at eliminating the button when the + :: clamp doesn't even truncate extra lines + :: =x-show "{sow} && needsClamp($el.parentElement.previousElementSibling, 5)" ;button.fund-butn-ac-s(type "button", x-show sow, x-on-click "expanded = {qiq}"): {txt} == == @@ -510,22 +547,24 @@ |= [emt=bean swa=(unit swap) mod=tape xoc=tape] ^- manx =/ ini=tape ?~(swa "undefined" "'{(trip symbol.u.swa)}'") + =/ its=tape "initTomSelect($el, \{empty: {(bool:enjs:ff emt)}})" + =/ uts=tape "updateTokenSelect({ini})" ;div(class cas, x-data ~) :: FIXME: This is a hack to make the 'selz' use uniform padding :: in the filter UI ;div(class "fund-form-group {?.(emt ~ (trip %p-0))}") ;select#proj-chain.fund-tsel =name "can" =required ~ - =x-init "initTomSelect($el, {(bool:enjs:ff emt)})" + =x-init its =x-on-change "updateTokenSelect" ;* =+ can=?~(swa id:(~(got by xmap:fc) %ethereum) chain.u.swa) %+ welp ?. emt ~ - :_ ~ ;option(value ""): Any Chain + :_ ~ ;option(value ~): Any Chain %+ turn xlis:fc |= xet=xeta ^- manx - :_ ; {(caps:fx (trip tag.xet))} + :_ ; {(xeta:enjs:ff xet)} :- %option ;: welp [%value (bloq:enjs:ff id.xet)]~ @@ -542,26 +581,44 @@ ;select#proj-token-options.hidden(required ~) ;* %+ welp ?. emt ~ - :_ ~ ;option(value ""): Any Token + :_ ~ ;option(value ~): Any Token %+ turn slis:fc |= swa=swap ^- manx - :_ ; {(trip name.swa)} + :_ ; {(swad:enjs:ff swa)} :- %option - ;: welp - [%value (trip symbol.swa)]~ - [%data-type (trip -.swa)]~ - [%data-chain (bloq:enjs:ff chain.swa)]~ - [%data-image (aset:enrl:ff symbol.swa)]~ + :~ [%value (trip symbol.swa)] + [%data-type (trip -.swa)] + [%data-chain (bloq:enjs:ff chain.swa)] + [%data-image (aset:enrl:ff symbol.swa)] + :: FIXME: Should handle %chip case more elegantly + [%data-href (esat:enrl:ff addr.swa chain.swa)] == == ;select#proj-token.fund-tsel =name "tok" =required ~ - =x-init "initTomSelect($el, {(bool:enjs:ff emt)}); updateTokenSelect({ini})" + =x-init :(welp its "; " uts) =x-model mod + =x-ref "token" =x-on-change xoc; ;* ?: emt ~ - :_ ~ ;label(for "tok"): Token + :_ ~ + ;div(class "inline-flex gap-1 items-center") + :: FIXME: Better if this were automatically populated by CSS + :: rules like all of the other s + ;label(for "tok") + ; Token + ;span(class "-ml-1 text-highlight1-500"): * + == + :: FIXME: It would be better if this were an element, + :: but that doesn't properly auto-update via 'x-bind-href' + ;button =type "button" + =x-on-click "openHREF($refs.token.tomselect.options[$refs.token.tomselect.activeOption.dataset.value].href, true)" + :: TODO: Remove static width & replace with outer + :: sizing control + ;img.w-6.fund-butn-icon@"{(aset:enrl:ff %etherscan)}"; + == + == == == ++ mile-ther :: milestone funding thermometer @@ -650,9 +707,13 @@ == == ++ ship-logo :: icon for a user ship - |= sip=@p + |= [sip=@p bol=bowl:gall] + ^- manx + (icon-logo %rect (~(ship-logo fa bol) sip)) + ++ ship-tytl :: title for a user ship + |= [sip=@p bol=bowl:gall] ^- manx - (icon-logo & (surt:enrl:ff sip)) + ;span(class "line-clamp-1 {cas}"): {(~(ship-tytl fa bol) sip)} ++ cash-bump :: bumper for cash amount |= [tan=manx ban=manx] ^- manx @@ -680,18 +741,18 @@ %dead "highlight1-400" == ++ icon-stax :: stack of icons (leftmost on top) - |= [box=bean liz=(list tape)] + |= [typ=?(%circ %rect) liz=(list tape)] ^- manx ;div(class "flex flex-row-reverse h-6 {cas}") ;* %+ turn (enum:fx (flop liz)) |= [lid=@ lin=tape] =+ lim=?:(=(0 lid) "" "-mr-3") - (~(icon-logo ..$ "relative h-full {lim}") box lin) + (~(icon-logo ..$ "relative h-full {lim}") typ lin) == - ++ icon-logo :: single icon circle - |= [box=bean lin=tape] + ++ icon-logo :: single icon image + |= [typ=?(%circ %rect) lin=tape] ^- manx - ;img@"{lin}"(class "fund-aset-{(trip ?:(box %boxx %circ))} {cas}"); + ;img@"{lin}"(class "fund-aset-{(trip typ)} {cas}"); ++ cheq-swix :: checkbox switch |= nam=tape ^- manx diff --git a/desk/bare/lib/fund/meta-0.hoon b/desk/bare/lib/fund/meta/0.hoon similarity index 100% rename from desk/bare/lib/fund/meta-0.hoon rename to desk/bare/lib/fund/meta/0.hoon diff --git a/desk/bare/lib/fund/proj-0.hoon b/desk/bare/lib/fund/proj/0.hoon similarity index 100% rename from desk/bare/lib/fund/proj-0.hoon rename to desk/bare/lib/fund/proj/0.hoon diff --git a/desk/bare/lib/fund/proj-1.hoon b/desk/bare/lib/fund/proj/1.hoon similarity index 100% rename from desk/bare/lib/fund/proj-1.hoon rename to desk/bare/lib/fund/proj/1.hoon diff --git a/desk/bare/lib/fund/proj/2.hoon b/desk/bare/lib/fund/proj/2.hoon new file mode 100644 index 0000000..3b465fc --- /dev/null +++ b/desk/bare/lib/fund/proj/2.hoon @@ -0,0 +1,187 @@ +/- *fund-proj-2 +/+ fc=fund-core, ff=fund-form, fx=fund-xtra +|% +:: +:: +pj: p(ro)j(ect) (library); helper door for $proj data +:: +++ pj + |_ proj + :: FIXME: All the `wox=@p` function signatures will be simplified + :: once a project stores/supports multiple different workers + +* miz `(list mile)`milestones + ++ stat :: project-wide status + ^- ^stat + status:mil:next + ++ cost :: summed milestone costs + ^- cash + (roll (turn miz |=(n=mile cost.n)) add) + ++ plej :: summed pledge amounts + ^- cash + %- ~(rep by pledges) + |=([[k=@p v=[^plej peta]] a=cash] (add a cash.v)) + ++ fill :: project-wide cost fill + ^- cash + |^ (add trib-cash pruf-cash) + ++ trib-cash + %- ~(rep by contribs) + |=([[k=addr v=[treb deta]] a=cash] (add a ?~(pruf.v 0 cash.u.pruf.v))) + ++ pruf-cash + %- ~(rep by proofs) + |=([[k=addr v=pruf] a=cash] (add a ?-(note.v %with 0, %depo cash.v))) + -- + ++ take :: project-wide claimed funds + ^- cash + %- roll :_ add + %+ turn miz + |= mil=mile + ?. ?& ?=(%done status.mil) + ?=(^ withdrawal.mil) + ?=(^ xact.u.withdrawal.mil) + |(=(0x0 q.u.xact.u.withdrawal.mil) ?=(^ pruf.u.withdrawal.mil)) + == + 0 + cash.u.withdrawal.mil :: cash.u.pruf.u.withdrawal.mil + ++ odit :: project-wide audit + ^- ^odit + (filo:fc [cost fill plej ~]) + ++ odim :: per-milestone audit + ^- (list ^odit) + =/ lin=@ (dec (lent miz)) + :: NOTE: Use @sd values since the last milestone can have an overage, + :: which produces negative fill values (reconciled in `+filo`) + =< - %^ spin miz [0 (sun:si fill) (sun:si plej)] + |= [mil=mile min=@ fre=@sd pre=@sd] + =/ [dun=? amo=@ud] + ?+ status.mil [| cost.mil] + %dead [& ?~(withdrawal.mil 0 cash.u.withdrawal.mil)] + :: + %done + ?~ withdrawal.mil [| cost.mil] + :_ cash.u.withdrawal.mil + |(=(0x0 +:(fall xact.u.withdrawal.mil [0 0x1])) ?=(^ pruf.u.withdrawal.mil)) + == + =+ end==(min lin) + =+ fos=(sun:si amo) + =+ fil=?:(|(end =(-1 (cmp:si fre fos))) fre fos) + =+ pos=?:(dun --0 (dif:si fos fil)) + =+ pej=?:(|(end =(-1 (cmp:si pre pos))) pre pos) + [(filo:fc [cost.mil (abs:si fil) (abs:si pej) ~]) +(min) (dif:si fre fil) (dif:si pre pej)] + ++ mula :: project-wide $mula list + ^- (list ^mula) + =- (sort - |=([m=^mula n=^mula] (gth (tula:fc m) (tula:fc n)))) + ;: welp + (turn ~(val by contribs) |=([t=treb *] `^mula`[%trib -.t])) + (turn ~(val by pledges) |=([p=^plej *] `^mula`[%plej p])) + (turn ~(val by proofs) |=(p=pruf `^mula`[%pruf p])) + (murn miz |=(m=mile `(unit ^mula)`?~(withdrawal.m ~ (bind pruf.u.withdrawal.m (lead %pruf))))) + == + ++ bloq :: project-wide latest block + ^- ^bloq + =/ mul=(list ^mula) mula + =/ moq=^bloq ?~(mul 0 (tula:fc i.mul)) + =/ loq=^bloq p:xact:(fall contract *^oath) + (max loq moq) + ++ next :: next active milestone + ^- [min=@ mil=mile] + :: NOTE: Provide index past last milestone when all are completed + =- ?^(- i.- [(lent miz) (rear miz)]) + %+ skip (enum:fx miz) + |=([@ n=mile] ?=(?(%done %dead) status.n)) + ++ whos :: all ships involved in the project + ^- (set @p) + %- silt + ^- (list @p) + ;: welp + [p.assessment]~ + (turn ~(val by pledges) |=([p=^plej *] ship.p)) + (murn ~(val by contribs) |=([t=treb *] ship.t)) + == + ++ rols :: project $role(s) of user + |= [wox=@p who=@p] + ^- (set role) + %- silt + ;: welp + ?.(=(wox who) ~ [%work]~) + ?.(=(p.assessment who) ~ [%orac]~) + ?. ?| (~(has by pledges) who) + %- ~(rep by contribs) + |=([[k=addr v=[treb deta]] a=_|] |(a ?~(ship.v | =(u.ship.v who)))) + == + ~ + [%fund]~ + == + ++ oath :: text of assessment contract + |= wox=@p + ^- tape + %*($ vath wox wox) + ++ vath :: versioned text of assessment contract + |= [wox=@p ver=over] + ^- tape + |^ =- "I, {}, hereby agree to assess the following project proposed by {}:\0a\0a{txt}" + ^- [ses=@p txt=tape] + :- p.assessment + %- zing %+ join "\0a" + %- skip :_ |=(t=tape =(~ t)) + %+ weld + ^- (list tape) + :~ "title: {(trip title)}" + "oracle: {} (for {(comp-enjs q.assessment [%chip %0 %0x0 '' '' 6])}%)" + (swap-enjs payment) + "summary: {(trip summary)}" + == + %+ turn (enum:fx miz) + |= [min=@ mil=mile] + ^- tape + %- zing %+ join "\0a\09" + ^- (list tape) + :~ "milestone #{<+(min)>}:" + "title: {(trip title.mil)}" + "cost: {(comp-enjs cost.mil payment)}" + "summary: {(trip summary.mil)}" + == + ++ comp-enjs + |= [amo=cash swa=swap] + ^- tape + =/ dex=@ud ?+(-.swa 0 %coin decimals.swa) + ?+ ver (comp:enjs:ff amo swa) + %v0-0-0 + (r-co:co [%d & (pro:si -1 (sun:si dex)) amo]) + :: + %v0-4-0 + =+ cax=(drg:fl (sun:fl amo)) + ?> ?=(%d -.cax) + (flot:fx cax(e (dif:si e.cax (sun:si dex))) ~) + == + ++ swap-enjs + |= swa=swap + ^- tape + ?: ?=(%v0-0-0 ver) ~ + =- "{tyt}: {pay}{adr} {med}" + ^- [tyt=tape adr=tape med=tape pay=tape] + :* tyt=?+(ver "payment" ?(%v1-1-0 %v1-0-0 %v0-4-0) "currency") + adr=?+(-.swa " ({(addr:enjs:ff addr.swa)})" %chip ~) + med=?+(-.swa "on eth chain {(bloq:enjs:ff chain.swa)}" %chip "out of band") + :: + ^= pay + ?+ ver (swap:enjs:ff swa) + ?(%v1-0-0 %v0-4-0) + ?+ symbol.swa "usdc" + %'USDC' "usdc" + %'WSTR' "wstr" + %'fundUSDC' "usdc" + %'fundWSTR' "wstr" + == + == + == + -- + ++ bail :: text of bail statement (no payout) + |= [min=@ byp=?(%done %dead)] + ^- tape + =- "I, {ses}, decree {tag}project with safe {saf} {end} with no payout." + :* ses=(trip (scot %p p.assessment)) + tag=?-(byp %done "milestone {<+(min)>} of ", %dead ~) + saf=(addr:enjs:ff safe:(fall contract *^oath)) + end=?-(byp %done "completed", %dead "canceled") + == + -- +-- diff --git a/desk/bare/sur/contacts.hoon b/desk/bare/sur/contacts.hoon new file mode 100644 index 0000000..eb0f702 --- /dev/null +++ b/desk/bare/sur/contacts.hoon @@ -0,0 +1,107 @@ +|% +:: [import] inlined imports for this core +:: +:: g: groups (/=landscape=/sur/groups/hoon) +:: e: epic (/=landscape=/sur/epic/hoon) +++ g + |% + +$ flag (pair @p @tas) + -- +++ e + |% + +$ saga + $% [%dex ver=@ud] + [%lev ~] + [%chi ~] + == + +$ epic @ud + -- +-- +|% +:: [compat] protocol-versioning scheme +:: +:: adopted from :groups, slightly modified. +:: +:: for our action/update marks, we +:: - *must* support our version (+okay) +:: - *should* support previous versions (especially actions) +:: - but *can't* support future versions +:: +:: in the case of updates at unsupported protocol versions, +:: we backoff and subscribe for version changes (/epic). +:: (this alone is unlikely to help with future versions, +:: but perhaps our peer will downgrade. in the meantime, +:: we wait to be upgraded.) +:: ++| %compat +++ okay `epic`0 +++ mar + |% + ++ base + |% + +$ act %contact-action + +$ upd %contact-update + -- + :: + ++ act `mark`^~((rap 3 *act:base '-' (scot %ud okay) ~)) + ++ upd `mark`^~((rap 3 *upd:base '-' (scot %ud okay) ~)) + -- +:: ++| %types ++$ contact + $: nickname=@t + bio=@t + status=@t + color=@ux + avatar=(unit @t) + cover=(unit @t) + groups=(set flag:g) + == +:: ++$ foreign [for=$@(~ profile) sag=$@(~ saga)] ++$ profile [wen=@da con=$@(~ contact)] ++$ rolodex (map ship foreign) +:: ++$ epic epic:e ++$ saga + $@ $? %want :: subscribing + %fail :: %want failed + %lost :: epic %fail + ~ :: none intended + == + saga:e +:: ++$ field + $% [%nickname nickname=@t] + [%bio bio=@t] + [%status status=@t] + [%color color=@ux] + [%avatar avatar=(unit @t)] + [%cover cover=(unit @t)] + [%add-group =flag:g] + [%del-group =flag:g] + == +:: ++$ action + :: %anon: delete our profile + :: %edit: change our profile + :: %meet: track a peer + :: %heed: follow a peer + :: %drop: discard a peer + :: %snub: unfollow a peer + :: + $% [%anon ~] + [%edit p=(list field)] + [%meet p=(list ship)] + [%heed p=(list ship)] + [%drop p=(list ship)] + [%snub p=(list ship)] + == +:: ++$ update :: network + $% [%full profile] + == +:: ++$ news :: local + [who=ship con=$@(~ contact)] +-- diff --git a/desk/bare/sur/fund/core-0.hoon b/desk/bare/sur/fund/core/0.hoon similarity index 100% rename from desk/bare/sur/fund/core-0.hoon rename to desk/bare/sur/fund/core/0.hoon diff --git a/desk/bare/sur/fund/core-1.hoon b/desk/bare/sur/fund/core/1.hoon similarity index 100% rename from desk/bare/sur/fund/core-1.hoon rename to desk/bare/sur/fund/core/1.hoon diff --git a/desk/bare/sur/fund/data-0.hoon b/desk/bare/sur/fund/data/0.hoon similarity index 100% rename from desk/bare/sur/fund/data-0.hoon rename to desk/bare/sur/fund/data/0.hoon diff --git a/desk/bare/sur/fund/data-1.hoon b/desk/bare/sur/fund/data/1.hoon similarity index 100% rename from desk/bare/sur/fund/data-1.hoon rename to desk/bare/sur/fund/data/1.hoon diff --git a/desk/bare/sur/fund/meta-0.hoon b/desk/bare/sur/fund/meta/0.hoon similarity index 100% rename from desk/bare/sur/fund/meta-0.hoon rename to desk/bare/sur/fund/meta/0.hoon diff --git a/desk/bare/sur/fund/proj.hoon b/desk/bare/sur/fund/proj.hoon index 5f8fa9e..c66cd06 100644 --- a/desk/bare/sur/fund/proj.hoon +++ b/desk/bare/sur/fund/proj.hoon @@ -62,9 +62,10 @@ [%bump sat=stat oat=(unit oath)] [%mula mula] [%blot mid=@ dif=blot] + [%view mid=@ dif=view] [%draw min=@ dif=xact] [%wipe min=@ sig=(unit sigm)] - [%redo ~] + [%redo sob=(unit bloq) tob=(unit bloq)] :: meta prods :: [%join ~] [%exit ~] diff --git a/desk/bare/sur/fund/proj-0.hoon b/desk/bare/sur/fund/proj/0.hoon similarity index 100% rename from desk/bare/sur/fund/proj-0.hoon rename to desk/bare/sur/fund/proj/0.hoon diff --git a/desk/bare/sur/fund/proj-1.hoon b/desk/bare/sur/fund/proj/1.hoon similarity index 100% rename from desk/bare/sur/fund/proj-1.hoon rename to desk/bare/sur/fund/proj/1.hoon diff --git a/desk/bare/sur/fund/proj/2.hoon b/desk/bare/sur/fund/proj/2.hoon new file mode 100644 index 0000000..5f8fa9e --- /dev/null +++ b/desk/bare/sur/fund/proj/2.hoon @@ -0,0 +1,78 @@ +/- *fund-core +|% +:: +:: $deta: display metadata (for visible + addressable items) +:: ++$ deta + $: id=@ud + show=? + == +:: +:: $peta: display metadata and interpretation (deta + optional view) +:: ++$ peta + $: view=(unit view) + deta + == +:: +:: $mile: segment of work (milestone) within a project +:: ++$ mile + $: title=@t + summary=@t + image=(unit @t) + cost=cash + status=stat + withdrawal=(unit with) + == +:: +:: $proj: collection of work (milestones) requesting funding +:: +++ proj + $: title=@t + summary=@t + image=(unit @t) + assessment=sess + payment=swap + milestones=(lest mile) + contract=(unit oath) + pledges=(map ship [plej peta]) + contribs=(map addr [treb deta]) + proofs=(map addr pruf) + == +:: +:: $prej: project with peer information +:: ++$ prej [proj live=_|] +:: +:: $proz: collection of projects keyed by id (host/term) +:: ++$ proz (map flag proj) +:: +:: $prez: project collection with peer information +:: ++$ prez (map flag prej) +:: +:: $prod: raw delta identifier +:: ++$ prod + $% :: proj prods :: + [%init pro=proj] + [%drop ~] + [%bump sat=stat oat=(unit oath)] + [%mula mula] + [%blot mid=@ dif=blot] + [%draw min=@ dif=xact] + [%wipe min=@ sig=(unit sigm)] + [%redo ~] + :: meta prods :: + [%join ~] + [%exit ~] + [%lure who=@p wut=role] + [%copy wer=@tas] + == +:: +:: $poke: project-bound delta ($prod) +:: ++$ poke (pair flag prod) +-- diff --git a/desk/bare/sur/sss/meta-0.hoon b/desk/bare/sur/sss/meta/0.hoon similarity index 100% rename from desk/bare/sur/sss/meta-0.hoon rename to desk/bare/sur/sss/meta/0.hoon diff --git a/desk/bare/sur/sss/proj.hoon b/desk/bare/sur/sss/proj.hoon index 2fedbcf..8907efa 100644 --- a/desk/bare/sur/sss/proj.hoon +++ b/desk/bare/sur/sss/proj.hoon @@ -1,4 +1,4 @@ -/- pold=sss-proj-4 +/- pold=sss-proj-5 /+ *fund-proj, fc=fund-core, config, sss |% +| %misc @@ -29,38 +29,9 @@ -- +| %core -+$ vers _%5 ++$ vers _%6 +$ path [%fund %proj sip=@ nam=@ ~] ++ lake - =/ up - |_ pro=proj:pold - ++ proj - ^- ^proj - :* title=title.pro - summary=summary.pro - image=image.pro - assessment=assessment.pro - :: - ^= payment - ?. ?=(%enft -.payment.pro) payment.pro - :* %enft - chain=chain.payment.pro - addr=addr.payment.pro - name=name.payment.pro - symbol=symbol.payment.pro - :: NOTE: Hardcoded so as not to create a dependency on the - :: /lib/fund/chain/hoon file - uri=|=(i=@ud "https://azimuth.network/erc721/{}.json") - limits=(malt ~[[%size |=(=@t =(%star t))]]) - == - :: - milestones=milestones.pro - contract=contract.pro - pledges=pledges.pro - contribs=contribs.pro - proofs=proofs.pro - == - -- |% ++ name %proj +$ rock [vers proj] @@ -72,7 +43,7 @@ ^- rock ?+ -.voc $(voc (urck:lake:pold voc)) vers voc - vers:pold $(voc [*vers ~(proj up +.voc)]) + vers:pold $(voc [*vers +.voc]) == ++ uwve |= vav=vave @@ -83,7 +54,7 @@ vers:pold =- $(vav [*vers -]) ?+ +.vav +.vav - [* * %init *] [bol.vav p.pok.vav %init ~(proj up pro.q.pok.vav)] + [* * %redo *] [bol.vav p.pok.vav %redo ~ ~] == == ++ wash @@ -199,6 +170,7 @@ `mile`mil(status sat.pod, withdrawal `[~ sig fill.mod ~]) :: pledges + ?. =((lent miz) +(min)) pledges.pro %- ~(run by pledges.pro) |=([p=plej d=peta] [p d(view `%stif)]) == @@ -276,6 +248,28 @@ == == == + :: + %blot + =/ sow=? ?=(%show dif.pod) + %_ pro + contribs + %- ~(run by contribs.pro) + |= [teb=treb det=deta] + [teb ?.(=(id.det mid.pod) det det(show sow))] + :: + pledges + %- ~(run by pledges.pro) + |= [pej=plej pet=peta] + [pej ?.(=(id.pet mid.pod) pet pet(show sow))] + == + :: + %view + %_ pro + pledges + %- ~(run by pledges.pro) + |= [pej=plej pet=peta] + [pej ?.(=(id.pet mid.pod) pet pet(view `dif.pod))] + == :: %draw =/ [min=@ mil=mile] [min.pod (snag min.pod miz)] @@ -309,14 +303,42 @@ (edit-mile min mil(withdrawal `[~ u.sig.pod fil ~])) :: %redo + =/ sob=bloq (fall sob.pod ?~(contract.pro 0 p.xact.u.contract.pro)) + =/ tob=bloq (fall tob.pod 1.000.000.000.000.000) :: NOTE: Basically 'Number.MAX' + =+ gud=|=(p=pruf &((gte sob p.xact.when.p) (lte tob p.xact.when.p))) %_ pro - proofs proofs:*proj - contribs (~(run by contribs.pro) |=([t=treb d=deta] [t(pruf ~) d])) + proofs + %- ~(rep by proofs.pro) + |= [[key=addr val=pruf] acc=(map addr pruf)] + ^- (map addr pruf) + ?.((gud val) acc (~(put by acc) key val)) + :: + contribs + %- ~(run by contribs.pro) + |= [teb=treb det=deta] + :_ det + %_ teb + pruf + ?~ pruf.teb ~ + ?. (gud u.pruf.teb) ~ + pruf.teb + == :: milestones ;; (lest mile) %+ turn milestones.pro - |=(m=mile m(withdrawal (bind withdrawal.m |=(w=with w(pruf ~))))) + |= mil=mile + %_ mil + withdrawal + %+ bind withdrawal.mil + |= wit=with + %_ wit + pruf + ?~ pruf.wit ~ + ?. (gud u.pruf.wit) ~ + pruf.wit + == + == == == -- diff --git a/desk/bare/sur/sss/proj-0.hoon b/desk/bare/sur/sss/proj/0.hoon similarity index 100% rename from desk/bare/sur/sss/proj-0.hoon rename to desk/bare/sur/sss/proj/0.hoon diff --git a/desk/bare/sur/sss/proj-1.hoon b/desk/bare/sur/sss/proj/1.hoon similarity index 100% rename from desk/bare/sur/sss/proj-1.hoon rename to desk/bare/sur/sss/proj/1.hoon diff --git a/desk/bare/sur/sss/proj-2.hoon b/desk/bare/sur/sss/proj/2.hoon similarity index 100% rename from desk/bare/sur/sss/proj-2.hoon rename to desk/bare/sur/sss/proj/2.hoon diff --git a/desk/bare/sur/sss/proj-3.hoon b/desk/bare/sur/sss/proj/3.hoon similarity index 100% rename from desk/bare/sur/sss/proj-3.hoon rename to desk/bare/sur/sss/proj/3.hoon diff --git a/desk/bare/sur/sss/proj-4.hoon b/desk/bare/sur/sss/proj/4.hoon similarity index 100% rename from desk/bare/sur/sss/proj-4.hoon rename to desk/bare/sur/sss/proj/4.hoon diff --git a/desk/bare/sur/sss/proj/5.hoon b/desk/bare/sur/sss/proj/5.hoon new file mode 100644 index 0000000..9a2ca64 --- /dev/null +++ b/desk/bare/sur/sss/proj/5.hoon @@ -0,0 +1,324 @@ +/- pold=sss-proj-4 +/+ *fund-proj-2, fc=fund-core, config, sss +|% ++| %misc +++ flag + |= pat=path + ^- ^flag + [`@p`(slav %p sip.pat) `@tas`(slav %tas nam.pat)] +++ conn + |_ [bol=bowl:gall suz=_(mk-subs:sss lake path) puz=_(mk-pubs:sss lake path)] + ++ subs :: $proj subscriptions + =/ da (da:sss lake path) + (da suz bol -:!>(*result:da) -:!>(*from:da) -:!>(*fail:da)) + ++ pubs :: $proj publications + =/ du (du:sss lake path) + (du puz bol -:!>(*result:du)) + ++ mine :: local data only + ^- prez + %- ~(rep by read:pubs) + |= [[k=path v=[(unit (set ship)) vers p=proj]] a=prez] + (~(put by a) (flag k) p.v &) + ++ ours :: local and remote data + ^- prez + %- ~(uni by mine) + ^- prez + %- ~(rep by read:subs) + |= [[k=[ship dude:gall p=path] v=[s=? f=? vers p=proj]] a=prez] + (~(put by a) (flag p.k) p.v &(!s.v !f.v)) + -- + ++| %core ++$ vers _%5 ++$ path [%fund %proj sip=@ nam=@ ~] +++ lake + =/ up + |_ pro=proj:pold + ++ proj + ^- ^proj + :* title=title.pro + summary=summary.pro + image=image.pro + assessment=assessment.pro + :: + ^= payment + ?. ?=(%enft -.payment.pro) payment.pro + :* %enft + chain=chain.payment.pro + addr=addr.payment.pro + name=name.payment.pro + symbol=symbol.payment.pro + :: NOTE: Hardcoded so as not to create a dependency on the + :: /lib/fund/chain/hoon file + uri=|=(i=@ud "https://azimuth.network/erc721/{}.json") + limits=(malt ~[[%size |=(=@t =(%star t))]]) + == + :: + milestones=milestones.pro + contract=contract.pro + pledges=pledges.pro + contribs=contribs.pro + proofs=proofs.pro + == + -- + |% + ++ name %proj + +$ rock [vers proj] + +$ vock $%(vock:lake:pold rock) + +$ wave [vers bol=bowl:gall pok=poke] + +$ vave $%(vave:lake:pold wave) + ++ urck + |= voc=vock + ^- rock + ?+ -.voc $(voc (urck:lake:pold voc)) + vers voc + vers:pold $(voc [*vers ~(proj up +.voc)]) + == + ++ uwve + |= vav=vave + ^- wave + ?+ -.vav $(vav (uwve:lake:pold vav)) + vers vav + :: + vers:pold + =- $(vav [*vers -]) + ?+ +.vav +.vav + [* * %init *] [bol.vav p.pok.vav %init ~(proj up pro.q.pok.vav)] + == + == + ++ wash + :: TODO: Add useful error messages to all the various error cases + |= [[vers pro=proj] [vers bol=bowl:gall lag=^flag pod=prod]] + ^- rock + =* mes `(mess prod)`[src.bol /fund/proj/(scot %p p.lag)/[q.lag] pod] + =* miz `(list mile)`milestones.pro + => |% + ++ aver-work |-(~|(bad-wash+mes ?>(=(our.bol src.bol) %.y))) + ++ aver-orac |-(~|(bad-wash+mes ?>(=(p.assessment.pro src.bol) %.y))) + ++ edit-mile |=([i=@ m=mile] %_(pro milestones ;;((lest mile) (snap miz i m)))) + ++ edit-milz |=(t=$-(mile mile) %_(pro milestones ;;((lest mile) (turn miz t)))) + ++ good-sigm |=([s=sigm w=(set addr)] &((~(has in w) from.s) (csig:fc s))) + ++ orac-sigm |=(s=sigm =+((need contract.pro) (good-sigm s (sy [orac.-]~)))) + ++ team-sigm |=(s=sigm =+((need contract.pro) (good-sigm s (sy ~[orac.- work.-])))) + ++ peer-sigm |=(s=sigm =+((need contract.pro) (good-sigm s (sy ~[orac.- work.- !<(addr (slot:config %sign-addr))])))) + ++ dead-milz + |= oat=oath + ?> (team-sigm sigm.oat) + ?> ?| ?=([%| @ux] mesg.sigm.oat) + =((crip (~(bail pj pro) (dec (lent miz)) %dead)) +.mesg.sigm.oat) + == + %_ pro + milestones + ;; (lest mile) + =+ niz=(turn miz |=(m=mile ?:(?=(%done status.m) m m(status %dead)))) + %+ snoc (snip niz) + =+ mil=(rear niz) + mil(withdrawal `[~ sigm.oat (sub ~(fill pj pro) ~(take pj pro)) ~]) + :: + pledges + %- ~(run by pledges.pro) + |=([p=plej d=peta] [p d(view `%slyd)]) + == + -- + =/ sat=stat ~(stat pj pro) + :- *vers + ?+ -.pod pro + %init + ?> aver-work + ?> =(%born sat) + ?> =(%born ~(stat pj pro.pod)) + :: NOTE: For now, stars and galaxies only + ?> (star:fx p.assessment.pro.pod) + pro.pod + :: + %bump + =/ [min=@ mil=mile] ~(next pj pro) + ?: ?=(%born status.mil) + ?> &(?=(%prop sat.pod) ?=(~ oat.pod)) + ?> aver-work :: born=>prop:worker + (edit-milz |=(m=mile m(status %prop))) + ?: ?=(%prop status.mil) + ?> ?| &(?=(%born sat.pod) ?=(~ oat.pod)) + &(?=(?(%prop %lock) sat.pod) ?=(^ oat.pod)) + == + ?> |(?=(?(%born %prop) sat.pod) aver-work) :: prop=>lock:worker + ?> |(?=(?(%born %lock) sat.pod) aver-orac) :: prop=>prop:oracle + ?> |(?=(?(%prop %lock) sat.pod) ?=(~ contract.pro) aver-work) :: prop=>born:worker(post-orac-accept) + =. contract.pro + ?+ sat.pod !! + %born + ~ + :: + %prop + =+ sig=sigm:(need oat.pod) + ?> %. (trip `@t`p.mesg.sig) + :: NOTE: Try all versions listed in simple type union `over` + %~ has in %- silt + =- (turn vez |=(v=@ (~(vath pj pro) our.bol ;;(over v)))) + ^- vez=(list @) + =/ typ=type ~(repo ut -:!>(*over)) + ?> ?=([%hint * %fork *] typ) + (murn ~(tap in p.q.typ) |=(t=type ?>(?=([%atom *] t) q.t))) + ?> (csig:fc sig) + =+(o=*oath `o(sigm sig)) + :: + %lock + =+ our-oat=(need contract.pro) + =+ pod-oat=(need oat.pod) + ?> =(sigm.our-oat sigm.pod-oat) + ?> =(orac.pod-oat from.sigm.pod-oat) + `pod-oat + == + (edit-milz |=(m=mile m(status sat.pod))) + ?: ?=(%lock status.mil) + ?: ?=(%work sat.pod) + ?> aver-work :: lock=>work:worker + (edit-mile min mil(status sat.pod)) + ?: ?=(%dead sat.pod) + (dead-milz (need oat.pod)) + ~|(bad-wash+mes !!) :: %lock =X=> ?(%born %prop %sess %done) + ?: ?=(?(%work %sess) status.mil) + ?: ?=(%work sat.pod) + ?> aver-orac :: work/sess=>work:oracle + (edit-mile min mil(status sat.pod)) + ?: ?=(%sess sat.pod) + ?> aver-work :: work/sess=>sess:worker + (edit-mile min mil(status sat.pod)) + ?: ?=(%done sat.pod) + ?> aver-orac :: work/sess=>done:oracle + =/ sig=sigm sigm:(need oat.pod) + =/ mod=odit (snag min ~(odim pj pro)) + ?> (orac-sigm sig) + ?> ?| ?=([%| @ux] mesg.sig) + =((crip (~(bail pj pro) min sat.pod)) +.mesg.sig) + == + %_ pro + milestones + ;; (lest mile) + %^ snap miz min + `mile`mil(status sat.pod, withdrawal `[~ sig fill.mod ~]) + :: + pledges + ?. =((lent miz) +(min)) pledges.pro + %- ~(run by pledges.pro) + |=([p=plej d=peta] [p d(view `%stif)]) + == + ?: ?=(%dead sat.pod) + (dead-milz (need oat.pod)) + ~|(bad-wash+mes !!) :: ?(%work %sess %done %dead) =X=> ?(%born %lock) + ~|(bad-wash+mes !!) :: %dead =X=> status + :: + %mula + ?< ?=(?(%born %prop) sat) + ?> (gth cash.pod 0) + ?- +<.pod + %pruf + :: TODO: When duplicate attestations come in, they currently + :: overwrite the existing data; this hasn't been an issue, but + :: it's unclear if this is the best behavior + ?> =(src our):bol + ?- note.pod + %depo + ?~ teb=(~(get by contribs.pro) q.xact.when.pod) + %_(pro proofs (~(put by proofs.pro) q.xact.when.pod +>.pod)) + %_(pro contribs (~(put by contribs.pro) q.xact.when.pod u.teb(pruf `+>.pod))) + :: + %with + =/ myz=(list (pair @ mile)) + %+ skim +:(roll miz |=([n=mile a=[@ (list [@ mile])]] [+(-.a) [[-.a n] +.a]])) + |= [min=@ mil=mile] + ?& ?=(^ withdrawal.mil) + ?=(^ xact.u.withdrawal.mil) + =(q.u.xact.u.withdrawal.mil q.xact.when.pod) + == + ?~ mil=`(unit (pair @ mile))`?~(myz ~ `i.myz) + %_(pro proofs (~(put by proofs.pro) q.xact.when.pod +>.pod)) + ?> ?=(^ withdrawal.q.u.mil) + (edit-mile p.u.mil q.u.mil(withdrawal `u.withdrawal.q.u.mil(pruf `+>.pod))) + == + :: + ?(%plej %trib) + ?< ?=(?(%done %dead) sat) + :: FIXME: Very inefficient, but also very convenient! + =/ nid=@ud + .+ %+ max + (~(rep by contribs.pro) |=([[k=addr v=[treb deta]] a=@ud] (max a id.v))) + (~(rep by pledges.pro) |=([[k=ship v=[plej peta]] a=@ud] (max a id.v))) + ?- +<.pod + %plej + :: NOTE: This is a sufficient check because we only allow the + :: host of a project to accept donations on the project's behalf + :: (so src.bol must always be the %plej attestor; no forwarding!) + ?> =(src.bol ship.pod) + ?> (plan:fx src.bol) + ?< (~(has by pledges.pro) ship.pod) + %_(pro pledges (~(put by pledges.pro) ship.pod +>.pod ~ nid &)) + :: + %trib + =/ pej=(unit [plej peta]) ?~(ship.pod ~ (~(get by pledges.pro) u.ship.pod)) + =/ puf=(unit pruf) (~(get by proofs.pro) q.xact.when.pod) + ?> |(?=(~ pej) &(=(src.bol ship.u.pej) =(cash.u.pej cash.pod))) + %_ pro + pledges + ?~ pej pledges.pro + (~(del by pledges.pro) (need ship.pod)) + :: + proofs + ?~ puf proofs.pro + (~(del by proofs.pro) q.xact.when.u.puf) + :: + contribs + %- ~(put by contribs.pro) + =- [q.xact.when.pod - nid &] + :* trib=+>.pod + plej=?~(pej ~ `-.u.pej) + pruf=?~(puf ~ `u.puf) + == + == + == + == + :: + %draw + =/ [min=@ mil=mile] [min.pod (snag min.pod miz)] + ?> ?=(?(%dead %done) status.mil) + ?> |(?=(%done status.mil) =((lent miz) +(min))) + ?> &(?=(^ withdrawal.mil) ?=(~ xact.u.withdrawal.mil)) + =/ puf=(unit pruf) (~(get by proofs.pro) q.dif.pod) + %_ pro + milestones + ;; (lest mile) + %^ snap miz min + `mile`mil(withdrawal `u.withdrawal.mil(xact `dif.pod, pruf puf)) + :: + proofs + ?~ puf proofs.pro + (~(del by proofs.pro) q.dif.pod) + == + :: + %wipe + =/ [min=@ mil=mile] [min.pod (snag min.pod miz)] + ?> ?=(?(%dead %done) status.mil) + ?> |(?=(%done status.mil) =((lent miz) +(min))) + ?> |(?=(~ withdrawal.mil) ?=(~ xact.u.withdrawal.mil)) + =/ mod=odit (snag min ~(odim pj pro)) + ?~ sig.pod (edit-mile min mil(withdrawal ~)) + ?> (peer-sigm u.sig.pod) + ?> ?| ?=([%| @ux] mesg.u.sig.pod) + =((crip (~(bail pj pro) min status.mil)) +.mesg.u.sig.pod) + == + =+ fil=?:(?=(%done status.mil) fill.mod (sub ~(fill pj pro) ~(take pj pro))) + (edit-mile min mil(withdrawal `[~ u.sig.pod fil ~])) + :: + %redo + %_ pro + proofs proofs:*proj + contribs (~(run by contribs.pro) |=([t=treb d=deta] [t(pruf ~) d])) + :: + milestones + ;; (lest mile) + %+ turn milestones.pro + |=(m=mile m(withdrawal (bind withdrawal.m |=(w=with w(pruf ~))))) + == + == + -- +-- diff --git a/desk/bare/web/fund/page/asset.hoon b/desk/bare/web/fund/page/asset.hoon index ef2983c..2a342f9 100644 --- a/desk/bare/web/fund/page/asset.hoon +++ b/desk/bare/web/fund/page/asset.hoon @@ -54,4 +54,4 @@ :+ 200 [%content-type (forj '/' p.mym)] :: 1 week cache time ?:(!<(bean (slot:config %debug)) ~ ['cache-control' 'max-age=604800']~) -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/config.hoon b/desk/bare/web/fund/page/config.hoon index 1b9d0da..e0e92a4 100644 --- a/desk/bare/web/fund/page/config.hoon +++ b/desk/bare/web/fund/page/config.hoon @@ -33,4 +33,4 @@ ;+ (prod-butn:ui:fh %vita-disable %false "no ✗" ~ ~) == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/index.hoon b/desk/bare/web/fund/page/index.hoon index befda34..1ce6a1e 100644 --- a/desk/bare/web/fund/page/index.hoon +++ b/desk/bare/web/fund/page/index.hoon @@ -47,4 +47,4 @@ == -- -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/proj-edit.hoon b/desk/bare/web/fund/page/proj-edit.hoon index ab37139..e25d32e 100644 --- a/desk/bare/web/fund/page/proj-edit.hoon +++ b/desk/bare/web/fund/page/proj-edit.hoon @@ -197,12 +197,10 @@ == ;div(class "flex") ;div(class "fund-form-group") - ;select#proj-oracle.fund-tsel(name "sea", x-init "initTomSelect($el, false)") + ;select#proj-oracle.fund-tsel(name "sea", x-init "initTomSelect($el, \{empty: true, create: tsCreateOracle($el)})") ;* =+ ses=?~(pru !<(@p (slot:config %point)) p.assessment.u.pru) =+ dad=(sein:title our.bol now.bol our.bol) - :: FIXME: These options are hard-coded from the - :: ~tocwex.syndicate group's oracle directory - %+ turn ~[!<(@p (slot:config %point)) dad ~reb ~bitdeg ~roswet ~nisfeb ~hosdys ~ridlyd ~darlur ~mocbel ~posdeg ~dalten] + %+ turn [ses !<(@p (slot:config %point)) dad !<((list @p) (slot:config %feat-oraz))] |= ora=@p ^- manx :_ ; {} @@ -341,4 +339,4 @@ == == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/proj-list.hoon b/desk/bare/web/fund/page/proj-list.hoon index 70de686..29bb30e 100644 --- a/desk/bare/web/fund/page/proj-list.hoon +++ b/desk/bare/web/fund/page/proj-list.hoon @@ -1,7 +1,7 @@ :: /web/fund/page/proj-list/hoon: render project listing page for %fund :: /- fd=fund-data, f=fund -/+ fj=fund-proj, fh=fund-http, fc=fund-chain, fx=fund-xtra +/+ fj=fund-proj, fh=fund-http, fc=fund-chain, fa=fund-alien, fx=fund-xtra /+ rudder, config %- :(corl mine:preface:fh init:preface:fh) ^- page:fd @@ -152,9 +152,7 @@ ^- manx :: TODO: Replace the latter with a ship-generated sigil pair :: (project worker and oracle with slightly different colors). - =/ bak=tape - ?^ image.pre (trip u.image.pre) - (surt:enrl:ff:fh p.lag) + =/ bak=tape ?^(image.pre (trip u.image.pre) (~(ship-logo fa bol) p.lag)) ;a/"{(flat:enrl:ff:fh lag)}"(class "flex flex-col gap-2 font-serif {cas}") ;div(class "aspect-video bg-cover bg-center rounded-md bg-[url('{bak}')]") ;div(class "flex flex-row flex-wrap justify-start items-center p-2 gap-2") @@ -162,7 +160,7 @@ ; {(swam:enjs:ff:fh ~(cost pj:fj -.pre) payment.pre)} == ;div(class "bg-gray-100 rounded-md p-0.5") - ;+ %+ ~(icon-stax ui:fh "h-8") | + ;+ %+ ~(icon-stax ui:fh "h-8") %circ :~ (aset:enrl:ff:fh symbol.payment.pre) (aset:enrl:ff:fh tag:(~(got by xmap:fc) chain.payment.pre)) == @@ -172,8 +170,8 @@ ;div(class "w-full flex-1 flex flex-row gap-2 justify-between items-start") ;div(class "flex-1 min-w-0 text-lg"): {(trip title.pre)} ;div(class "bg-gray-100 rounded-lg p-0.5 line-clamp-2") - ;+ %+ ~(icon-stax ui:fh "h-8") & - (turn ~[p.lag p.assessment.pre] surt:enrl:ff:fh) + ;+ %+ ~(icon-stax ui:fh "h-8") %rect + (turn ~[p.lag p.assessment.pre] ~(ship-logo fa bol)) == == == @@ -182,9 +180,7 @@ ^- manx :: TODO: Replace the latter with a ship-generated sigil pair :: (project worker and oracle with slightly different colors). - =/ bak=tape - ?^ image.met (trip u.image.met) - (surt:enrl:ff:fh worker.met) + =/ bak=tape ?^(image.met (trip u.image.met) (~(ship-logo fa bol) worker.met)) :: TODO: Add data attributes to allow for FE sorting/filtering ;div =x-on-click "joinProject('{(flag:enjs:ff:fh lag)}')" =class "flex flex-col gap-2 font-serif hover:cursor-pointer {cas}" @@ -194,7 +190,7 @@ ; {(swam:enjs:ff:fh cost.met payment.met)} == ;div(class "bg-gray-100 rounded-md p-0.5 line-clamp-2") - ;+ (icon-stax:ui:fh & (turn ~[worker.met oracle.met] surt:enrl:ff:fh)) + ;+ (icon-stax:ui:fh %rect (turn ~[worker.met oracle.met] ~(ship-logo fa bol))) == == == @@ -218,7 +214,7 @@ :: leave it for now ;div(class "min-h-[100vh] flex flex-col justify-between", x-data "proj_list") ;div(class "flex flex-col p-2 gap-2") - ;+ =/ cas=tape "w-full grid gap-4 justify-center" + ;* =/ cas=tape "w-full grid gap-4 justify-center" =/ pas=tape "{cas} grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(auto,500px))]" =/ mas=tape "{cas} grid-cols-2 sm:grid-cols-[repeat(auto-fit,minmax(auto,250px))]" =/ wax=manx @@ -232,44 +228,44 @@ == ?+ dyp !! %following - ;div - ;h1: Following - ;+ ?~ paz=(turn pyz proj-card:ui) wax - ;div(class pas) - ;* paz - == + :~ ;h1: Following + ?~ paz=(turn pyz proj-card:ui) wax + ;div(class pas) + ;* paz + == == :: %discover - ;div - ;div(class "flex flex-row gap-2 justify-start items-center") - ;h1: Discover - ;button#fund-help-disc.fund-tipi(type "button") - ;img.fund-butn-icon@"{(aset:enrl:ff:fh %help)}"; - == - ;div#fund-help-disc-opts(class "hidden") - ;p - ; Discovery of new projects depends on the %pals - ; network. Projects are publicized to your %pals, and - ; they can optionally republicize them to their %pals. - ; Tell your friends and see who can discover the - ; largest project collection! + =/ hel=tape (trip !<(@t (slot:config %meta-help))) + :~ ;div(class "flex flex-row gap-2 justify-start items-center") + ;h1: Discover + ;button#fund-help-disc.fund-tipi(type "button", x-init "initTippy($el)") + ;img.fund-butn-icon@"{(aset:enrl:ff:fh %help)}"; == - ;a.text-link/"{(trip !<(@t (slot:config %meta-help)))}/project-discovery"(target "_blank") - ; Read the docs to learn more. + ;div#fund-help-disc-opts(class "hidden") + ;p + ; Discovery of new projects depends on the %pals + ; network. Projects are publicized to your %pals, and + ; they can optionally republicize them to their %pals. + ; Tell your friends and see who can discover the + ; largest project collection! + == + ;a.text-link/"{hel}/project-discovery"(target "_blank") + ; Read the docs to learn more. + == == == - == - ;+ ?~ maz=(turn myz meta-card:ui) wax - ;div(class mas) - ;* maz - == + ?~ maz=(turn myz meta-card:ui) wax + ;div(class mas) + ;* maz + == == :: %action =/ sas=tape "grid gap-4 grid-rows-1 grid-flow-col overflow-x-auto" =/ sus=tape "w-[50vw] sm:w-[250px]" ?^ text.arg + :_ ~ %^ mota-well:ui mas "No projects found." |= [lag=flag:f pre=prej:proj:f] ?| ?& ?=(?(%prop %sess) ~(stat pj:fj -.pre)) @@ -282,55 +278,54 @@ (~(has in (sy ~[p.lag p.assessment.pre])) our.bol) == == - ;div - ;div(class "flex flex-row justify-between") - ;h1: My Actions - ;a.self-center.fund-butn-ac-m/"{(dest:enrl:ff:fh /create/project)}" - ; new project + - == - == - ;div(class "flex flex-col gap-4") - ;div :: my $prez - ;h2: My Open Projects - ;+ %^ ~(mota-well ui sus) sas "No live projects." - |= [lag=flag:f pre=prej:proj:f] - ?& ?! ?=(?(%done %dead) ~(stat pj:fj -.pre)) - =(our.bol p.lag) - == - == - ;div :: $prez with %prop status - ;h2: Service Requests - ;+ %^ ~(mota-well ui sus) sas "No outstanding requests." - |= [lag=flag:f pre=prej:proj:f] - ?& ?=(%prop ~(stat pj:fj -.pre)) - =(p.assessment.pre our.bol) - == - == - ;div :: $prez with %sess status - ;h2: Review Requests - ;+ %^ ~(mota-well ui sus) sas "No projects pending review." - |= [lag=flag:f pre=prej:proj:f] - ?& ?=(%sess ~(stat pj:fj -.pre)) - =(p.assessment.pre our.bol) - == - == - ;div :: $prez with unfulfilled $plej - ;h2: Outstanding Pledges - ;+ %^ ~(mota-well ui sus) sas "No outstanding pledges." - |= [lag=flag:f pre=prej:proj:f] - ?& !?=(?(%born %prop %done %dead) ~(stat pj:fj -.pre)) - (~(has by pledges.pre) our.bol) - == + :~ ;div(class "flex flex-row justify-between") + ;h1: My Actions + ;a.self-center.fund-butn-ac-m/"{(dest:enrl:ff:fh /create/project)}" + ; new project + + == == - ;div :: worker|oracle done|dead $prez - ;h2: Work Archive - ;+ %^ mota-well:ui mas "No archived projects." - |= [lag=flag:f pre=prej:proj:f] - ?& ?=(?(%done %dead) ~(stat pj:fj -.pre)) - (~(has in (sy ~[p.lag p.assessment.pre])) our.bol) - == + ;div(class "flex flex-col gap-4") + ;div :: my $prez + ;h2: My Open Projects + ;+ %^ ~(mota-well ui sus) sas "No live projects." + |= [lag=flag:f pre=prej:proj:f] + ?& ?! ?=(?(%done %dead) ~(stat pj:fj -.pre)) + =(our.bol p.lag) + == + == + ;div :: $prez with %prop status + ;h2: Service Requests + ;+ %^ ~(mota-well ui sus) sas "No outstanding requests." + |= [lag=flag:f pre=prej:proj:f] + ?& ?=(%prop ~(stat pj:fj -.pre)) + =(p.assessment.pre our.bol) + == + == + ;div :: $prez with %sess status + ;h2: Review Requests + ;+ %^ ~(mota-well ui sus) sas "No projects pending review." + |= [lag=flag:f pre=prej:proj:f] + ?& ?=(%sess ~(stat pj:fj -.pre)) + =(p.assessment.pre our.bol) + == + == + ;div :: $prez with unfulfilled $plej + ;h2: Outstanding Pledges + ;+ %^ ~(mota-well ui sus) sas "No outstanding pledges." + |= [lag=flag:f pre=prej:proj:f] + ?& !?=(?(%born %prop %done %dead) ~(stat pj:fj -.pre)) + (~(has by pledges.pre) our.bol) + == + == + ;div :: worker|oracle done|dead $prez + ;h2: Work Archive + ;+ %^ mota-well:ui mas "No archived projects." + |= [lag=flag:f pre=prej:proj:f] + ?& ?=(?(%done %dead) ~(stat pj:fj -.pre)) + (~(has in (sy ~[p.lag p.assessment.pre])) our.bol) + == + == == - == == == == @@ -390,7 +385,7 @@ == ;div(x-show "filt_status.mode == 'work'") ;select#filt-worker.fund-tsel - =x-init "initTomSelect($el, true, true)" + =x-init "initTomSelect($el, \{empty: true, forceUp: true})" =x-model "filt_status.params.work" ;* =/ woz=(set @p) %+ roll `(list (set flag:f))`~[~(key by mez) ~(key by pez)] @@ -404,14 +399,14 @@ :- %option ;: welp [%value "{}"]~ - [%data-image "https://azimuth.network/erc721/{(bloq:enjs:ff:fh `@`wok)}.svg"]~ + [%data-image (~(ship-logo fa bol) wok)]~ ?.(&(?=(^ work.arg) =(u.work.arg wok)) ~ [%selected ~]~) == == == ;div(x-show "filt_status.mode == 'orac'") ;select#filt-oracle.fund-tsel - =x-init "initTomSelect($el, true, true)" + =x-init "initTomSelect($el, \{empty: true, forceUp: true})" =x-model "filt_status.params.orac" ;* =/ orz=(set @p) =- (~(uni in (silt mel)) (silt pel)) @@ -426,14 +421,14 @@ :- %option ;: welp [%value "{}"]~ - [%data-image "https://azimuth.network/erc721/{(bloq:enjs:ff:fh `@`ora)}.svg"]~ + [%data-image (~(ship-logo fa bol) ora)]~ ?.(&(?=(^ orac.arg) =(u.orac.arg ora)) ~ [%selected ~]~) == == == ;div(x-show "filt_status.mode == 'stat'") ;select#filt-status.fund-tsel - =x-init "initTomSelect($el, true, true)" + =x-init "initTomSelect($el, \{empty: true, forceUp: true})" =x-model "filt_status.params.stat" ;* :- ;option(value ""): Any Status %+ turn `(list stat:f)`~[%born %prop %lock %work %sess %done %dead] @@ -591,4 +586,4 @@ == == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/proj-next.hoon b/desk/bare/web/fund/page/proj-next.hoon index e950628..5877ca2 100644 --- a/desk/bare/web/fund/page/proj-next.hoon +++ b/desk/bare/web/fund/page/proj-next.hoon @@ -321,4 +321,4 @@ == == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/proj-view.hoon b/desk/bare/web/fund/page/proj-view.hoon index 643c4c0..bf1bcfe 100644 --- a/desk/bare/web/fund/page/proj-view.hoon +++ b/desk/bare/web/fund/page/proj-view.hoon @@ -1,7 +1,7 @@ :: /web/fund/page/proj-view/hoon: render base project page for %fund :: /- fd=fund-data -/+ f=fund-proj, fh=fund-http, fc=fund-chain, fx=fund-xtra +/+ f=fund-proj, fh=fund-http, fc=fund-chain, fa=fund-alien, fx=fund-xtra /+ rudder, config %- :(corl dump:preface:fh init:preface:fh (proj:preface:fh &)) ^- page:fd @@ -137,6 +137,48 @@ == == == + :: + ?(%mula-blot %mula-view %mula-mine %mula-redo) + ?+ arz=(parz:fh bod (sy ~[%mut %mui])) p.arz [%| *] + =/ mut=@t (~(got by p.arz) %mut) + =/ mui=@t (~(got by p.arz) %mui) + =/ med=@ ?+(mut (addr:dejs:ff:fh mui) %plej (ship:dejs:ff:fh mui)) + =/ mid=@ + ?+ mut 0 + %plej ?~(pej=(~(get by pledges.pro) `@p`med) 0 id.u.pej) + %trib ?~(teb=(~(get by contribs.pro) `addr:f`med) 0 id.u.teb) + == + ?- dif + %mula-blot + =- [%blot mid ?:(sow %hide %show)] + ^- sow=? + ?+ mut !! + %plej show:(~(got by pledges.pro) `@p`med) + %trib show:(~(got by contribs.pro) `addr:f`med) + == + :: + %mula-view + =- [%view mid ?:(sly %stif %slyd)] + ^- sly=? + ?. ?=(%plej mut) !! + =+ pej=(~(got by pledges.pro) `@p`med) + ?~(vyw=view.pej !! =(%slyd u.vyw)) + :: + %mula-mine + ?. ?=(%pruf mut) !! + =/ who=(unit @p) ?.((auth:fh bol) ~ `src.bol) + =/ puf=pruf:f (~(got by proofs.pro) `addr:f`med) + [%mula %trib who cash.puf when.puf %$] + :: + %mula-redo + =/ win=@ud :(mul 4 60 2) :: 4 blocks/sec * 60 sec/min * 2 mins + =- [%redo `(sub boq win) `(add boq win)] + ^- boq=bloq:f + ?. ?=(%trib mut) !! + =+ tib=(~(got by contribs.pro) `addr:f`med) + p.xact.when.tib + == + == == == ++ final :: POST render @@ -175,7 +217,7 @@ ;div(class "flex flex-col gap-1 p-2", x-data "proj_view") ;+ %^ work-tytl:ui:fh (trip title.pro) sat ;span: {(swam:enjs:ff:fh cost.pod payment.pro)} - ;img.w-full@"{(trip ?^(image.pro u.image.pro (surc:enrl:ff:fh p.lag)))}"; + ;img.w-full@"{(trip ?^(image.pro u.image.pro (crip (~(ship-logo fa bol) p.lag))))}"; ;* =- ?~ buz ~ :_ ~ :: NOTE: For "w-full on stick," add `-top-[1px]`, `x-init "initSticky($el)"` @@ -334,9 +376,8 @@ ;label(for "sum"): amount == ;div(class "fund-form-group") - ;+ :_ :_ ~ - ?: nft - ;option(value "-1", data-image (colt:enrl:ff:fh %black)): …loading… + ;+ :_ ?: nft ~ + :_ ~ ;option =value (trip symbol.payment.pro) =data-image (aset:enrl:ff:fh symbol.payment.pro) @@ -347,21 +388,27 @@ [%id "proj-token"]~ [%name "tok"]~ [%class "fund-tsel"]~ - :: ?.(nft ~ [%multiple ~]~) + ?.(nft ~ [%multiple ~]~) ?~(pej ~ ?:(nft [%required ~]~ [%disabled ~]~)) + ['@fund-wallet.window' "$el?.tomselect?.load && $el.tomselect.load()"]~ :_ ~ :- %x-init %- zing %+ join "\0a" ^- (list tape) - :~ :(weld "const nft = " (bool:enjs:ff:fh nft) ";") - :(weld "const pej = " (bloq:enjs:ff:fh ?~(pej 0 cash.u.pej)) ";") + :~ :(weld "const isNFT = " (bool:enjs:ff:fh nft) ";") + :(weld "const maxItems = " (bloq:enjs:ff:fh ?~(pej 0 cash.u.pej)) ";") ^- tape ^~ %+ rip 3 ''' if (typeof initTomSelect !== 'undefined') { - initTomSelect($el, false, false); - // FIXME: Restore this once multi-send NFTs are supported - // initTomSelect($el, nft, false, !nft ? undefined : pej); - nft && updateNftsSelect($el); + if (!!$el?.tomselect) { + $el?.tomselect?.load && $el.tomselect.load(); + } else { + initTomSelect($el, { + empty: isNFT, + maxItems: !isNFT ? undefined : 1, // maxItems, + load: !isNFT ? undefined : tsLoadNFTs($el), + }); + } } ''' == @@ -420,7 +467,7 @@ ;h1: Funding Tracker ;div(class "flex flex-wrap gap-1 items-center") ;h6(class "leading-none tracking-widest"): Funded via - ;+ %+ icon-stax:ui:fh | + ;+ %+ icon-stax:ui:fh %circ :~ (aset:enrl:ff:fh symbol.payment.pro) (aset:enrl:ff:fh tag:(~(got by xmap:fc) chain.payment.pro)) == @@ -518,49 +565,19 @@ ;div(class "flex flex-col gap-2") ;h1(class "sm:hidden"): Participants ;+ %: ship-card:ui:fh - p.lag - "Project Worker" - ^- tape ^~ - %+ rip 3 - ''' - The project worker is the identity which does the work - defined in the project overview and milestones. When a - milestone is completed and approved by the trusted - oracle, the project worker receives the listed payout - for that milestone. - ''' - "user-guides/project-workers" - ?~(contract.pro 0x0 work.u.contract.pro) - chain.payment.pro - ;* ?. &(=(our src):bol !wok) ~ - :_ ~ - ;a.fund-butn-ac-s/"{(chat:enrl:ff:fh p.lag)}"(target "_blank"): 💬 + p.lag bol %work + chain.payment.pro ?~(contract.pro 0x0 work.u.contract.pro) == ;+ %: ship-card:ui:fh - p.assessment.pro - "Trusted Oracle" - ^- tape ^~ - %+ rip 3 - ''' - The role of the trusted oracle is to assess completion - of the scope of work. When a milestone review is - requested, the oracle can chose to mark it as complete - by providing a cryptographically signed message, - allowing the worker to withdraw funds. - ''' - "user-guides/trusted-oracles" - ?~(contract.pro 0x0 from.sigm.u.contract.pro) - chain.payment.pro - ;* ?. &(=(our src):bol !ora) ~ - :_ ~ - ;a.fund-butn-ac-s/"{(chat:enrl:ff:fh p.assessment.pro)}"(target "_blank"): 💬 + p.assessment.pro bol %orac + chain.payment.pro ?~(contract.pro 0x0 from.sigm.u.contract.pro) == == ;div(class "flex flex-col gap-2") ;div(class "flex flex-row justify-between items-center") ;h1: Transactions ;div(class "flex flex-wrap items-center gap-1") - ;* =- :~ (icon-stax:ui:fh & (scag 3 (turn ~(tap in siz) surt:enrl:ff:fh))) + ;* =- :~ (icon-stax:ui:fh %rect (scag 3 (turn ~(tap in siz) surt:enrl:ff:fh))) ;h6: {<~(wyt in siz)>} total == ^- siz=(set @p) @@ -574,21 +591,39 @@ %+ turn muz |= mul=mula:f ^- manx - ;div(class "p-2.5 flex flex-col gap-y-2 fund-card") + =/ [myp=tape muf=tape mid=tape mow=bean] + :- myp=(trip -.mul) + ?- -.mul + %plej + =+ pej=(~(got by pledges.pro) ship.mul) + [muf=~ mid=(ship:enjs:ff:fh ship.mul) mow=show.pej] + :: + %trib + =+ teb=(~(got by contribs.pro) q.xact.when.mul) + [muf=(addr:enjs:ff:fh from.when.mul) mid=(addr:enjs:ff:fh q.xact.when.mul) mow=show.teb] + :: + %pruf + [muf=(addr:enjs:ff:fh from.when.mul) mid=(addr:enjs:ff:fh q.xact.when.mul) mow=&] + == + ;div + =x-data "\{ mula_type: '{myp}', mula_idex: '{mid}', mula_from: '{muf}' }" + =class "p-2.5 flex flex-col gap-y-2 fund-card" ;div(class "flex flex-wrap items-center justify-between") ;div(class "flex items-center gap-x-2") - ;* =- ~[(icon-logo:ui:fh & url) [[%h5 ~] [[%$ [%$ txt] ~]]~ ~]] + ;* =- :~ (icon-logo:ui:fh %rect url) + ;h5(x-init ?+(-.mul ~ %pruf "initENS($el, '{muf}')")): {txt} + == ^- [url=tape txt=tape] ?- -.mul - %plej [(surt:enrl:ff:fh ship.mul) "{}"] + %plej [(surt:enrl:ff:fh ship.mul) (ship:enjs:ff:fh ship.mul)] :: %trib ?~ ship.mul [(colt:enrl:ff:fh %black) "anonymous"] - [(surt:enrl:ff:fh u.ship.mul) "{}"] + [(surt:enrl:ff:fh u.ship.mul) (ship:enjs:ff:fh u.ship.mul)] :: %pruf - ?~ ship.mul [(aset:enrl:ff:fh %link) "chain data"] - [(surt:enrl:ff:fh u.ship.mul) "{}"] + ?~ ship.mul [(aset:enrl:ff:fh %link) (sadr:enjs:ff:fh from.when.mul)] + [(surt:enrl:ff:fh u.ship.mul) (ship:enjs:ff:fh u.ship.mul)] == == ;div(class "flex items-center gap-x-2") @@ -623,10 +658,65 @@ == == == - ;+ ?: |(=('' note.mul) ?=(%pruf -.mul)) - ;p(class "fund-warn"): No message included. - :: TODO: Consider including the pledge message here too - ;p(class "leading-normal tracking-wide"): {(trip note.mul)} + ;div(class "flex flex-col gap-2") + ;+ =/ msg=@t + ?- -.mul + %pruf %$ + %plej note.mul + :: + %trib + =+ teb=-:(~(got by contribs.pro) q.xact.when.mul) + ?.(=(%$ note.teb) note.teb ?~(plej.teb %$ note.u.plej.teb)) + == + ?. mow + ;p(class "fund-warn"): Message hidden by administrator. + ?: =(%$ msg) + ;p(class "fund-warn"): No message included. + ;p(class "leading-normal tracking-wide"): {(trip msg)} + == + ;* =- ?~ buz ~ + :_ ~ + ;div(class "flex flex-row justify-end gap-2 items-center") + ;* buz + == + ^- buz=marl + ;: welp + :: chain data claim button :: + ?. &((auth:fh bol) ?=(%pruf -.mul) ?=(%depo note.mul)) ~ + :_ ~ + ;form =method "post" + =x-show "($store.wallet.address ?? '').toLowerCase() == mula_from" + ;+ (prod-butn:ui:fh %mula-mine %action "claim transaction ~" "editMula" ~) + == + :: pledge edit view button :: + ?. ?& pyr + ?=(%plej -.mul) + =+ pej=(~(got by pledges.pro) ship.mul) + ?=(^ view.pej) + == + ~ + :_ ~ + ;form(method "post") + ;+ (prod-butn:ui:fh %mula-view %action "toggle status ~" "editMula" ~) + == + :: attested redo button :: + ?. ?& pyr + ?=(%trib -.mul) + =+ teb=(~(got by contribs.pro) q.xact.when.mul) + ?=(~ pruf.teb) + == + ~ + :_ ~ + ;form(method "post") + ;+ (prod-butn:ui:fh %mula-redo %action "query chain ~" "editMula" ~) + == + :: mula blot button :: + ?. &(pyr !?=(%pruf -.mul)) ~ + :_ ~ + ;form(method "post") + ;+ (prod-butn:ui:fh %mula-blot %action "toggle shown ~" "editMula" ~) + == + == == == == @@ -643,10 +733,6 @@ ;data#fund-meta-flag(value (flag:enjs:ff:fh lag)); ;* ?~ image.pro ~ :_ ~ ;data#fund-meta-logo(value (trip u.image.pro)); - :: FIXME: These fields are included to supply cross-element - :: information - ;data#fund-swap-type(value (trip -.payment.pro)); - ;data#fund-swap-symb(value (cuss (trip symbol.payment.pro))); == ;script ;+ ;/ @@ -659,6 +745,7 @@ :(weld "safe_bloq: " (bloq:enjs:ff:fh ?~(contract.pro 0 p.xact.u.contract.pro)) ",") :(weld "work_addr: '" (addr:enjs:ff:fh ?~(contract.pro 0x0 work.u.contract.pro)) "',") :(weld "orac_addr: '" (addr:enjs:ff:fh ?~(contract.pro 0x0 from.sigm.u.contract.pro)) "',") + :(weld "swap_type: '" (trip -.payment.pro) "',") :(weld "swap_chain: " (bloq:enjs:ff:fh chain.payment.pro) ",") :(weld "swap_symbol: '" (trip symbol.payment.pro) "',") :(weld "orac_cut: " (cash:enjs:ff:fh q.assessment.pro 6) ",") @@ -669,6 +756,9 @@ ^- tape ^~ %+ rip 3 ''' + init() { + Alpine.store("project").update(this.swap_type, this.swap_symbol); + }, initSticky(elem) { const observer = new IntersectionObserver( ([e]) => e.target.firstChild.classList.toggle("pinned", e.intersectionRatio < 1), @@ -867,10 +957,15 @@ } ); }, + editMula(event) { + this.sendForm(event, [], () => ( + Promise.resolve({mut: this.mula_type, mui: this.mula_idex}) + )); + }, }))); ''' == == == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/page/ship.hoon b/desk/bare/web/fund/page/ship.hoon index 3ed53ae..0ce0023 100644 --- a/desk/bare/web/fund/page/ship.hoon +++ b/desk/bare/web/fund/page/ship.hoon @@ -39,4 +39,4 @@ == == -- -:: VERSION: [1 3 0] +:: VERSION: [1 4 0] diff --git a/desk/bare/web/fund/script/boot.js b/desk/bare/web/fund/script/boot.js index 242a180..1359655 100644 --- a/desk/bare/web/fund/script/boot.js +++ b/desk/bare/web/fund/script/boot.js @@ -16,6 +16,7 @@ import ZeroMd from 'https://cdn.jsdelivr.net/npm/zero-md@3'; import DOMPurify from 'https://cdn.jsdelivr.net/npm/dompurify@3.1.3/+esm'; import TippyJs from 'https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/+esm'; import TomSelect from 'https://cdn.jsdelivr.net/npm/tom-select@2.3.1/+esm'; +import UrbitOb from 'https://cdn.jsdelivr.net/npm/urbit-ob@5.0.1/+esm'; import * as SAFE from './safe.js'; import { FUND_SIGN_ADDR } from './config.js'; import { CONTRACT, NETWORK } from './const.js'; @@ -214,8 +215,8 @@ if (window.Alpine === undefined) { ['fund-butn-tr-m', 'fund-butn-true fund-butn-medi'], // true ['fund-butn-fa-m', 'fund-butn-false fund-butn-medi'], // false ['fund-butn-co-m', 'fund-butn-conn fund-butn-medi'], // conn - ['fund-aset-circ', 'h-6 bg-white rounded-full'], - ['fund-aset-boxx', 'h-6 bg-white rounded'], + ['fund-aset-circ', 'h-6 aspect-square bg-white rounded-full'], + ['fund-aset-rect', 'h-6 aspect-square bg-white rounded'], ['fund-odit-ther', 'w-full rounded-md flex h-4 sm:h-8 text-primary-700'], // FIXME: text-primary-600 ['fund-odit-sect', 'h-full flex justify-center items-center text-center first:rounded-l-md last:rounded-r-md'], ], @@ -294,6 +295,35 @@ if (window.Alpine === undefined) { } }); + Alpine.store("wallet", { + address: null, + chain: null, + status: "…loading…", + update(address, chain) { + this.address = address; + this.chain = chain; + this.status = + (address === undefined) ? "connect 💰" + : (address === null) ? "…loading…" + : `${address.slice(0, 5)}…${address.slice(-4)}`; + window.dispatchEvent(new CustomEvent("fund-wallet", {detail: address})); + }, + }); + Alpine.store("project", { + swap: undefined, + symbol: undefined, + nfts: {}, + update(swap, symbol) { + this.swap = swap; + this.symbol = symbol; + this.nfts = {}; + window.dispatchEvent(new CustomEvent("fund-project", {detail: symbol})); + }, + loadNFTs(addr, nfts) { + this.nfts[addr] = nfts; + }, + }); + document.addEventListener('alpine:init', () => Alpine.data('fund', () => ({ cmd, copyText, @@ -306,7 +336,8 @@ if (window.Alpine === undefined) { initTippy, initTomSelect, updateTokenSelect, - updateNftsSelect, + tsCreateOracle, + tsLoadNFTs, CONTRACT, NETWORK, ...SAFE, // FIXME: Makes 'safe.js' available to inline/non-module scripts @@ -359,15 +390,16 @@ if (window.Alpine === undefined) { // FIXME: It's better to use this instead of `window.open` for local URLs // because the `` click emulation prompts a partial turbojs reload where // `window.open` prompts a full page reload - function openHREF(href) { + function openHREF(href, tab=false) { const link = document.createElement("a"); link.setAttribute("class", "hidden"); link.setAttribute("href", href); + if (tab) { link.setAttribute("target", "_blank"); } document.body.appendChild(link); link.click(); } - function sendForm(event, checks = [], action = Promise.resolve(undefined)) { + function sendForm(event, checks=[], action=Promise.resolve(undefined)) { event.preventDefault(); if ((event.target.form !== undefined) && !event.target.form.reportValidity()) { return Promise.resolve(undefined); @@ -391,7 +423,7 @@ if (window.Alpine === undefined) { } // NOTE: 'formData' is not a 'FormData' object; it's a {str => str} map - function sendFormData(formData, event = undefined) { + function sendFormData(formData, event=undefined) { const form = document.createElement("form"); form.method = "post"; // FIXME: This is necessary in order to send the raw message @@ -435,6 +467,16 @@ if (window.Alpine === undefined) { throw new Error(`connected wallet is not the ${roleTitle} wallet for this project; please connect one of the follwing wallets to continue:\n${expectedAddresses.join("\n")}`); } + // FIXME: This doesn't work well for calculating line clamps on the + // 'zero-md' elements because they generate content dynamically + // + // function needsClamp(elem, clamp) { + // const divHeight = elem.offsetHeight + // const lineHeight = parseInt(elem.style.lineHeight); + // const lines = divHeight / lineHeight; + // return lines > clamp; + // } + function initENS(elem, address) { getEnsName(window.Wagmi, {address}).then(ensName => { elem.innerHTML = ensName @@ -458,7 +500,14 @@ if (window.Alpine === undefined) { }); } - function initTomSelect(elem, empty, upOnly = false, maxItems = undefined) { + // function initTomSelect(elem, empty, upOnly=false, maxItems=undefined, create=false) { + function initTomSelect(elem, { + empty=false, // Bool + forceUp=false, // Bool + maxItems=undefined, // Number? + create=undefined, // ((value, data) => void)? + load=undefined, // ((query, callback) => void)? + } = {}) { function renderSelector(data, escape) { return `
@@ -474,12 +523,20 @@ if (window.Alpine === undefined) { const tselElem = new TomSelect(elem, { allowEmptyOption: empty, render: { - option(data, escape) { return renderSelector(data, escape); }, - item(data, escape) { return renderSelector(data, escape); }, + loading: (data, escape) => "
", + option: (data, escape) => renderSelector(data, escape), + item: (data, escape) => renderSelector(data, escape), + ...(!create ? {} : { + no_results: (data, escape) => null, + option_create: (data, escape) => ` +
+ Use custom option ${escape(data.input)} +
`, + }), }, onDropdownOpen: (dropdown) => { if ( - upOnly || + forceUp || (dropdown.getBoundingClientRect().bottom > (window.innerHeight || document.documentElement.clientHeight)) ) { @@ -490,8 +547,14 @@ if (window.Alpine === undefined) { dropdown.classList.remove('dropup'); }, ...(empty ? {} : {controlInput: null}), - ...((maxItems === undefined || maxItems === 0) ? {} : {maxItems}) + ...((maxItems === undefined || maxItems === 0) ? {} : {maxItems}), + ...(!load ? {} : {load}), + ...(!create ? {} : { + create: !!create, + onOptionAdd: create, + }), }); + tselElem?.load && tselElem.load(); elem.matches(":disabled") && tselElem.disable(); } } @@ -505,6 +568,7 @@ if (window.Alpine === undefined) { text: elem.innerText, image: elem.dataset.image, chain: elem.dataset.chain, + href: elem.dataset.href, })).filter(({value, chain}) => ( chain === tokenChain || value === "" )); @@ -517,26 +581,57 @@ if (window.Alpine === undefined) { ); } - function updateNftsSelect(elem) { - const walletNfts = document.querySelector("#fund-nfts-wallet"); - const tokenSelect = document.querySelector('#proj-token').tomselect; - const tokenNftOpts = Array.from(walletNfts.children).map(elem => ({ - value: elem.value, - text: elem.innerText, - image: elem.dataset.image, - })); - if (tokenNftOpts.length === 0) { - tokenNftOpts.push({ - value: "-1", - text: "(no nfts in wallet)", - image: "https://placehold.co/24x24/black/black?text=\\n", - }); - } + function tsCreateOracle(elem) { + return (value, data) => { + const okClans = new Set(["galaxy", "star"]); + if (!UrbitOb.isValidPatp(value) || !okClans.has(UrbitOb.clan(value))) { + elem.tomselect.removeOption(value); + } else if (data?.image === undefined) { + elem.tomselect.updateOption(value, { + value: data.value, + text: data.text, + image: `https://azimuth.network/erc721/${UrbitOb.patp2dec(value)}.svg`, + }); + } + }; + } - tokenSelect.clear(true); - tokenSelect.clearOptions(); - tokenSelect.addOptions(tokenNftOpts); - tokenSelect.addItem(tokenNftOpts[0].value); + function tsLoadNFTs(elem) { + return (query, callback) => { + const self = elem.tomselect; + if (self.loading > 1) return callback(); + + const address = Alpine.store("wallet").address; + const chain = Alpine.store("wallet").chain; + const loadedNFTs = Alpine.store("project").nfts?.[address]; + const loadNFTOptions = loadedNFTs + ? Promise.resolve(loadedNFTs) + : SAFE.nftsGetAll(address, chain, Alpine.store("project").symbol).then(nfts => ( + // TODO: Generalize this logic by querying metadata filters from the BE + nfts.filter(nft => ((nft?.raw?.metadata?.attributes ?? []).some(attr => ( + (attr?.trait_type === "size" && attr?.value === "star") + )))).map(({name, image, tokenId}) => ({ + value: tokenId, + text: name, + image: image.cachedUrl, + })) + )).catch(() => []); + + self.clear(true); + self.clearOptions(); + loadNFTOptions.then(options => { + const nftOptions = (options.length > 0) ? options : [{ + value: "-1", + text: "(no nfts in wallet)", + image: "https://placehold.co/24x24/black/black?text=\\n", + }]; + Alpine.store("project").loadNFTs(address, nftOptions); + callback(nftOptions); + // NOTE: Auto-select if only one available; iffy on the ui/ux + // if (nftOptions.length === 0) { self.addItem(-1); } + delete self.loadedSearches[query]; + }).catch(() => callback()); + }; } window.Wagmi = createConfig({ @@ -614,48 +709,18 @@ if (window.Alpine === undefined) { }); const setPageWallet = ({connections, current, status}) => { - const walletButton = document.querySelector("#fund-butn-wallet"); - const walletNfts = document.querySelector("#fund-nfts-wallet"); - if (status === "disconnected") { const connection = connections.get(current); if (!connection) { - walletButton.innerHTML = "connect 💰"; + Alpine.store("wallet").update(undefined, undefined); } else { reconnect(window.Wagmi, {connector: connection.connector}); } } else if (status === "reconnecting") { - walletButton.innerHTML = "…loading…"; + Alpine.store("wallet").update(null, null); } else if (status === "connected") { const { address, chainId } = getAccount(window.Wagmi); - - walletButton.innerHTML = `${address.slice(0, 5)}…${address.slice(-4)}`; - - const walletSwapType = document.querySelector("#fund-swap-type"); - if (walletSwapType && walletSwapType.value === "enft") { - const walletSwapSymbol = document.querySelector("#fund-swap-symb"); - SAFE.nftsGetAll(address, chainId, walletSwapSymbol.value).then(nfts => { - const options = nfts - .filter(nft => ((nft?.raw?.metadata?.attributes ?? []).some(attr => ( - (attr?.trait_type === "size" && attr?.value === "star") - )))).map(({name, image, tokenId}) => ({ - value: tokenId, - text: name, - image: image.cachedUrl, - })); - console.log(`loading ${options.length} nfts`); - while (walletNfts.firstChild) { - walletNfts.removeChild(walletNfts.lastChild); - } - options.forEach(({value, text, image}) => { - const elem = document.createElement("option"); - elem.setAttribute("value", value); - elem.setAttribute("data-image", image); - elem.innerText = text; - walletNfts.appendChild(elem); - }); - }); - } + Alpine.store("wallet").update(address, chainId); } }; diff --git a/desk/bare/web/fund/script/const.js b/desk/bare/web/fund/script/const.js index dfef294..57f22d9 100644 --- a/desk/bare/web/fund/script/const.js +++ b/desk/bare/web/fund/script/const.js @@ -1,3 +1,5 @@ +import { FUND_ALCH_AKEY, FUND_RPCE_ETHE, FUND_RPCE_SEPO } from './config.js'; + export const FUND_CUT = 0.01; export const ADDRESS = Object.freeze({ @@ -16,33 +18,12 @@ export const NETWORK = Object.freeze({ 11155111: "SEPOLIA", }), APIKEY: Object.freeze({ - ETHEREUM: - ["3", "E", "1", "u", "G", "l", "C", "j", "i", "F", "W", "V", "n", - "L", "r", "1", "3", "b", "b", "n", "J", "B", "F", "F", "W", "c", - "-", "4", "W", "B", "2", "2"].join(""), - SEPOLIA: - ["2", "R", "E", "E", "E", "A", "S", "1", "y", "b", "f", "y", "f", - "H", "R", "3", "4", "w", "B", "D", "F", "C", "I", "H", "C", "S", - "X", "J", "t", "d", "f", "E"].join(""), + ETHEREUM: FUND_ALCH_AKEY, + SEPOLIA: FUND_ALCH_AKEY, }), RPC: Object.freeze({ - // NOTE: Make it at least nontrivial to sniff out and steal these API keys - ETHEREUM: - ["h", "t", "t", "p", "s", ":", "/", "/", "e", "t", "h", - "-", "m", "a", "i", "n", "n", "e", "t", ".", "g", ".", - "a", "l", "c", "h", "e", "m", "y", ".", "c", "o", "m", - "/", "v", "2", "/", "3", "E", "1", "u", "G", "l", "C", - "j", "i", "F", "W", "V", "n", "L", "r", "1", "3", "b", - "b", "n", "J", "B", "F", "F", "W", "c", "-", "4", "W", - "B", "2", "2"].join(""), - SEPOLIA: - ["h", "t", "t", "p", "s", ":", "/", "/", "e", "t", "h", - "-", "s", "e", "p", "o", "l", "i", "a", ".", "g", ".", - "a", "l", "c", "h", "e", "m", "y", ".", "c", "o", "m", - "/", "v", "2", "/", "2", "R", "E", "E", "E", "A", "S", - "1", "y", "b", "f", "y", "f", "H", "R", "3", "4", "w", - "B", "D", "F", "C", "I", "H", "C", "S", "X", "J", "t", - "d", "f", "E"].join(""), + ETHEREUM: FUND_RPCE_ETHE, + SEPOLIA: FUND_RPCE_SEPO, }), }); diff --git a/desk/bare/web/fund/script/safe.js b/desk/bare/web/fund/script/safe.js index 0aa7725..2516702 100644 --- a/desk/bare/web/fund/script/safe.js +++ b/desk/bare/web/fund/script/safe.js @@ -73,7 +73,7 @@ export const nftsGetAll = async (wallet, chainId, token) => { queryUrl.searchParams.append("pageKey", pageKey); } return isLastCall - ? new Promise(resolve => resolve(results)) + ? Promise.resolve(results) : fetch(queryUrl) .then(response => response.json()) .then(json => getNFTs( @@ -103,7 +103,7 @@ export const safeGetBalance = async ({fundToken, safeAddress}) => { } }; -export const safeGetTransfers = async ({fundToken, safeAddress, safeInitBlock}) => { +export const safeGetTransfers = async ({fundToken, safeAddress, safeInitBlock, dirFilter}) => { const TOKEN = safeTransactionToken({tok: fundToken}); // FIXME: If there are ever projects that exceed ~2k contributions, this will // need to be changed to paginate RPC queries. @@ -115,7 +115,12 @@ export const safeGetTransfers = async ({fundToken, safeAddress, safeInitBlock}) fromBlock: BigInt(safeInitBlock), // toBlock: "safe", }); - return transferLogs; + const filteredTransferLogs = transferLogs.filter(({args: {from, to, tokenId}}) => ( + (dirFilter === "with") ? (from === safeAddress) + : (dirFilter === "depo") ? (to === safeAddress) + : true + )); + return filteredTransferLogs; }; export const safeSignDeploy = async ({projectChain, projectContent}) => { @@ -152,9 +157,7 @@ export const safeExecDeploy = async ({projectChain, oracleAddress}) => { safeNextSaltNonce(), ], }); - const deployReceipt = await waitForTransactionReceipt(window.Wagmi, { - hash: deployTransaction, - }); + const deployReceipt = await safeAwaitTransaction({hash: deployTransaction}); const safeAddress = deployReceipt.logs.find( ({topics}) => topics.length > 1 ).address; @@ -183,9 +186,7 @@ export const safeExecDeposit = async ({projectChain, fundAmount, fundToken, fund ); const sendTransaction = await writeContract(window.Wagmi, wrappedTransaction); - const sendReceipt = await waitForTransactionReceipt(window.Wagmi, { - hash: sendTransaction, - }); + const sendReceipt = await safeAwaitTransaction({hash: sendTransaction}); return [ funderAddress, sendReceipt.blockNumber.toString(), @@ -259,6 +260,27 @@ const safeAddressSort = (getAddress = (v) => v) => (a, b) => { [a, b].map(v => fromHex(getAddress(v), "bigint"))); }; +// NOTE: Needed due to aggressive load balancing on RPC endpoints; see here: +// https://github.com/wevm/wagmi/issues/3152 +const safeAwaitTransaction = async ({hash}) => { + let attempts = 0; + const maxAttempts = 3; + + let receipt = undefined; + while (receipt === undefined && attempts++ < maxAttempts) { + try { + receipt = await waitForTransactionReceipt(window.Wagmi, {hash, confirmations: 3}); + } catch (error) { + if (!(error instanceof TransactionNotFoundError)) throw error; + } + } + if (receipt === undefined) { + throw new SafeError(`unable to detect confirmation for transaction '${hash}'`); + } + + return receipt; +}; + const safeWrapTransaction = ({tok, val, from, to}) => { const TOKEN = safeTransactionToken({tok, val, from, to}); return { @@ -292,6 +314,7 @@ const safeGetClaimTransactions = async ({projectChain, fundAmount, fundToken, sa } else if (TOKEN.ABI === ABI.ERC721) { const transfers = await safeGetTransfers({fundToken, safeAddress, safeInitBlock}); const remainingTokens = await nftsGetAll(safeAddress, projectChain, fundToken); + // TODO: Needs to be generalized for any NFT const validTokenSet = new Set(remainingTokens.filter(nft => ( (nft?.raw?.metadata?.attributes ?? []).some(attr => ( (attr?.trait_type === "size" && attr?.value === "star") @@ -338,6 +361,7 @@ const safeGetRefundTransactions = async ({projectChain, fundToken, safeAddress, }); } else if (TOKEN.ABI === ABI.ERC721) { const remainingTokens = await nftsGetAll(safeAddress, projectChain, fundToken); + // TODO: Needs to be generalized for any NFT const validTokenSet = new Set(remainingTokens.filter(nft => ( (nft?.raw?.metadata?.attributes ?? []).some(attr => ( (attr?.trait_type === "size" && attr?.value === "star") @@ -483,8 +507,6 @@ const safeExecWithdrawal = async ({transactions, oracleSignature, oracleAddress, ).reduce((a, n) => concat([a, n]), "") ]), }); - const withdrawReceipt = await waitForTransactionReceipt(window.Wagmi, { - hash: withdrawTransaction, - }); + const withdrawReceipt = await safeAwaitTransaction({hash: withdrawTransaction}); return [withdrawReceipt.blockNumber.toString(), withdrawReceipt.transactionHash]; }; diff --git a/desk/bare/web/fund/style/fund.css b/desk/bare/web/fund/style/fund.css index 188e71c..003c833 100644 --- a/desk/bare/web/fund/style/fund.css +++ b/desk/bare/web/fund/style/fund.css @@ -75,7 +75,7 @@ input[required] + label:after, select[required] + label:after, div.ts-wrapper + label:after { content: '*'; - color: #b80c09; /* text-highlight1-500 */ + color: #ff0033; /* text-highlight1-500 */ } /* Drop-up for selectors near the bottom of the page (https://stackoverflow.com/a/78298291) */ diff --git a/desk/full/lib/fund/alien.hoon b/desk/full/lib/fund/alien.hoon new file mode 120000 index 0000000..eefdb68 --- /dev/null +++ b/desk/full/lib/fund/alien.hoon @@ -0,0 +1 @@ +../../../bare/lib/fund/alien.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/core-0.hoon b/desk/full/lib/fund/core-0.hoon deleted file mode 120000 index c55fe10..0000000 --- a/desk/full/lib/fund/core-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/lib/fund/core-0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/core-1.hoon b/desk/full/lib/fund/core-1.hoon deleted file mode 120000 index 15b24f3..0000000 --- a/desk/full/lib/fund/core-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/lib/fund/core-1.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/core/0.hoon b/desk/full/lib/fund/core/0.hoon new file mode 120000 index 0000000..3d82587 --- /dev/null +++ b/desk/full/lib/fund/core/0.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/core/0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/core/1.hoon b/desk/full/lib/fund/core/1.hoon new file mode 120000 index 0000000..36805a7 --- /dev/null +++ b/desk/full/lib/fund/core/1.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/core/1.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/meta-0.hoon b/desk/full/lib/fund/meta-0.hoon deleted file mode 120000 index 6306633..0000000 --- a/desk/full/lib/fund/meta-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/lib/fund/meta-0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/meta/0.hoon b/desk/full/lib/fund/meta/0.hoon new file mode 120000 index 0000000..ce06fff --- /dev/null +++ b/desk/full/lib/fund/meta/0.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/meta/0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/proj-0.hoon b/desk/full/lib/fund/proj-0.hoon deleted file mode 120000 index a3fda56..0000000 --- a/desk/full/lib/fund/proj-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/lib/fund/proj-0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/proj-1.hoon b/desk/full/lib/fund/proj-1.hoon deleted file mode 120000 index 04b6011..0000000 --- a/desk/full/lib/fund/proj-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/lib/fund/proj-1.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/proj/0.hoon b/desk/full/lib/fund/proj/0.hoon new file mode 120000 index 0000000..c885219 --- /dev/null +++ b/desk/full/lib/fund/proj/0.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/proj/0.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/proj/1.hoon b/desk/full/lib/fund/proj/1.hoon new file mode 120000 index 0000000..2e5eaef --- /dev/null +++ b/desk/full/lib/fund/proj/1.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/proj/1.hoon \ No newline at end of file diff --git a/desk/full/lib/fund/proj/2.hoon b/desk/full/lib/fund/proj/2.hoon new file mode 120000 index 0000000..c396128 --- /dev/null +++ b/desk/full/lib/fund/proj/2.hoon @@ -0,0 +1 @@ +../../../../bare/lib/fund/proj/2.hoon \ No newline at end of file diff --git a/desk/full/sur/contacts.hoon b/desk/full/sur/contacts.hoon new file mode 120000 index 0000000..a8fde11 --- /dev/null +++ b/desk/full/sur/contacts.hoon @@ -0,0 +1 @@ +../../bare/sur/contacts.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/core-0.hoon b/desk/full/sur/fund/core-0.hoon deleted file mode 120000 index 84286ff..0000000 --- a/desk/full/sur/fund/core-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/core-0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/core-1.hoon b/desk/full/sur/fund/core-1.hoon deleted file mode 120000 index b625e12..0000000 --- a/desk/full/sur/fund/core-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/core-1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/core/0.hoon b/desk/full/sur/fund/core/0.hoon new file mode 120000 index 0000000..a599379 --- /dev/null +++ b/desk/full/sur/fund/core/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/core/0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/core/1.hoon b/desk/full/sur/fund/core/1.hoon new file mode 120000 index 0000000..a1fc073 --- /dev/null +++ b/desk/full/sur/fund/core/1.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/core/1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/data-0.hoon b/desk/full/sur/fund/data-0.hoon deleted file mode 120000 index a49a670..0000000 --- a/desk/full/sur/fund/data-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/data-0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/data-1.hoon b/desk/full/sur/fund/data-1.hoon deleted file mode 120000 index bbec6da..0000000 --- a/desk/full/sur/fund/data-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/data-1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/data/0.hoon b/desk/full/sur/fund/data/0.hoon new file mode 120000 index 0000000..215e745 --- /dev/null +++ b/desk/full/sur/fund/data/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/data/0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/data/1.hoon b/desk/full/sur/fund/data/1.hoon new file mode 120000 index 0000000..a6032ef --- /dev/null +++ b/desk/full/sur/fund/data/1.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/data/1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/meta-0.hoon b/desk/full/sur/fund/meta-0.hoon deleted file mode 120000 index df7ffdf..0000000 --- a/desk/full/sur/fund/meta-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/meta-0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/meta/0.hoon b/desk/full/sur/fund/meta/0.hoon new file mode 120000 index 0000000..a3eb45a --- /dev/null +++ b/desk/full/sur/fund/meta/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/meta/0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/proj-0.hoon b/desk/full/sur/fund/proj-0.hoon deleted file mode 120000 index 1d202c4..0000000 --- a/desk/full/sur/fund/proj-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/proj-0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/proj-1.hoon b/desk/full/sur/fund/proj-1.hoon deleted file mode 120000 index b3f7b85..0000000 --- a/desk/full/sur/fund/proj-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/fund/proj-1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/proj/0.hoon b/desk/full/sur/fund/proj/0.hoon new file mode 120000 index 0000000..cb12063 --- /dev/null +++ b/desk/full/sur/fund/proj/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/proj/0.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/proj/1.hoon b/desk/full/sur/fund/proj/1.hoon new file mode 120000 index 0000000..32509e1 --- /dev/null +++ b/desk/full/sur/fund/proj/1.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/proj/1.hoon \ No newline at end of file diff --git a/desk/full/sur/fund/proj/2.hoon b/desk/full/sur/fund/proj/2.hoon new file mode 120000 index 0000000..cc9f022 --- /dev/null +++ b/desk/full/sur/fund/proj/2.hoon @@ -0,0 +1 @@ +../../../../bare/sur/fund/proj/2.hoon \ No newline at end of file diff --git a/desk/full/sur/settings.hoon b/desk/full/sur/settings.hoon new file mode 100644 index 0000000..9ba9683 --- /dev/null +++ b/desk/full/sur/settings.hoon @@ -0,0 +1,44 @@ +/+ *mip +|% +:: +++ settings-0 + =< settings + |% + +$ settings (map key bucket) + +$ bucket (map key val) + +$ val + $% [%s p=@t] + [%b p=?] + [%n p=@] + == + -- +:: +++ settings-1 + =< settings + |% + +$ settings (map key bucket) + -- ++$ bucket (map key val) ++$ key term ++$ val + $~ [%n 0] + $% [%s p=@t] + [%b p=?] + [%n p=@] + [%a p=(list val)] + == +:: ++$ settings (mip desk key bucket) ++$ event + $% [%put-bucket =desk =key =bucket] + [%del-bucket =desk =key] + [%put-entry =desk buc=key =key =val] + [%del-entry =desk buc=key =key] + == ++$ data + $% [%all =settings] + [%bucket =bucket] + [%desk desk=(map key bucket)] + [%entry =val] + == +-- diff --git a/desk/full/sur/sss/meta-0.hoon b/desk/full/sur/sss/meta-0.hoon deleted file mode 120000 index 3afdd2e..0000000 --- a/desk/full/sur/sss/meta-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/meta-0.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/meta/0.hoon b/desk/full/sur/sss/meta/0.hoon new file mode 120000 index 0000000..6f76a05 --- /dev/null +++ b/desk/full/sur/sss/meta/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/meta/0.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj-0.hoon b/desk/full/sur/sss/proj-0.hoon deleted file mode 120000 index a1dafcc..0000000 --- a/desk/full/sur/sss/proj-0.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/proj-0.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj-1.hoon b/desk/full/sur/sss/proj-1.hoon deleted file mode 120000 index e0dc650..0000000 --- a/desk/full/sur/sss/proj-1.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/proj-1.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj-2.hoon b/desk/full/sur/sss/proj-2.hoon deleted file mode 120000 index 735f89c..0000000 --- a/desk/full/sur/sss/proj-2.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/proj-2.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj-3.hoon b/desk/full/sur/sss/proj-3.hoon deleted file mode 120000 index 9569fe3..0000000 --- a/desk/full/sur/sss/proj-3.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/proj-3.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj-4.hoon b/desk/full/sur/sss/proj-4.hoon deleted file mode 120000 index 437be8d..0000000 --- a/desk/full/sur/sss/proj-4.hoon +++ /dev/null @@ -1 +0,0 @@ -../../../bare/sur/sss/proj-4.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/0.hoon b/desk/full/sur/sss/proj/0.hoon new file mode 120000 index 0000000..76d225a --- /dev/null +++ b/desk/full/sur/sss/proj/0.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/0.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/1.hoon b/desk/full/sur/sss/proj/1.hoon new file mode 120000 index 0000000..53e11b7 --- /dev/null +++ b/desk/full/sur/sss/proj/1.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/1.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/2.hoon b/desk/full/sur/sss/proj/2.hoon new file mode 120000 index 0000000..067118c --- /dev/null +++ b/desk/full/sur/sss/proj/2.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/2.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/3.hoon b/desk/full/sur/sss/proj/3.hoon new file mode 120000 index 0000000..37bd35b --- /dev/null +++ b/desk/full/sur/sss/proj/3.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/3.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/4.hoon b/desk/full/sur/sss/proj/4.hoon new file mode 120000 index 0000000..0c8ff6a --- /dev/null +++ b/desk/full/sur/sss/proj/4.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/4.hoon \ No newline at end of file diff --git a/desk/full/sur/sss/proj/5.hoon b/desk/full/sur/sss/proj/5.hoon new file mode 120000 index 0000000..baa8e11 --- /dev/null +++ b/desk/full/sur/sss/proj/5.hoon @@ -0,0 +1 @@ +../../../../bare/sur/sss/proj/5.hoon \ No newline at end of file diff --git a/meta/docs/test.md b/meta/docs/test.md index d6444f9..64bd717 100644 --- a/meta/docs/test.md +++ b/meta/docs/test.md @@ -37,6 +37,7 @@ These tests must be run on `~zod` in order to work! :fund &fund-poke [%proj [our %test] %mula %trib `our 1.000.000 s(xact [5 0x0]) ''] :fund &fund-poke [%proj [our %test] %mula %plej our 50.000.000 6 (crip "{} plej")] :fund &fund-poke [%proj [our %test] %draw 0 [7 0x0]] +:fund &fund-poke [%proj [our %test] %redo ~ ~] :fund &fund-poke [%proj [our %tes2] %init p(title '5', summary '%', assessment [~nec 1.000.000], currency sepolia-usdc:coin:x, milestones ~[m(title '6', summary '^', cost 1.000.000.000.000)])] :fund &fund-poke [%proj [our %tes2] %bump %prop ~] :fund &fund-poke [%prof ~nec %join ~] diff --git a/meta/exec/regen b/meta/exec/regen index 4200594..3c075c9 100755 --- a/meta/exec/regen +++ b/meta/exec/regen @@ -66,3 +66,6 @@ cp mut/lib/manx-utils.hoon full/lib/ git clone --depth 1 https://github.com/Fang-/suite.git pal cp pal/mar/pals/effect.hoon full/mar/pals/ cp pal/sur/pals.hoon full/sur/ + +git clone --depth 1 https://github.com/tloncorp/landscape.git lan +cp lan/desk/sur/settings.hoon full/sur/