Smart Contract

BloctoStorageRent

A.1dfd1e5b87b847dc.BloctoStorageRent

Dependents

0 imports
1/*
2This tool adds a new entitlemtent called all to some functions that it cannot be sure if it is safe to make access(all)
3those functions you should check and update their entitlemtents ( or change to all access )
4
5Please see: 
6https://cadence-lang.org/docs/cadence-migration-guide/nft-guide#update-all-pub-access-modfiers
7
8IMPORTANT SECURITY NOTICE
9Please familiarize yourself with the new entitlements feature because it is extremely important for you to understand in order to build safe smart contracts.
10If you change pub to access(all) without paying attention to potential downcasting from public interfaces, you might expose private functions like withdraw 
11that will cause security problems for your contract.
12
13*/
14
15import FungibleToken from 0xf233dcee88fe0abe
16import FlowStorageFees from 0xe467b9dd11fa00df
17import FlowToken from 0x1654653399040a61
18
19access(all)
20contract BloctoStorageRent{ 
21	access(all)
22	let BloctoStorageRentAdminStoragePath: StoragePath
23	
24	access(contract)
25	var StorageRentRefillThreshold: UInt64
26	
27	access(contract)
28	var RefilledAccounts: [Address]
29	
30	access(contract)
31	var RefilledAccountInfos:{ Address: RefilledAccountInfo}
32	
33	access(contract)
34	var RefillRequiredBlocks: UInt64
35	
36	access(all)
37	view fun getStorageRentRefillThreshold(): UInt64{ 
38		return self.StorageRentRefillThreshold
39	}
40	
41	access(all)
42	view fun getRefilledAccounts(): [Address]{ 
43		return self.RefilledAccounts
44	}
45	
46	access(all)
47	view fun getRefilledAccountInfos():{ Address: RefilledAccountInfo}{ 
48		return self.RefilledAccountInfos
49	}
50	
51	access(all)
52	view fun getRefillRequiredBlocks(): UInt64{ 
53		return self.RefillRequiredBlocks
54	}
55	
56	access(all)
57	fun tryRefill(_ address: Address){ 
58		self.cleanExpiredRefilledAccounts(10)
59		let recipient = getAccount(address)
60		let receiverRef =
61			recipient.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).borrow()
62		if receiverRef == nil || (receiverRef!).owner == nil{ 
63			return
64		}
65
66		if self.RefilledAccountInfos[address] != nil
67		&& getCurrentBlock().height - (self.RefilledAccountInfos[address]!).atBlock
68		< self.RefillRequiredBlocks{ 
69			return
70		}
71		var low: UInt64 = recipient.storage.used
72		var high: UInt64 = recipient.storage.capacity
73		if high < low{ 
74			high <-> low
75		}
76		if high - low < self.StorageRentRefillThreshold {
77			let vaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
78				?? panic("Could not borrow reference to the owner's Vault!")
79			
80			let requiredAmount = FlowStorageFees.storageCapacityToFlow(FlowStorageFees.convertUInt64StorageBytesToUFix64Megabytes(self.StorageRentRefillThreshold * 2))
81			self.addRefilledAccount(address)
82			(receiverRef!).deposit(from: <-(vaultRef!).withdraw(amount: requiredAmount))
83		}
84	}
85	
86	access(all)
87	view fun checkEligibility(_ address: Address): Bool{ 
88		if self.RefilledAccountInfos[address] != nil
89		&& getCurrentBlock().height - (self.RefilledAccountInfos[address]!).atBlock
90		< self.RefillRequiredBlocks{ 
91			return false
92		}
93		let acct = getAccount(address)
94		var high: UInt64 = acct.storage.capacity
95		var low: UInt64 = acct.storage.used
96		if high < low{ 
97			high <-> low
98		}
99		if high - low >= self.StorageRentRefillThreshold{ 
100			return false
101		}
102		return true
103	}
104	
105	access(contract)
106	fun addRefilledAccount(_ address: Address){ 
107		if self.RefilledAccountInfos[address] != nil{ 
108			self.RefilledAccounts.remove(at: (self.RefilledAccountInfos[address]!).index)
109		}
110		self.RefilledAccounts.append(address)
111		self.RefilledAccountInfos[address] = RefilledAccountInfo(
112				self.RefilledAccounts.length - 1,
113				getCurrentBlock().height
114			)
115	}
116	
117	access(all)
118	fun cleanExpiredRefilledAccounts(_ batchSize: Int){ 
119		var index = 0
120		while index < batchSize && self.RefilledAccounts.length > index{ 
121			if self.RefilledAccountInfos[self.RefilledAccounts[index]] != nil && getCurrentBlock().height - (self.RefilledAccountInfos[self.RefilledAccounts[index]]!).atBlock < self.RefillRequiredBlocks{ 
122				break
123			}
124			self.RefilledAccountInfos.remove(key: self.RefilledAccounts[index])
125			self.RefilledAccounts.remove(at: index)
126			index = index + 1
127		}
128	}
129	
130	access(all)
131	struct RefilledAccountInfo{ 
132		access(all)
133		let atBlock: UInt64
134		
135		access(all)
136		let index: Int
137		
138		init(_ index: Int, _ atBlock: UInt64){ 
139			self.index = index
140			self.atBlock = atBlock
141		}
142	}
143	
144	access(all)
145	resource Admin{ 
146		access(all)
147		fun setStorageRentRefillThreshold(_ threshold: UInt64){ 
148			BloctoStorageRent.StorageRentRefillThreshold = threshold
149		}
150		
151		access(all)
152		fun setRefillRequiredBlocks(_ blocks: UInt64){ 
153			BloctoStorageRent.RefillRequiredBlocks = blocks
154		}
155	}
156
157	init(){ 
158		self.BloctoStorageRentAdminStoragePath = /storage/BloctoStorageRentAdmin
159		self.StorageRentRefillThreshold = 5000
160		self.RefilledAccounts = []
161		self.RefilledAccountInfos ={} 
162		self.RefillRequiredBlocks = 86400
163		let admin <- create Admin()
164		self.account.storage.save(<-admin, to:  /storage/BloctoStorageRentAdmin)
165	}
166}