github.com/lino-network/lino@v0.6.11/x/developer/manager/manager.go (about) 1 package developer 2 3 import ( 4 "fmt" 5 6 codec "github.com/cosmos/cosmos-sdk/codec" 7 sdk "github.com/cosmos/cosmos-sdk/types" 8 9 "github.com/lino-network/lino/param" 10 linotypes "github.com/lino-network/lino/types" 11 "github.com/lino-network/lino/utils" 12 "github.com/lino-network/lino/x/account" 13 "github.com/lino-network/lino/x/developer/model" 14 "github.com/lino-network/lino/x/developer/types" 15 "github.com/lino-network/lino/x/price" 16 "github.com/lino-network/lino/x/vote" 17 votetypes "github.com/lino-network/lino/x/vote/types" 18 ) 19 20 const ( 21 maxAffiliatedAccount = 500 22 23 exportVersion = 1 24 importVersion = 1 25 ) 26 27 type DeveloperManager struct { 28 storage model.DeveloperStorage 29 // deps 30 paramHolder param.ParamKeeper 31 vote vote.VoteKeeper 32 acc account.AccountKeeper 33 price price.PriceKeeper 34 } 35 36 // NewDeveloperManager - create new developer manager 37 func NewDeveloperManager(key sdk.StoreKey, holder param.ParamKeeper, vote vote.VoteKeeper, acc account.AccountKeeper, price price.PriceKeeper) DeveloperManager { 38 return DeveloperManager{ 39 storage: model.NewDeveloperStorage(key), 40 paramHolder: holder, 41 vote: vote, 42 acc: acc, 43 price: price, 44 } 45 } 46 47 // InitGenesis - init developer manager 48 func (dm DeveloperManager) InitGenesis(ctx sdk.Context, reservePoolAmount linotypes.Coin) sdk.Error { 49 if !reservePoolAmount.IsNotNegative() { 50 return types.ErrInvalidReserveAmount(reservePoolAmount) 51 } 52 dm.storage.SetReservePool(ctx, &model.ReservePool{ 53 Total: reservePoolAmount, 54 }) 55 return nil 56 } 57 58 // DoesDeveloperExist - check if given developer exists and not deleted before. 59 func (dm DeveloperManager) DoesDeveloperExist(ctx sdk.Context, username linotypes.AccountKey) bool { 60 dev, err := dm.storage.GetDeveloper(ctx, username) 61 if err != nil { 62 return false 63 } 64 return !dev.IsDeleted 65 } 66 67 func (dm DeveloperManager) GetDeveloper(ctx sdk.Context, username linotypes.AccountKey) (model.Developer, sdk.Error) { 68 dev, err := dm.storage.GetDeveloper(ctx, username) 69 if err != nil { 70 return model.Developer{}, err 71 } 72 return *dev, nil 73 } 74 75 // GetLiveDevelopers - returns all developers that are live(not deregistered). 76 func (dm DeveloperManager) GetLiveDevelopers(ctx sdk.Context) []model.Developer { 77 rst := make([]model.Developer, 0) 78 devs := dm.storage.GetAllDevelopers(ctx) 79 for _, dev := range devs { 80 if !dev.IsDeleted { 81 rst = append(rst, dev) 82 } 83 } 84 return rst 85 } 86 87 // RegisterDeveloper - register a developer. 88 // Stateful validation: 89 // 1. account exists. 90 // 2. has never been a developer. 91 // 3. VoteDuty is simply a voter, not a validator candidate. 92 // 4. not an affiliated account. 93 // 4. has minimum LS. 94 func (dm DeveloperManager) RegisterDeveloper(ctx sdk.Context, username linotypes.AccountKey, website, description, appMetaData string) sdk.Error { 95 if !dm.acc.DoesAccountExist(ctx, username) { 96 return types.ErrAccountNotFound() 97 } 98 if dm.storage.HasDeveloper(ctx, username) { 99 return types.ErrDeveloperAlreadyExist(username) 100 } 101 if duty, err := dm.vote.GetVoterDuty(ctx, username); err != nil || duty != votetypes.DutyVoter { 102 return types.ErrInvalidVoterDuty() 103 } 104 if dm.storage.HasUserRole(ctx, username) { 105 return types.ErrInvalidUserRole() 106 } 107 // check developer minimum LS requirement 108 param, err := dm.paramHolder.GetDeveloperParam(ctx) 109 if err != nil { 110 return err 111 } 112 staked, err := dm.vote.GetLinoStake(ctx, username) 113 if err != nil { 114 return err 115 } 116 if !staked.IsGTE(param.DeveloperMinDeposit) { 117 return types.ErrInsufficientDeveloperDeposit() 118 } 119 120 // assign duty in vote 121 err = dm.vote.AssignDuty(ctx, username, votetypes.DutyApp, param.DeveloperMinDeposit) 122 if err != nil { 123 return err 124 } 125 developer := &model.Developer{ 126 Username: username, 127 Website: website, 128 Description: description, 129 AppMetaData: appMetaData, 130 IsDeleted: false, 131 } 132 dm.storage.SetDeveloper(ctx, *developer) 133 return nil 134 } 135 136 // UpdateDeveloper - update developer. 137 // 1. developer must not be deleted. 138 func (dm DeveloperManager) UpdateDeveloper(ctx sdk.Context, username linotypes.AccountKey, website, description, appMetadata string) sdk.Error { 139 // Use DoesDeveloperExist to make sure we don't forget to check IsDeleted. 140 if !dm.DoesDeveloperExist(ctx, username) { 141 return types.ErrDeveloperNotFound() 142 } 143 developer, err := dm.GetDeveloper(ctx, username) 144 if err != nil { 145 return err 146 } 147 developer.Website = website 148 developer.Description = description 149 developer.AppMetaData = appMetadata 150 dm.storage.SetDeveloper(ctx, developer) 151 return nil 152 } 153 154 // UnregisterDeveloper - unregister a developer 155 // validation: 156 // 1. Developer exists. 157 // 2. No IDA issued or IDA revoked. 158 // TODO: 159 // remove all affiliated accounts. 160 // mark developer as deleted. 161 func (dm DeveloperManager) UnregisterDeveloper(ctx sdk.Context, username linotypes.AccountKey) sdk.Error { 162 return linotypes.ErrUnimplemented("UnregisterDeveloper") 163 } 164 165 // IssueIDA - Application issue IDA 166 func (dm DeveloperManager) IssueIDA(ctx sdk.Context, appname linotypes.AccountKey, idaName string, idaPrice int64) sdk.Error { 167 if !dm.DoesDeveloperExist(ctx, appname) { 168 return types.ErrDeveloperNotFound() 169 } 170 // not issued before 171 if dm.storage.HasIDA(ctx, appname) { 172 return types.ErrIDAIssuedBefore() 173 } 174 175 // internally we store MiniIDAPrice, which is 10^(-5) * IDAPrice in MiniDollar, then we have 176 // 1 IDA = IDAPrice * 10^(-3) Dollar 177 // 1 IDA = IDAPrice * 10^(7) MiniDollar 178 // 10^(5) MiniIDA = IDAPrice * 10^(7) MiniDollar 179 // 1 MiniIDA = IDAPrice * 10^(2) * MiniDollar 180 miniIDAPrice := linotypes.NewMiniDollarFromInt(sdk.NewInt(idaPrice).MulRaw(100)) 181 ida := model.AppIDA{ 182 App: appname, 183 Name: idaName, 184 MiniIDAPrice: miniIDAPrice, 185 IsRevoked: false, 186 RevokeCoinPrice: linotypes.NewMiniDollar(0), 187 } 188 dm.storage.SetIDA(ctx, ida) 189 dm.storage.SetIDAStats(ctx, appname, model.AppIDAStats{ 190 Total: linotypes.NewMiniDollar(0), 191 }) 192 return nil 193 } 194 195 func (dm DeveloperManager) IDAConvertFromLino(ctx sdk.Context, username, appname linotypes.AccountKey, amount linotypes.Coin) sdk.Error { 196 if _, err := dm.validAppIDA(ctx, appname); err != nil { 197 return err 198 } 199 if !dm.acc.DoesAccountExist(ctx, username) || !dm.acc.DoesAccountExist(ctx, appname) { 200 return types.ErrAccountNotFound() 201 } 202 203 err := dm.acc.MoveCoin(ctx, 204 linotypes.NewAccOrAddrFromAcc(username), 205 linotypes.NewAccOrAddrFromAcc(appname), 206 amount) 207 if err != nil { 208 return err 209 } 210 miniDollar, err := dm.exchangeMiniDollar(ctx, appname, amount) 211 if err != nil { 212 return err 213 } 214 215 bank := dm.storage.GetIDABank(ctx, appname, username) 216 bank.Balance = bank.Balance.Plus(miniDollar) 217 dm.storage.SetIDABank(ctx, appname, username, bank) 218 return nil 219 } 220 221 // MintIDA - mint some IDA by converting LINO to IDA (internally MiniDollar). 222 func (dm DeveloperManager) MintIDA(ctx sdk.Context, appname linotypes.AccountKey, amount linotypes.Coin) sdk.Error { 223 if _, err := dm.validAppIDA(ctx, appname); err != nil { 224 return err 225 } 226 bank := dm.storage.GetIDABank(ctx, appname, appname) 227 // ignored 228 // if bank.Unauthed { 229 // return types.ErrIDAUnauthed() 230 // } 231 miniDollar, err := dm.exchangeMiniDollar(ctx, appname, amount) 232 if err != nil { 233 return err 234 } 235 bank.Balance = bank.Balance.Plus(miniDollar) 236 dm.storage.SetIDABank(ctx, appname, appname, bank) 237 return nil 238 } 239 240 // exchangeMinidollar - exchange Minidollar with @p amount coin, return the exchanged minidollar. 241 // Return minidollar must be added to somewhere, otherwise it's burned. 242 // 1. move @p amount from account saving to reserve pool. 243 func (dm DeveloperManager) exchangeMiniDollar(ctx sdk.Context, appname linotypes.AccountKey, amount linotypes.Coin) (linotypes.MiniDollar, sdk.Error) { 244 bought, err := dm.price.CoinToMiniDollar(ctx, amount) 245 if err != nil { 246 return linotypes.NewMiniDollar(0), err 247 } 248 if !bought.IsPositive() { 249 return linotypes.NewMiniDollar(0), types.ErrExchangeMiniDollarZeroAmount() 250 } 251 // exchange 252 err = dm.acc.MoveToPool(ctx, 253 linotypes.DevIDAReservePool, linotypes.NewAccOrAddrFromAcc(appname), amount) 254 if err != nil { 255 return linotypes.NewMiniDollar(0), err 256 } 257 pool := dm.storage.GetReservePool(ctx) 258 pool.Total = pool.Total.Plus(amount) 259 pool.TotalMiniDollar = pool.TotalMiniDollar.Plus(bought) 260 dm.storage.SetReservePool(ctx, pool) 261 idaStats := dm.storage.GetIDAStats(ctx, appname) 262 idaStats.Total = idaStats.Total.Plus(bought) 263 dm.storage.SetIDAStats(ctx, appname, *idaStats) 264 return bought, nil 265 } 266 267 // AppTransferIDA - transfer IDA back or from app, by app. 268 func (dm DeveloperManager) AppTransferIDA(ctx sdk.Context, appname, signer linotypes.AccountKey, amount linotypes.MiniIDA, from, to linotypes.AccountKey) sdk.Error { 269 if !(from == appname || to == appname) { 270 return types.ErrInvalidTransferTarget() 271 } 272 ida, err := dm.validAppIDA(ctx, appname) 273 if err != nil { 274 return err 275 } 276 if !dm.acc.DoesAccountExist(ctx, from) || !dm.acc.DoesAccountExist(ctx, to) { 277 return types.ErrAccountNotFound() 278 } 279 // singer must be the affiliated of the app to be able to move IDAs. 280 signerApp, err := dm.GetAffiliatingApp(ctx, signer) 281 if err != nil || signerApp != appname { 282 return types.ErrInvalidSigner() 283 } 284 minidollar := linotypes.MiniIDAToMiniDollar(amount, ida.MiniIDAPrice) 285 return dm.appIDAMove(ctx, appname, from, to, minidollar) 286 } 287 288 // MoveIDA - app move ida, authorization check applied. 289 // 1. amount must > 0. 290 // 2. from's bank is not frozen. 291 func (dm DeveloperManager) MoveIDA(ctx sdk.Context, app, from, to linotypes.AccountKey, amount linotypes.MiniDollar) sdk.Error { 292 if _, err := dm.validAppIDA(ctx, app); err != nil { 293 return err 294 } 295 if !dm.acc.DoesAccountExist(ctx, from) || !dm.acc.DoesAccountExist(ctx, to) { 296 return types.ErrAccountNotFound() 297 } 298 return dm.appIDAMove(ctx, app, from, to, amount) 299 } 300 301 func (dm DeveloperManager) validAppIDA(ctx sdk.Context, app linotypes.AccountKey) (*model.AppIDA, sdk.Error) { 302 if !dm.DoesDeveloperExist(ctx, app) { 303 return nil, types.ErrDeveloperNotFound() 304 } 305 ida, err := dm.storage.GetIDA(ctx, app) 306 if err != nil { 307 return nil, err 308 } 309 if ida.IsRevoked { 310 return nil, types.ErrIDARevoked() 311 } 312 return ida, nil 313 } 314 315 // appIDAMove - authorization check applied. app is not checked. 316 func (dm DeveloperManager) appIDAMove(ctx sdk.Context, app, from, to linotypes.AccountKey, amount linotypes.MiniDollar) sdk.Error { 317 if !amount.IsPositive() { 318 return linotypes.ErrInvalidIDAAmount() 319 } 320 fromBank := dm.storage.GetIDABank(ctx, app, from) 321 toBank := dm.storage.GetIDABank(ctx, app, to) 322 if fromBank.Unauthed { 323 return types.ErrIDAUnauthed() 324 } 325 if fromBank.Balance.LT(amount) { 326 return types.ErrNotEnoughIDA() 327 } 328 fromBank.Balance = fromBank.Balance.Minus(amount) 329 toBank.Balance = toBank.Balance.Plus(amount) 330 dm.storage.SetIDABank(ctx, app, from, fromBank) 331 dm.storage.SetIDABank(ctx, app, to, toBank) 332 return nil 333 } 334 335 func (dm DeveloperManager) GetMiniIDAPrice(ctx sdk.Context, app linotypes.AccountKey) (linotypes.MiniDollar, sdk.Error) { 336 ida, err := dm.validAppIDA(ctx, app) 337 if err != nil { 338 return linotypes.NewMiniDollar(0), err 339 } 340 return ida.MiniIDAPrice, nil 341 } 342 343 // BurnIDA - Burn some @p amount of IDA on @p user's account, burned IDA will be converted to 344 // LINO and saved onto the user's account. Return the amount of coins 345 // removed from reserve pool. The coins can has been . NOTE: cannot burn @p amount so little 346 // that can only can only buy less than 1 coin, i.e. zero. 347 func (dm DeveloperManager) BurnIDA(ctx sdk.Context, app, user linotypes.AccountKey, amount linotypes.MiniDollar) (linotypes.Coin, sdk.Error) { 348 bank, err := dm.GetIDABank(ctx, app, user) 349 if err != nil { 350 return linotypes.NewCoinFromInt64(0), err 351 } 352 if bank.Unauthed { 353 return linotypes.NewCoinFromInt64(0), types.ErrIDAUnauthed() 354 } 355 if bank.Balance.LT(amount) { 356 return linotypes.NewCoinFromInt64(0), types.ErrNotEnoughIDA() 357 } 358 bought, used, err := dm.price.MiniDollarToCoin(ctx, amount) 359 if err != nil { 360 return linotypes.NewCoinFromInt64(0), err 361 } 362 if !bought.IsPositive() { 363 return linotypes.NewCoinFromInt64(0), types.ErrBurnZeroIDA() 364 } 365 366 // internal reserve pool check 367 pool := dm.storage.GetReservePool(ctx) 368 if !pool.Total.IsGTE(bought) { 369 return linotypes.NewCoinFromInt64(0), types.ErrInsuffientReservePool() 370 } 371 pool.Total = pool.Total.Minus(bought) 372 pool.TotalMiniDollar = pool.TotalMiniDollar.Minus(used) 373 dm.storage.SetReservePool(ctx, pool) 374 375 // ida stats update 376 idaStats := dm.storage.GetIDAStats(ctx, app) 377 idaStats.Total = idaStats.Total.Minus(used) 378 dm.storage.SetIDAStats(ctx, app, *idaStats) 379 380 // ida bank update 381 bank.Balance = bank.Balance.Minus(used) 382 dm.storage.SetIDABank(ctx, app, user, &bank) 383 384 // external 385 // after burn, move coins from the reserve pool to the user's account. 386 // only called upon donation, so the newly added coins will then be moved 387 // to the vote's frictions pool. 388 err = dm.acc.MoveFromPool(ctx, 389 linotypes.DevIDAReservePool, linotypes.NewAccOrAddrFromAcc(user), bought) 390 if err != nil { 391 return linotypes.NewCoinFromInt64(0), err 392 } 393 394 return bought, nil 395 } 396 397 func (dm DeveloperManager) GetIDA(ctx sdk.Context, app linotypes.AccountKey) (model.AppIDA, sdk.Error) { 398 ida, err := dm.validAppIDA(ctx, app) 399 if err != nil { 400 return model.AppIDA{}, err 401 } 402 return *ida, err 403 } 404 405 func (dm DeveloperManager) GetIDABank(ctx sdk.Context, app, user linotypes.AccountKey) (model.IDABank, sdk.Error) { 406 if !dm.DoesDeveloperExist(ctx, app) { 407 return model.IDABank{}, types.ErrDeveloperNotFound() 408 } 409 if !dm.acc.DoesAccountExist(ctx, user) { 410 return model.IDABank{}, types.ErrAccountNotFound() 411 } 412 return *dm.storage.GetIDABank(ctx, app, user), nil 413 } 414 415 // UpdateAffiliated - add or remove an affiliated account. 416 func (dm DeveloperManager) UpdateAffiliated(ctx sdk.Context, appname, username linotypes.AccountKey, activate bool) sdk.Error { 417 if !dm.DoesDeveloperExist(ctx, appname) { 418 return types.ErrDeveloperNotFound() 419 } 420 if !dm.acc.DoesAccountExist(ctx, username) { 421 return types.ErrAccountNotFound() 422 } 423 app, err := dm.storage.GetDeveloper(ctx, appname) 424 if err != nil { 425 return err 426 } 427 if app.NAffiliated >= maxAffiliatedAccount { 428 return types.ErrMaxAffiliatedExceeded() 429 } 430 if activate { 431 err := dm.addAffiliated(ctx, appname, username) 432 if err != nil { 433 return err 434 } 435 app.NAffiliated += 1 436 dm.storage.SetDeveloper(ctx, *app) 437 } else { 438 err := dm.removeAffiliated(ctx, appname, username) 439 if err != nil { 440 return err 441 } 442 app.NAffiliated -= 1 443 dm.storage.SetDeveloper(ctx, *app) 444 } 445 return nil 446 } 447 448 // To activate an affiliated account, check: 449 // 1. not affiliated to any developer. 450 // 2. not a developer. 451 // 3. not on any other duty. 452 func (dm DeveloperManager) addAffiliated(ctx sdk.Context, app, username linotypes.AccountKey) sdk.Error { 453 if dm.storage.HasUserRole(ctx, username) { 454 return types.ErrInvalidAffiliatedAccount("is affiliated already") 455 } 456 // TODO(@yumin): Do we check if username as developer is deleted already? 457 if dm.storage.HasDeveloper(ctx, username) { 458 return types.ErrInvalidAffiliatedAccount("is/was developer") 459 } 460 duty, err := dm.vote.GetVoterDuty(ctx, username) 461 if err == nil && duty != votetypes.DutyVoter { 462 return types.ErrInvalidAffiliatedAccount("on duty of something else") 463 } 464 dm.storage.SetAffiliatedAcc(ctx, app, username) 465 dm.storage.SetUserRole(ctx, username, &model.Role{ 466 AffiliatedApp: app, 467 }) 468 return nil 469 } 470 471 // To remove an affiliated account from app 472 // 1. user is the affiliated account of app. 473 func (dm DeveloperManager) removeAffiliated(ctx sdk.Context, app, username linotypes.AccountKey) sdk.Error { 474 role, err := dm.storage.GetUserRole(ctx, username) 475 if err != nil { 476 return err 477 } 478 if role.AffiliatedApp != app { 479 return types.ErrInvalidAffiliatedAccount("not affiliated account of provided app") 480 } 481 dm.storage.DelAffiliatedAcc(ctx, app, username) 482 dm.storage.DelUserRole(ctx, username) 483 return nil 484 } 485 486 // GetAffiliatingApp - get username's affiliating app, or username itself is an app. 487 func (dm DeveloperManager) GetAffiliatingApp(ctx sdk.Context, username linotypes.AccountKey) (linotypes.AccountKey, sdk.Error) { 488 // username is app itself. 489 if dm.DoesDeveloperExist(ctx, username) { 490 return username, nil 491 } 492 // user's role. 493 role, err := dm.storage.GetUserRole(ctx, username) 494 if err != nil { 495 return "", err 496 } 497 return role.AffiliatedApp, nil 498 } 499 500 // GetAffiliated returns all affiliated account of app. 501 func (dm DeveloperManager) GetAffiliated(ctx sdk.Context, app linotypes.AccountKey) []linotypes.AccountKey { 502 if !dm.DoesDeveloperExist(ctx, app) { 503 return nil 504 } 505 return dm.storage.GetAllAffiliatedAcc(ctx, app) 506 } 507 508 // UpdateAuthorization - update app's authorization on user. 509 func (dm DeveloperManager) UpdateIDAAuth(ctx sdk.Context, app, username linotypes.AccountKey, active bool) sdk.Error { 510 // when developer is revoked, no need to update auth 511 if !dm.DoesDeveloperExist(ctx, app) { 512 return types.ErrDeveloperNotFound() 513 } 514 if !dm.acc.DoesAccountExist(ctx, username) { 515 return types.ErrAccountNotFound() 516 } 517 if dm.storage.HasAffiliatedAcc(ctx, app, username) { 518 return types.ErrInvalidIDAAuth() 519 } 520 bank := dm.storage.GetIDABank(ctx, app, username) 521 if bank.Unauthed == !active { 522 return types.ErrInvalidIDAAuth() 523 } 524 bank.Unauthed = !active 525 dm.storage.SetIDABank(ctx, app, username, bank) 526 return nil 527 } 528 529 // ReportConsumption - add consumption to a developer. 530 func (dm DeveloperManager) ReportConsumption(ctx sdk.Context, app linotypes.AccountKey, consumption linotypes.MiniDollar) sdk.Error { 531 developer, err := dm.storage.GetDeveloper(ctx, app) 532 if err != nil { 533 return err 534 } 535 developer.AppConsumption = developer.AppConsumption.Plus(consumption) 536 dm.storage.SetDeveloper(ctx, *developer) 537 return nil 538 } 539 540 // DistributeDevInflation - distribute monthly app inflation. 541 func (dm DeveloperManager) MonthlyDistributeDevInflation(ctx sdk.Context) sdk.Error { 542 // No-op if there is no developer, leave inflations in pool. 543 devs := dm.GetLiveDevelopers(ctx) 544 if len(devs) == 0 { 545 return nil 546 } 547 inflation, err := dm.acc.GetPool(ctx, linotypes.InflationDeveloperPool) 548 if err != nil { 549 return err 550 } 551 distSchema := make([]sdk.Dec, len(devs)) 552 totalConsumption := linotypes.NewMiniDollar(0) 553 for _, dev := range devs { 554 totalConsumption = totalConsumption.Plus(dev.AppConsumption) 555 } 556 if totalConsumption.IsZero() { 557 // if not any consumption here, we evenly distribute all inflation 558 for i := range devs { 559 distSchema[i] = linotypes.NewDecFromRat(1, int64(len(devs))) 560 } 561 } else { 562 for i, dev := range devs { 563 distSchema[i] = dev.AppConsumption.ToDec().Quo(totalConsumption.ToDec()) 564 } 565 } 566 567 distributed := linotypes.NewCoinFromInt64(0) 568 for i, developer := range devs { 569 if i == (len(devs) - 1) { 570 if err := dm.acc.MoveFromPool(ctx, linotypes.InflationDeveloperPool, 571 linotypes.NewAccOrAddrFromAcc(developer.Username), 572 inflation.Minus(distributed)); err != nil { 573 return err 574 } 575 break 576 } 577 percentage := distSchema[i] 578 myShareRat := inflation.ToDec().Mul(percentage) 579 myShareCoin := linotypes.DecToCoin(myShareRat) 580 distributed = distributed.Plus(myShareCoin) 581 if err := dm.acc.MoveFromPool(ctx, linotypes.InflationDeveloperPool, 582 linotypes.NewAccOrAddrFromAcc(developer.Username), 583 myShareCoin); err != nil { 584 return err 585 } 586 } 587 588 dm.clearConsumption(ctx) 589 return nil 590 } 591 592 func (dm DeveloperManager) clearConsumption(ctx sdk.Context) { 593 devs := dm.GetLiveDevelopers(ctx) 594 for _, dev := range devs { 595 dev.AppConsumption = linotypes.NewMiniDollar(0) 596 dm.storage.SetDeveloper(ctx, dev) 597 } 598 } 599 600 func (dm DeveloperManager) GetReservePool(ctx sdk.Context) model.ReservePool { 601 return *dm.storage.GetReservePool(ctx) 602 } 603 604 func (dm DeveloperManager) GetIDAStats(ctx sdk.Context, app linotypes.AccountKey) (model.AppIDAStats, sdk.Error) { 605 if _, err := dm.validAppIDA(ctx, app); err != nil { 606 return model.AppIDAStats{}, err 607 } 608 stats := *dm.storage.GetIDAStats(ctx, app) 609 return stats, nil 610 } 611 612 func (dm DeveloperManager) ExportToFile(ctx sdk.Context, cdc *codec.Codec, filepath string) error { 613 state := &model.DeveloperTablesIR{ 614 Version: exportVersion, 615 } 616 stores := dm.storage.StoreMap(ctx) 617 618 // export developers 619 stores[string(model.DeveloperSubstore)].Iterate(func(key []byte, val interface{}) bool { 620 dev := val.(*model.Developer) 621 state.Developers = append(state.Developers, model.DeveloperIR{ 622 Username: dev.Username, 623 AppConsumption: dev.AppConsumption, 624 Website: dev.Website, 625 Description: dev.Description, 626 AppMetaData: dev.AppMetaData, 627 IsDeleted: dev.IsDeleted, 628 NAffiliated: dev.NAffiliated, 629 }) 630 return false 631 }) 632 633 // export IDAs 634 stores[string(model.IdaSubstore)].Iterate(func(key []byte, val interface{}) bool { 635 ida := val.(*model.AppIDA) 636 state.IDAs = append(state.IDAs, model.AppIDAIR(*ida)) 637 return false 638 }) 639 640 // export ida balance 641 stores[string(model.IdaBalanceSubstore)].Iterate(func(key []byte, val interface{}) bool { 642 app, user := model.ParseIDABalanceKey(key) 643 bank := val.(*model.IDABank) 644 state.IDABanks = append(state.IDABanks, model.IDABankIR{ 645 App: app, 646 User: user, 647 Balance: bank.Balance, 648 Unauthed: bank.Unauthed, 649 }) 650 return false 651 }) 652 653 // export reserve pool 654 stores[string(model.ReservePoolSubstore)].Iterate(func(key []byte, val interface{}) bool { 655 pool := val.(*model.ReservePool) 656 state.ReservePool = model.ReservePoolIR(*pool) 657 return false 658 }) 659 660 // export affiliated accounts 661 stores[string(model.AffiliatedAccSubstore)].Iterate(func(key []byte, _ interface{}) bool { 662 app, user := model.ParseAffiliatedAccKey(key) 663 state.AffiliatedAccs = append(state.AffiliatedAccs, model.AffiliatedAccIR{ 664 App: app, 665 User: user, 666 }) 667 return false 668 }) 669 670 // export UserRoles 671 stores[string(model.UserRoleSubstore)].Iterate(func(key []byte, val interface{}) bool { 672 role := val.(*model.Role) 673 state.UserRoles = append(state.UserRoles, model.UserRoleIR{ 674 User: linotypes.AccountKey(key), 675 AffiliatedApp: role.AffiliatedApp, 676 }) 677 return false 678 }) 679 680 // export IDA stats 681 stores[string(model.IdaStatsSubstore)].Iterate(func(key []byte, val interface{}) bool { 682 stats := val.(*model.AppIDAStats) 683 state.IDAStats = append(state.IDAStats, model.IDAStatsIR{ 684 App: linotypes.AccountKey(key), 685 Total: stats.Total, 686 }) 687 return false 688 }) 689 690 return utils.Save(filepath, cdc, state) 691 } 692 693 // Import from file 694 func (dm DeveloperManager) ImportFromFile(ctx sdk.Context, cdc *codec.Codec, filepath string) error { 695 rst, err := utils.Load(filepath, cdc, func() interface{} { return &model.DeveloperTablesIR{} }) 696 if err != nil { 697 return err 698 } 699 table := rst.(*model.DeveloperTablesIR) 700 701 if table.Version != importVersion { 702 return fmt.Errorf("unsupported import version: %d", table.Version) 703 } 704 705 // import developers 706 for _, dev := range table.Developers { 707 dm.storage.SetDeveloper(ctx, model.Developer{ 708 Username: dev.Username, 709 AppConsumption: dev.AppConsumption, 710 Website: dev.Website, 711 Description: dev.Description, 712 AppMetaData: dev.AppMetaData, 713 IsDeleted: dev.IsDeleted, 714 NAffiliated: dev.NAffiliated, 715 }) 716 } 717 718 // import IDAs 719 for _, ida := range table.IDAs { 720 dm.storage.SetIDA(ctx, model.AppIDA(ida)) 721 } 722 723 // import IDABanks 724 for _, bank := range table.IDABanks { 725 dm.storage.SetIDABank(ctx, bank.App, bank.User, &model.IDABank{ 726 Balance: bank.Balance, 727 Unauthed: bank.Unauthed, 728 }) 729 } 730 731 // import reserve pool 732 pool := model.ReservePool(table.ReservePool) 733 dm.storage.SetReservePool(ctx, &pool) 734 735 // import affiliated accounts 736 for _, acc := range table.AffiliatedAccs { 737 dm.storage.SetAffiliatedAcc(ctx, acc.App, acc.User) 738 } 739 740 // import user roles 741 for _, role := range table.UserRoles { 742 dm.storage.SetUserRole(ctx, role.User, &model.Role{ 743 AffiliatedApp: role.AffiliatedApp, 744 }) 745 } 746 747 // import ida stats 748 for _, stat := range table.IDAStats { 749 dm.storage.SetIDAStats(ctx, stat.App, model.AppIDAStats{ 750 Total: stat.Total, 751 }) 752 } 753 754 return nil 755 }