Smart Contract
FindRelatedAccounts
A.097bafa4e0b48eef.FindRelatedAccounts
1access(all) contract FindRelatedAccounts {
2 // Entitlements
3 access(all) entitlement Owner
4
5 // Paths
6 access(all) let storagePath: StoragePath
7 access(all) let publicPath: PublicPath
8
9 // Events
10 access(all) event RelatedAccount(user: Address, walletId: String, walletName: String, address: String, network: String, action: String)
11
12 access(all) struct AccountInformation{
13 access(all) let name:String
14 access(all) let address:Address?
15 access(all) let network:String //do not use enum because of contract upgrade
16 access(all) let stringAddress: String //other networks besides flow may be not support Address, duplication of flow address in string
17 access(all) let extra: {String : AnyStruct}
18
19 init(name:String, address:Address?, network:String, otherAddress:String?){
20 pre{
21 address == nil && otherAddress != nil || address != nil && otherAddress == nil : "Please put either flow address or other address for account"
22 }
23 self.name=name
24 self.address=address
25 self.network=network
26 self.stringAddress=otherAddress ?? address!.toString()
27 self.extra={}
28 }
29 }
30
31 access(all) resource interface Public{
32 access(all) fun getFlowAccounts() : {String: [Address]}
33 access(all) fun getRelatedAccounts(_ network: String) : {String : [String]}
34 access(all) fun getAllRelatedAccounts() : {String : {String : [String]}}
35 access(all) fun getAllRelatedAccountInfo() : {String : AccountInformation}
36 // verify enfun sure this wallet address exist under the network
37 access(all) fun verify(network: String, address: String) : Bool
38 // linked enfun sure this wallet is linked in both wallet with the same name (but not socially linked only)
39 // only suppfun orts flow for now
40 access(all) fun linked(name: String, network: String, address: Address) : Bool
41 access(all) fun getAccount(name: String, network: String, address: String) : AccountInformation?
42 }
43
44 /// This is just an empty resource we create in storage, you can safely send a reference to it to obtain msg.sender
45 access(all) resource Accounts: Public {
46
47 // { ETH : ETH_Blocto }
48 access(self) let networks: {String : [String]}
49 // { ETH_Blocto : [Address] }
50 access(self) let wallets : {String : [String]}
51 // { ETH_Blocto_Address : AccountInformation }
52 access(self) let accounts : {String : AccountInformation}
53
54 init() {
55 self.networks={}
56 self.wallets={}
57 self.accounts={}
58 }
59
60 access(all) fun linked(name: String, network: String, address: Address) : Bool {
61 if let cap = FindRelatedAccounts.getCapability(address) {
62 if let acct = cap.borrow()!.getAccount(name: name, network: network, address: self.owner!.address.toString()) {
63 return true
64 }
65 }
66 return false
67 }
68
69 access(all) fun verify(network: String, address: String) : Bool {
70 if let wallets = self.networks[network] {
71 for wallet in wallets {
72 let ws = self.wallets[wallet]!
73 for candiidate in ws {
74 if candiidate.toLower() == address.toLower() {
75 return true
76 }
77 }
78 }
79 }
80 return false
81 }
82
83 access(all) fun getFlowAccounts() : {String: [Address]} {
84 let network = "Flow"
85 let tempItems : {String: [Address]} ={}
86 if let wallets = self.networks[network] {
87 for wallet in wallets {
88 let ws = self.wallets[wallet]!
89 for addr in ws {
90 let id = wallet.concat("_").concat(addr)
91 let info = self.accounts[id]!
92
93 let tempArray = tempItems[wallet] ?? []
94 tempArray.append(info.address!)
95 tempItems[wallet] = tempArray
96 }
97 }
98 }
99 return tempItems
100 }
101
102 access(all) fun getRelatedAccounts(_ network: String) : {String : [String]} {
103 return self.internal_getRelatedAccounts(network)[network] ?? {}
104 }
105
106 access(all) fun getAllRelatedAccounts() : {String : {String : [String]}} {
107 return self.internal_getRelatedAccounts(nil)
108 }
109
110 access(all) fun getAllRelatedAccountInfo() : {String : AccountInformation} {
111 return self.accounts
112 }
113
114 access(all) fun getAccount(name: String, network: String, address: String) : AccountInformation? {
115 let id = FindRelatedAccounts.getIdentifier(name: name, network: network, address: address)
116 return self.accounts[id]
117 }
118
119 access(contract) fun internal_getRelatedAccounts(_ network: String?) : {String : {String : [String]}} {
120
121 var isNil = network == nil
122
123 fun wanted(_ n: String) : Bool {
124 if network! == n {
125 return true
126 }
127 return false
128 }
129
130 var tempRes : {String : {String : [String]}} = {}
131 for n in self.networks.keys {
132 if !isNil && !wanted(n) {
133 continue
134 }
135 let tempItems : {String: [String]} = tempRes[n] ?? {}
136 if let wallets = self.networks[n] {
137 for wallet in wallets {
138 let ws = self.wallets[wallet]!
139 let tempArray = tempItems[wallet] ?? []
140 tempArray.appendAll(ws)
141 tempItems[wallet] = tempArray
142 }
143 }
144 tempRes[n] = tempItems
145 }
146 return tempRes
147 }
148
149 access(Owner) fun addFlowAccount(name: String, address:Address) {
150 let network = "Flow"
151 let id = FindRelatedAccounts.getIdentifier(name: name, network: network, address: address.toString())
152 if self.accounts[id] != nil {
153 return
154 }
155 self.internal_add(id: id, acct: AccountInformation(name: name, address:address, network: network, otherAddress:nil))
156 emit RelatedAccount(user: self.owner!.address, walletId: id, walletName: name, address: address.toString(), network: network, action: "add")
157 }
158
159 access(Owner) fun addRelatedAccount(name: String, network: String, address: String) {
160 let id = FindRelatedAccounts.getIdentifier(name: name, network: network, address: address)
161 if self.accounts[id] != nil {
162 return
163 }
164 self.internal_add(id: id, acct: AccountInformation(name: name, address:nil, network: network, otherAddress:address))
165 emit RelatedAccount(user: self.owner!.address, walletId: id, walletName: name, address: address, network: network, action: "add")
166 }
167
168 access(Owner) fun updateFlowAccount(name: String, oldAddress: Address, address:Address) {
169 self.removeRelatedAccount(name: name, network: "Flow", address: oldAddress.toString())
170 self.addFlowAccount(name: name, address: address)
171 }
172
173 access(Owner) fun updateRelatedAccount(name: String, network: String, oldAddress: String, address: String) {
174 self.removeRelatedAccount(name: name, network: network, address: oldAddress)
175 self.addRelatedAccount(name: name, network: network, address: address)
176 }
177
178 access(Owner) fun removeRelatedAccount(name: String, network: String, address: String) {
179 let id = FindRelatedAccounts.getIdentifier(name: name, network: network, address: address)
180 if self.accounts[id] == nil {
181 panic(network.concat(" address is not added as related account : ").concat(address))
182 }
183 self.internal_remove(id: id)
184 emit RelatedAccount(user: self.owner!.address, walletId: id, walletName: name, address: address, network: network, action: "remove")
185 }
186
187 access(contract) fun internal_add(id: String, acct: AccountInformation) {
188 let walletName = acct.network.concat("_").concat(acct.name)
189 let tempNetworks = self.networks[acct.network] ?? []
190 if !tempNetworks.contains(walletName) {
191 tempNetworks.append(walletName)
192 self.networks[acct.network] = tempNetworks
193 }
194
195 let tempWallets = self.wallets[walletName] ?? []
196 tempWallets.append(acct.stringAddress)
197 self.wallets[walletName] = tempWallets
198
199 self.accounts[id] = acct
200 }
201
202 access(contract) fun internal_remove(id: String) {
203 let acct = self.accounts.remove(key: id)!
204
205 let walletName = acct.network.concat("_").concat(acct.name)
206 let tempWallets = self.wallets[walletName]!
207 tempWallets.remove(at: tempWallets.firstIndex(of: acct.stringAddress)!)
208 if tempWallets.length > 0 {
209 self.wallets[walletName] = tempWallets
210 return
211 }
212 self.wallets.remove(key: walletName)
213
214 let tempNetwork = self.networks[acct.network]!
215 tempNetwork.remove(at: tempNetwork.firstIndex(of: walletName)!)
216 if tempNetwork.length > 0 {
217 self.networks[acct.network] = tempNetwork
218 return
219 }
220 self.networks.remove(key: acct.network)
221 }
222 }
223
224 access(all) fun createEmptyAccounts() : @Accounts{
225 return <- create Accounts()
226 }
227
228 access(all) fun getIdentifier(name: String, network: String, address: String) : String {
229 return network.concat("_").concat(name).concat("_").concat(address)
230 }
231
232 access(all) fun getCapability(_ addr: Address) : Capability<&{Public}>? {
233 let cap = getAccount(addr).capabilities.get<&{Public}>(self.publicPath)
234 if !cap.check() {
235 return nil
236 }
237 return cap
238
239 }
240
241 access(all) fun findRelatedFlowAccounts(address:Address) : {String: [Address]} {
242 let cap = self.getCapability(address)
243 if cap == nil {
244 return {}
245 }
246
247 return cap!.borrow()!.getFlowAccounts()
248 }
249
250 access(all) fun findRelatedAccounts(address:Address) : {String: {String: [String]}} {
251 let cap = self.getCapability(address)
252 if cap == nil {
253 return {}
254 }
255
256 return cap!.borrow()!.getAllRelatedAccounts()
257 }
258
259 init() {
260 self.storagePath = /storage/findRelatedAccounts
261 self.publicPath = /public/findRelatedAccounts
262 }
263
264}
265