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

3.94 Kb · 188 lines
  1package deadmanswitch
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"chain/runtime"
  7	"chain/runtime/unsafe"
  8)
  9
 10type Status int
 11
 12const (
 13	StatusActive Status = iota
 14	StatusClaimed
 15)
 16
 17func (s Status) String() string {
 18	switch s {
 19	case StatusActive:
 20		return "Active"
 21	case StatusClaimed:
 22		return "Claimed"
 23	default:
 24		return "Unknown"
 25	}
 26}
 27
 28var (
 29	initialized   bool
 30	owner         address
 31	beneficiary   address
 32	timeoutBlocks int64
 33	lastHeartbeat int64
 34	status        Status
 35)
 36
 37func Initialize(cur realm, ben address, timeout int64) {
 38	if !cur.Previous().IsUserCall() {
 39		panic("deadmanswitch: only direct user calls can initialize")
 40	}
 41	if initialized {
 42		panic("deadmanswitch: already initialized")
 43	}
 44	if timeout <= 0 {
 45		panic("deadmanswitch: timeoutBlocks must be positive")
 46	}
 47
 48	caller := cur.Previous().Address()
 49	if ben == caller {
 50		panic("deadmanswitch: beneficiary cannot be the owner")
 51	}
 52	if ben == "" {
 53		panic("deadmanswitch: beneficiary cannot be empty")
 54	}
 55
 56	owner = caller
 57	beneficiary = ben
 58	timeoutBlocks = timeout
 59	lastHeartbeat = runtime.ChainHeight()
 60	status = StatusActive
 61	initialized = true
 62}
 63
 64func Deposit(cur realm) {
 65	requireOwner(cur)
 66
 67	sent := unsafe.OriginSend()
 68	amount := sent.AmountOf("ugnot")
 69	if amount <= 0 {
 70		panic("deadmanswitch: deposit must include a positive ugnot amount")
 71	}
 72
 73	lastHeartbeat = runtime.ChainHeight()
 74}
 75
 76func Heartbeat(cur realm) {
 77	requireOwner(cur)
 78	lastHeartbeat = runtime.ChainHeight()
 79}
 80
 81func WithdrawOwner(cur realm, amount int64) {
 82	requireOwner(cur)
 83	if amount <= 0 {
 84		panic("deadmanswitch: withdraw amount must be positive")
 85	}
 86
 87	lastHeartbeat = runtime.ChainHeight()
 88	payout(cur, owner, amount)
 89}
 90
 91func ChangeBeneficiary(cur realm, newBeneficiary address) {
 92	requireOwner(cur)
 93	if newBeneficiary == owner {
 94		panic("deadmanswitch: beneficiary cannot be the owner")
 95	}
 96	if newBeneficiary == "" {
 97		panic("deadmanswitch: beneficiary cannot be empty")
 98	}
 99
100	beneficiary = newBeneficiary
101	lastHeartbeat = runtime.ChainHeight()
102}
103
104func ClaimAsBeneficiary(cur realm) {
105	if !initialized {
106		panic("deadmanswitch: not initialized yet")
107	}
108	if !cur.Previous().IsUserCall() {
109		panic("deadmanswitch: only direct user calls can claim")
110	}
111	if cur.Previous().Address() != beneficiary {
112		panic("deadmanswitch: caller is not the beneficiary")
113	}
114	if status != StatusActive {
115		panic("deadmanswitch: vault is not active (status=" + status.String() + ")")
116	}
117	if runtime.ChainHeight() < lastHeartbeat+timeoutBlocks {
118		panic("deadmanswitch: owner has not gone inactive long enough yet")
119	}
120
121	realmAddr := cur.Address()
122	b := banker.NewBanker(banker.BankerTypeRealmSend, cur)
123	balance := b.GetCoins(realmAddr).AmountOf("ugnot")
124
125	status = StatusClaimed
126
127	if balance > 0 {
128		b.SendCoins(realmAddr, beneficiary, chain.Coins{chain.NewCoin("ugnot", balance)})
129	}
130}
131
132func requireOwner(cur realm) {
133	if !initialized {
134		panic("deadmanswitch: not initialized yet")
135	}
136	if !cur.Previous().IsUserCall() {
137		panic("deadmanswitch: only direct user calls are accepted")
138	}
139	if cur.Previous().Address() != owner {
140		panic("deadmanswitch: caller is not the owner")
141	}
142	if status != StatusActive {
143		panic("deadmanswitch: vault is not active (status=" + status.String() + ")")
144	}
145}
146
147func payout(cur realm, to address, amount int64) {
148	realmAddr := cur.Address()
149	b := banker.NewBanker(banker.BankerTypeRealmSend, cur)
150
151	have := b.GetCoins(realmAddr).AmountOf("ugnot")
152	if have < amount {
153		panic("deadmanswitch: internal invariant violation, insufficient realm balance for payout")
154	}
155
156	b.SendCoins(realmAddr, to, chain.Coins{chain.NewCoin("ugnot", amount)})
157}
158
159func GetOwner() address {
160	return owner
161}
162
163func GetBeneficiary() address {
164	return beneficiary
165}
166
167func GetStatus() string {
168	if !initialized {
169		return "NotInitialized"
170	}
171	return status.String()
172}
173
174func GetLastHeartbeat() int64 {
175	return lastHeartbeat
176}
177
178func GetTimeoutBlocks() int64 {
179	return timeoutBlocks
180}
181
182func GetClaimableAtBlock() int64 {
183	return lastHeartbeat + timeoutBlocks
184}
185
186func IsInitialized() bool {
187	return initialized
188}