1package gnocial23import (4 "chain"5 "chain/runtime"6 "math/overflow"7 "strconv"8)910// Post publishes a top-level post and returns its id.11// gated is yes/1/true/khoa to withhold the body from Render.12// The body is still public chain state. Pass an empty gated value for a public post.13func Post(cur realm, body, gated string) int {14 who := caller(cur)15 mustNoPay()16 if state.paused {17 panic("paused")18 }19 author := mustMember(who)20 return writePost(author, 0, body, isYes(gated))21}2223// Reply publishes a reply to parent and returns the new id. Replies are public.24func Reply(cur realm, parent int, body string) int {25 who := caller(cur)26 mustNoPay()27 if state.paused {28 panic("paused")29 }30 author := mustMember(who)31 target := postByID(parent)32 if target == nil {33 panic("unknown post")34 }35 if target.Deleted {36 panic("deleted")37 }38 id := writePost(author, parent, body, false)39 target.Replies++40 state.replies.Set(pad(parent)+"/"+pad(id), id)41 return id42}4344func writePost(author *Profile, parent int, body string, gated bool) int {45 body = cleanBody(body)46 id := state.nextID47 state.nextID++48 p := &Article{49 ID: id,50 Author: author.Addr,51 Body: body,52 Height: runtime.ChainHeight(),53 ReplyTo: parent,54 Gated: gated,55 }56 state.posts.Set(pad(id), p)57 if parent == 0 {58 author.Posts++59 state.feed.Set(pad(id), id)60 state.byAuthor.Set(authorKey(author.Addr, id), id)61 chain.Emit(62 "ArenaPost",63 "id", strconv.Itoa(id),64 "author", author.Addr.String(),65 "handle", author.Handle,66 "gated", yesNo(gated),67 )68 } else {69 chain.Emit(70 "ArenaReply",71 "id", strconv.Itoa(id),72 "parent", strconv.Itoa(parent),73 "author", author.Addr.String(),74 "handle", author.Handle,75 )76 }77 return id78}7980func yesNo(v bool) string {81 if v {82 return "yes"83 }84 return "no"85}8687// Delete soft-deletes the caller's post and clears its body from current state.88// The original transaction still holds the text in chain history.89func Delete(cur realm, id int) {90 who := caller(cur)91 mustNoPay()92 mustMember(who)93 p := postByID(id)94 if p == nil {95 panic("unknown post")96 }97 if p.Deleted {98 panic("deleted")99 }100 if p.Author != who {101 panic("author only")102 }103 if p.ReplyTo == 0 {104 state.feed.Remove(pad(id))105 state.byAuthor.Remove(authorKey(who, id))106 if author := profileByAddr(who); author != nil && author.Posts > 0 {107 author.Posts--108 }109 } else {110 state.replies.Remove(pad(p.ReplyTo) + "/" + pad(id))111 if parent := postByID(p.ReplyTo); parent != nil && parent.Replies > 0 {112 parent.Replies--113 }114 }115 p.Deleted = true116 p.Body = ""117 p.Gated = false118 chain.Emit("ArenaDelete", "id", strconv.Itoa(id), "author", who.String())119}120121// Like adds the caller's like. One like per account.122func Like(cur realm, id int) {123 who := caller(cur)124 mustNoPay()125 mustMember(who)126 p := livePost(id)127 k := likeKey(id, who)128 if state.likes.Has(k) {129 panic("already liked")130 }131 state.likes.Set(k, 1)132 p.Likes++133 chain.Emit("ArenaLike", "id", strconv.Itoa(id), "from", who.String())134}135136// Unlike removes the caller's like.137func Unlike(cur realm, id int) {138 who := caller(cur)139 mustNoPay()140 mustMember(who)141 p := livePost(id)142 k := likeKey(id, who)143 if !state.likes.Has(k) {144 panic("not liked")145 }146 state.likes.Remove(k)147 if p.Likes > 0 {148 p.Likes--149 }150 chain.Emit("ArenaUnlike", "id", strconv.Itoa(id), "from", who.String())151}152153// Tip forwards the attached ugnot to the post's author. The realm does not keep it.154func Tip(cur realm, id int) {155 who := caller(cur)156 if state.paused {157 panic("paused")158 }159 mustMember(who)160 sent := takeUgnot(cur)161 p := livePost(id)162 author := profileByAddr(p.Author)163 if author == nil {164 panic("register first")165 }166 p.Tips = overflow.Add64p(p.Tips, sent)167 author.TipsIn = overflow.Add64p(author.TipsIn, sent)168 if self := profileByAddr(who); self != nil {169 self.TipsOut = overflow.Add64p(self.TipsOut, sent)170 }171 payout(cur, p.Author, sent, "author")172 chain.Emit(173 "ArenaTip",174 "id", strconv.Itoa(id),175 "from", who.String(),176 "to", p.Author.String(),177 "ugnot", strconv.FormatInt(sent, 10),178 )179}180181// GatedBody returns a gated post's text for the author or a current ticket holder.182// Chain storage is public; this only gates the convenience call and Render.183func GatedBody(cur realm, id int) string {184 who := caller(cur)185 mustNoPay()186 mustMember(who)187 p := livePost(id)188 if !p.Gated {189 return p.Body190 }191 if p.Author != who && getShares(who, p.Author) < 1 {192 panic("need a ticket")193 }194 return p.Body195}196197func livePost(id int) *Article {198 p := postByID(id)199 if p == nil {200 panic("unknown post")201 }202 if p.Deleted {203 panic("deleted")204 }205 return p206}207208// Liked reports whether who liked the post.209func Liked(who address, id int) bool {210 if id <= 0 {211 return false212 }213 return state.likes.Has(likeKey(id, who))214}215Admin() string
Buy(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, handle string, amount int)
Delete(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int)
Feed(before int, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Article
FeedCount() int
Follow(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, handle string)
FollowerList(who string, after string, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Profile
Following(from string, handle string) bool
FollowingList(who string, after string, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Profile
GatedBody(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int) string
GetPost(id int) (struct{ID int; Author .uverse.address; Body string; Height int64; ReplyTo int; Likes int; Replies int; Tips int64; Gated bool; Deleted bool}, bool)
Handles(after string, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Profile
Holdings(holder string, after string, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Holding
Hot(limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Article
LatestID() int
Like(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int)
Liked(who string, id int) bool
MemberCount() int
Paused() bool
Post(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, body string, gated string) int
PostsBy(addr string, before int, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Article
ProfileByHandle(handle string) (struct{Addr .uverse.address; Handle string; Name string; Bio string; Referrer .uverse.address; Joined int64; Posts int; Followers int; Following int; TipsIn int64; TipsOut int64; CreatorFee int64}, bool)
ProfileOf(addr string) (struct{Addr .uverse.address; Handle string; Name string; Bio string; Referrer .uverse.address; Joined int64; Posts int; Followers int; Following int; TipsIn int64; TipsOut int64; CreatorFee int64}, bool)
QuoteBuy(handle string, amount int) (due int64, curve int64, creatorFee int64, protocolFee int64, referralFee int64)
QuoteBuyFor(handle string, buyer string, amount int) (due int64, curve int64, creatorFee int64, protocolFee int64, referralFee int64)
QuoteSell(handle string, amount int) (proceeds int64, curve int64, creatorFee int64, protocolFee int64, referralFee int64)
QuoteSellFor(handle string, seller string, amount int) (proceeds int64, curve int64, creatorFee int64, protocolFee int64, referralFee int64)
Register(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, handle string, name string, bio string, referrer string)
Render(path string) string
Replies(parent int, after int, limit int) []gno.land/r/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/gnocial.Article
Reply(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, parent int, body string) int
Sell(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, handle string, amount int)
SetCreatorFee(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, points int64)
SetPaused(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, on string)
SetSuccessor(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, next string)
SetTreasury(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, next string)
SharesOf(holder string, handle string) int
Successor() string
SupplyOf(handle string) (supply int, reserve int64, holders int)
Sweep(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, confirm string)
Tip(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int)
TotalReserve() int64
TransferAdmin(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, next string)
Treasury() string
Unfollow(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, handle string)
Unlike(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int)
UpdateProfile(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, name string, bio string)
Version() string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.