Smart Contract
JoyrideAccounts
A.ecfad18ba9582d4f.JoyrideAccounts
1import FungibleToken from 0xf233dcee88fe0abe
2import JoyrideMultiToken from 0xecfad18ba9582d4f
3pub contract JoyrideAccounts {
4
5 access(contract) var gameToDeveloperAccount : {String: Address}
6 access(contract) var playerIDToPlayer : {String: Address}
7 access(contract) var treasuryCapability : Capability<&JoyrideMultiToken.PlatformAdmin>?
8 access(contract) var playerAccounts : @{String: PlayerAccount}
9 access(contract) var playerIdByAddress : {Address: String}
10
11 pub event AccountCreated(playerID: String, referralID: String?, accountAddress: Address)
12 pub event LinkDeveloperAccount(accountAddress: Address, gameID: String)
13 pub event AccountAlreadyExists(playerID: String, accountAddress: Address)
14 pub event InsufficientBalance(playerID: String, txID: String, amount: UFix64, currentBalance: UFix64)
15 init() {
16 self.playerIDToPlayer = {}
17 self.gameToDeveloperAccount = {}
18 self.playerAccounts <- {}
19 self.treasuryCapability = nil
20 self.playerIdByAddress = {}
21
22 self.account.save(<- create JoyrideAccountsAdmin(), to: /storage/JoyrideAccountsAdmin)
23 let cap = self.account.link<&JoyrideAccountsAdmin{GrantsTokenRewards, SharesProfits, PlayerAccounts}>(/private/JoyrideAccountsAdmin, target: /storage/JoyrideAccountsAdmin)
24 }
25
26 //Pretty sure this is safe to be public, since a valid Capability<&{JRXToken.Treasury}> can only be created by the JRXToken contract account.
27 pub fun linkTreasuryCapability(treasuryCapability: Capability<&JoyrideMultiToken.PlatformAdmin>) {
28 if(!treasuryCapability.check()) {panic("Capability from Invalid Source")}
29 self.treasuryCapability = treasuryCapability
30 }
31
32 pub fun PlayerRegisterCapability(capability:Capability<&JoyrideMultiToken.Vault>) {
33 let playerID = JoyrideAccounts.playerIdByAddress[capability.address] ?? panic("PlayerID not Found by Address")
34 let playerAccount <- JoyrideAccounts.playerAccounts.remove(key: playerID) ?? panic("PlayerID not found in JoyrideAccounts")
35 playerAccount.RegisterVaultCapability(capability: capability)
36 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <-playerAccount)
37 }
38
39 pub fun getPlayerAccount(playerID: String) : PublicAccount {
40 if(self.playerIDToPlayer[playerID] == nil) { panic("Player not found for playerID: ".concat(playerID)) }
41 return getAccount(self.playerIDToPlayer[playerID]!)
42 }
43
44 pub resource interface ReceivesPlayerRewards {
45 access(contract) fun ReceiveRewards(totalRewards: UFix64, rewardID: String, tokenContext: String) : Bool
46 }
47
48 pub resource PlayerAccount : ReceivesPlayerRewards {
49 pub let playerID: String
50 pub var accountAddress: Address?
51 access(self) var vaultCapability: Capability<&JoyrideMultiToken.Vault>?
52
53 init(playerID: String, referralID: String?) {
54 self.playerID = playerID
55 self.accountAddress = nil
56 self.vaultCapability = nil
57 }
58
59 access(contract) fun ReceiveRewards(totalRewards: UFix64, rewardID: String, tokenContext: String) : Bool {
60 if(self.vaultCapability == nil || JoyrideAccounts.treasuryCapability == nil) { return false }
61
62 let playerVault = self.vaultCapability!.borrow()
63 let treasury = JoyrideAccounts.treasuryCapability!.borrow()
64 if(playerVault == nil || treasury == nil) { return false }
65
66 let rewardVault <- treasury!.withdraw(vault: JoyrideMultiToken.Vaults.treasury, tokenContext: tokenContext, amount: totalRewards, purpose: rewardID)
67 if(rewardVault == nil) {
68 destroy rewardVault
69 return false
70 }
71 else {
72 playerVault!.depositToken(from: <-rewardVault!)
73 return true
74 }
75 }
76
77 //This must be called if the resource is ever transfered to another account.
78 access(contract) fun AssociatePaymentAddress(address: Address) {
79 JoyrideAccounts.playerIDToPlayer[self.playerID] = address
80 self.accountAddress = address
81 JoyrideAccounts.playerIdByAddress[address] = self.playerID
82 }
83
84 access(contract) fun WithdrawTokensForPayment(playerID: String, txID: String, amount: UFix64, tokenContext: String): @FungibleToken.Vault? {
85 if(self.vaultCapability == nil) {
86 return nil
87 }
88
89 let vault = self.vaultCapability!.borrow()
90 if(vault == nil) {
91 return nil
92 }
93
94 let currentBalance: UFix64 = vault?.balanceOf(tokenContext: tokenContext) ?? 0.0;
95 if(currentBalance < amount){
96 emit InsufficientBalance(playerID:playerID, txID:txID, amount:amount, currentBalance:currentBalance)
97 return nil;
98 }
99
100 let withdrawal <- vault?.withdrawToken(tokenContext: tokenContext, amount: amount)
101 if(withdrawal?.balance != amount) {
102 destroy <- withdrawal!
103 return nil
104 } else {
105 return <- withdrawal
106 }
107 }
108
109 access(contract) fun DepositTokens(vault: @FungibleToken.Vault) {
110 let playerVault = (self.vaultCapability ?? panic("No Capability Registered")).borrow() ?? panic("Capability could not be borrowed!")
111 playerVault.depositToken(from: <- vault)
112 }
113
114 access(contract) fun RegisterVaultCapability(capability:Capability<&JoyrideMultiToken.Vault>) {
115 if(capability.address != self.accountAddress) {panic("Cannot register capability to incorrect address")}
116 self.vaultCapability = capability
117 }
118 }
119
120 //Rewards and Payments Interfaces
121 //
122 //
123 //
124
125 pub resource interface GrantsTokenRewards {
126 pub fun GrantTokenRewards(playerID: String, amount: UFix64, tokenContext: String, rewardID: String) : Bool
127 }
128
129 pub resource interface SharesProfits {
130 pub fun ShareProfits(profits:@FungibleToken.Vault, inGameID: String, fromPlayerID: String) : @FungibleToken.Vault
131 pub fun ShareProfitsWithDevPercentage(profits:@FungibleToken.Vault, inGameID: String, fromPlayerID: String, devPercentage: UFix64) : @FungibleToken.Vault
132 }
133
134 pub resource interface PlayerAccounts {
135 pub fun GetPlayerJRXAccount(playerID: String) : &PlayerAccount?
136 pub fun AddPlayerAccount(playerID: String, referralID: String?, account: PublicAccount)
137 pub fun EscrowWithdraw(playerID: String, amount:UFix64, tokenContext: String): @FungibleToken.Vault?
138 pub fun PlayerDeposit(playerID: String, vault:@FungibleToken.Vault) : @FungibleToken.Vault?
139 pub fun AddDeveloperAccount(address: Address, gameID: String)
140 pub fun TransferPlayerAccount(playerID: String, to: PublicAccount)
141 pub fun EscrowWithdrawWithTnxId(playerID: String, txID: String, amount:UFix64, tokenContext: String): @FungibleToken.Vault?
142 }
143
144 pub resource JoyrideAccountsAdmin : GrantsTokenRewards, SharesProfits, PlayerAccounts {
145 pub fun GrantTokenRewards(playerID: String, amount: UFix64, tokenContext: String, rewardID: String) : Bool {
146 let playerAccount <- JoyrideAccounts.playerAccounts.remove(key: playerID)
147 if(playerAccount == nil) {
148 destroy playerAccount
149 return false
150 } else {
151 playerAccount?.ReceiveRewards(totalRewards: amount, rewardID: rewardID, tokenContext: tokenContext)
152 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <-playerAccount!)
153 return true
154 }
155 }
156
157 pub fun AddPlayerAccount(playerID: String, referralID: String?, account: PublicAccount) {
158 var checkAccount <- JoyrideAccounts.playerAccounts.remove(key: playerID)
159 if(checkAccount == nil){
160 destroy checkAccount
161 var playerAccount <-create PlayerAccount(playerID: playerID, referralID: referralID)
162 playerAccount.AssociatePaymentAddress(address: account.address)
163 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <-playerAccount)
164 emit AccountCreated(playerID: playerID, referralID: referralID, accountAddress: account.address)
165 }else {
166 checkAccount?.AssociatePaymentAddress(address: account.address)
167 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <-checkAccount!)
168 emit AccountAlreadyExists(playerID: playerID, accountAddress: account.address)
169 }
170 }
171
172 pub fun AddDeveloperAccount(address: Address, gameID: String) {
173 JoyrideAccounts.gameToDeveloperAccount[gameID] = address
174 emit LinkDeveloperAccount(accountAddress: address, gameID: gameID)
175 }
176
177 pub fun TransferPlayerAccount(playerID: String, to: PublicAccount) {
178 let checkAccount <- JoyrideAccounts.playerAccounts.remove(key: playerID) ?? panic("PlayerID not found!")
179 checkAccount.AssociatePaymentAddress(address: to.address)
180 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <-checkAccount)
181 }
182
183 pub fun GetPlayerJRXAccount(playerID: String) : &PlayerAccount? {
184 if(JoyrideAccounts.playerAccounts.containsKey(playerID)) {
185 let playerAccount <- JoyrideAccounts.playerAccounts.remove(key: playerID)!
186 let reference = &playerAccount as &PlayerAccount
187 destroy JoyrideAccounts.playerAccounts.insert(key: playerID, <- playerAccount)
188 return reference
189 }
190 return nil
191 }
192
193 pub fun ShareProfits(profits:@FungibleToken.Vault, inGameID: String, fromPlayerID: String) : @FungibleToken.Vault {
194 return <- self.ShareProfitsWithDevPercentage(profits: <- profits, inGameID: inGameID, fromPlayerID: fromPlayerID, devPercentage: 0.0)
195 }
196
197 pub fun ShareProfitsWithDevPercentage(profits:@FungibleToken.Vault, inGameID: String, fromPlayerID: String, devPercentage: UFix64) : @FungibleToken.Vault {
198 let playerAddress = JoyrideAccounts.playerIDToPlayer[fromPlayerID]
199 let playerAccount = self.GetPlayerJRXAccount(playerID:fromPlayerID)
200
201 let developerStaking = JoyrideAccounts.gameToDeveloperAccount[inGameID]
202 if(developerStaking == nil) {
203 return <- profits
204 }
205 else {
206 let vaultCapability = getAccount(developerStaking!).getCapability<&{JoyrideMultiToken.Receiver}>(JoyrideMultiToken.UserPublicPath).borrow()
207 if(vaultCapability == nil) {
208 return <- profits
209 }
210 else {
211 let devShare <- profits.withdraw(amount: profits.balance * devPercentage)
212 vaultCapability!.depositToken(from: <- devShare)
213 return <- profits
214 }
215 }
216 }
217
218 pub fun EscrowWithdraw(playerID: String, amount:UFix64, tokenContext: String): @FungibleToken.Vault? {
219 let playerAccount = self.GetPlayerJRXAccount(playerID:playerID)
220 if(playerAccount == nil) {
221 return nil
222 }
223 return <- playerAccount!.WithdrawTokensForPayment(playerID: playerID, txID:"", amount:amount, tokenContext: tokenContext)
224 }
225
226 pub fun EscrowWithdrawWithTnxId(playerID: String, txID: String, amount:UFix64, tokenContext: String): @FungibleToken.Vault? {
227 let playerAccount = self.GetPlayerJRXAccount(playerID:playerID)
228 if(playerAccount == nil) {
229 return nil
230 }
231 return <- playerAccount!.WithdrawTokensForPayment(playerID: playerID, txID:txID, amount:amount, tokenContext: tokenContext)
232 }
233
234 pub fun PlayerDeposit(playerID: String, vault:@FungibleToken.Vault) : @FungibleToken.Vault? {
235 let playerAccount = self.GetPlayerJRXAccount(playerID:playerID)
236 if(playerAccount != nil) {
237 playerAccount!.DepositTokens(vault: <-vault)
238 return nil
239 } else {
240 return <- vault
241 }
242 }
243 }
244
245 pub struct CreateAccountTransactionData {
246 pub var playerID: String
247 pub var referralID: String
248
249 init(playerID: String, referralID: String) {
250 self.playerID = playerID
251 self.referralID = referralID
252 }
253 }
254}