Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

deadmanswitch.gno

5.17 Kb · 256 lines
  1package deadmanswitch
  2
  3type SwitchStatus int
  4
  5const (
  6	SwitchActive SwitchStatus = iota
  7	SwitchTriggered
  8	SwitchClaimed
  9	SwitchCancelled
 10)
 11
 12type Beneficiary struct {
 13	Addr    string
 14	SharePct int64 // out of 100
 15}
 16
 17type Switch struct {
 18	ID            string
 19	Owner         string
 20	LastCheckin   int64
 21	TimeoutBlocks int64
 22	Balance       int64
 23	Beneficiaries []Beneficiary
 24	Status        SwitchStatus
 25	claimed       map[string]bool
 26}
 27
 28type Registry struct {
 29	admin         string
 30	switches      map[string]*Switch
 31	nextSeq       int64
 32	currentHeight int64
 33}
 34
 35func NewRegistry(admin string) *Registry {
 36	return &Registry{admin: admin, switches: make(map[string]*Switch)}
 37}
 38
 39func (r *Registry) Tick(height int64) {
 40	if height > r.currentHeight {
 41		r.currentHeight = height
 42	}
 43}
 44
 45func (r *Registry) CreateSwitch(owner string, timeoutBlocks int64) (string, bool) {
 46	if owner == "" || timeoutBlocks <= 0 {
 47		return "", false
 48	}
 49	r.nextSeq++
 50	id := itoa(r.nextSeq)
 51	r.switches[id] = &Switch{
 52		ID:            id,
 53		Owner:         owner,
 54		LastCheckin:   r.currentHeight,
 55		TimeoutBlocks: timeoutBlocks,
 56		Status:        SwitchActive,
 57		claimed:       make(map[string]bool),
 58	}
 59	return id, true
 60}
 61
 62func (r *Registry) SetBeneficiaries(caller, switchID string, addrs []string, pcts []int64) bool {
 63	s, ok := r.switches[switchID]
 64	if !ok || s.Status != SwitchActive || caller != s.Owner {
 65		return false
 66	}
 67	if len(addrs) != len(pcts) || len(addrs) == 0 {
 68		return false
 69	}
 70	var total int64
 71	for _, p := range pcts {
 72		if p <= 0 {
 73			return false
 74		}
 75		total += p
 76	}
 77	if total != 100 {
 78		return false
 79	}
 80	bens := make([]Beneficiary, len(addrs))
 81	for i, a := range addrs {
 82		bens[i] = Beneficiary{Addr: a, SharePct: pcts[i]}
 83	}
 84	s.Beneficiaries = bens
 85	return true
 86}
 87
 88func (r *Registry) Deposit(switchID string, amount int64) bool {
 89	s, ok := r.switches[switchID]
 90	if !ok || s.Status != SwitchActive || amount <= 0 {
 91		return false
 92	}
 93	s.Balance += amount
 94	return true
 95}
 96
 97func (r *Registry) Checkin(caller, switchID string) bool {
 98	s, ok := r.switches[switchID]
 99	if !ok || s.Status != SwitchActive || caller != s.Owner {
100		return false
101	}
102	s.LastCheckin = r.currentHeight
103	return true
104}
105
106func (r *Registry) Trigger(switchID string) bool {
107	s, ok := r.switches[switchID]
108	if !ok || s.Status != SwitchActive {
109		return false
110	}
111	if r.currentHeight-s.LastCheckin < s.TimeoutBlocks {
112		return false
113	}
114	if len(s.Beneficiaries) == 0 {
115		return false
116	}
117	s.Status = SwitchTriggered
118	return true
119}
120
121func (r *Registry) ClaimShare(caller, switchID string) int64 {
122	s, ok := r.switches[switchID]
123	if !ok || s.Status != SwitchTriggered {
124		return 0
125	}
126	if s.claimed[caller] {
127		return 0
128	}
129	var pct int64 = -1
130	for _, b := range s.Beneficiaries {
131		if b.Addr == caller {
132			pct = b.SharePct
133			break
134		}
135	}
136	if pct < 0 {
137		return 0
138	}
139	share := (s.Balance * pct) / 100
140	s.claimed[caller] = true
141	allClaimed := true
142	for _, b := range s.Beneficiaries {
143		if !s.claimed[b.Addr] {
144			allClaimed = false
145			break
146		}
147	}
148	if allClaimed {
149		s.Status = SwitchClaimed
150	}
151	return share
152}
153
154func (r *Registry) Cancel(caller, switchID string) bool {
155	s, ok := r.switches[switchID]
156	if !ok || caller != s.Owner || s.Status != SwitchActive {
157		return false
158	}
159	s.Status = SwitchCancelled
160	return true
161}
162
163func (r *Registry) GetSwitch(id string) (*Switch, bool) {
164	s, ok := r.switches[id]
165	return s, ok
166}
167
168func (r *Registry) BlocksUntilTrigger(switchID string) int64 {
169	s, ok := r.switches[switchID]
170	if !ok || s.Status != SwitchActive {
171		return -1
172	}
173	elapsed := r.currentHeight - s.LastCheckin
174	remaining := s.TimeoutBlocks - elapsed
175	if remaining < 0 {
176		return 0
177	}
178	return remaining
179}
180
181func itoa(n int64) string {
182	if n == 0 {
183		return "0"
184	}
185	neg := n < 0
186	if neg {
187		n = -n
188	}
189	var buf [20]byte
190	i := len(buf)
191	for n > 0 {
192		i--
193		buf[i] = byte('0' + n%10)
194		n /= 10
195	}
196	s := string(buf[i:])
197	if neg {
198		s = "-" + s
199	}
200	return s
201}
202
203// package-level singleton + exported entry points for the realm
204
205var registry = NewRegistry("g16m0r7rm7fv5hx0ekr7gvx8g4fr7eu08nzhk6gt")
206
207func Tick(height int64) {
208	registry.Tick(height)
209}
210
211func CreateSwitch(owner string, timeoutBlocks int64) string {
212	id, ok := registry.CreateSwitch(owner, timeoutBlocks)
213	if !ok {
214		return ""
215	}
216	return id
217}
218
219func SetBeneficiaries(caller, switchID string, addrs []string, pcts []int64) bool {
220	return registry.SetBeneficiaries(caller, switchID, addrs, pcts)
221}
222
223func Deposit(switchID string, amount int64) bool {
224	return registry.Deposit(switchID, amount)
225}
226
227func Checkin(caller, switchID string) bool {
228	return registry.Checkin(caller, switchID)
229}
230
231func Trigger(switchID string) bool {
232	return registry.Trigger(switchID)
233}
234
235func ClaimShare(caller, switchID string) int64 {
236	return registry.ClaimShare(caller, switchID)
237}
238
239func Cancel(caller, switchID string) bool {
240	return registry.Cancel(caller, switchID)
241}
242
243func BlocksUntilTrigger(switchID string) int64 {
244	return registry.BlocksUntilTrigger(switchID)
245}
246
247func Render(path string) string {
248	if path == "" {
249		return "DeadManSwitch realm — use CreateSwitch, Checkin, Trigger, ClaimShare"
250	}
251	s, ok := registry.GetSwitch(path)
252	if !ok {
253		return "switch not found: " + path
254	}
255	return "Owner: " + s.Owner + " | Balance: " + itoa(s.Balance) + " | Beneficiaries: " + itoa(int64(len(s.Beneficiaries)))
256}