Diff Contract: botcontroller

(6972d74 vs. a34da91)
  • const UTILITY_TOKEN_SYMBOL="ENG",BASE_SYMBOL="STEEMP",BASE_SYMBOL_PRECISION=8,CHAIN_TYPE="STEEM";actions.updateParams=async payload=>{if(api.sender!==api.owner)return;const{basicFee:basicFee,basicSettingsFee:basicSettingsFee,premiumFee:premiumFee,premiumBaseStake:premiumBaseStake,stakePerMarket:stakePerMarket,basicDurationBlocks:basicDurationBlocks,basicCooldownBlocks:basicCooldownBlocks,basicMinTickIntervalBlocks:basicMinTickIntervalBlocks,premiumMinTickIntervalBlocks:premiumMinTickIntervalBlocks,basicMaxTicksPerBlock:basicMaxTicksPerBlock,premiumMaxTicksPerBlock:premiumMaxTicksPerBlock}=payload,params=await api.db.findOne("params",{});basicFee&&"string"==typeof basicFee&&!api.BigNumber(basicFee).isNaN()&&api.BigNumber(basicFee).gte(0)&&(params.basicFee=basicFee),basicSettingsFee&&"string"==typeof basicSettingsFee&&!api.BigNumber(basicSettingsFee).isNaN()&&api.BigNumber(basicSettingsFee).gte(0)&&(params.basicSettingsFee=basicSettingsFee),premiumFee&&"string"==typeof premiumFee&&!api.BigNumber(premiumFee).isNaN()&&api.BigNumber(premiumFee).gte(0)&&(params.premiumFee=premiumFee),premiumBaseStake&&"string"==typeof premiumBaseStake&&!api.BigNumber(premiumBaseStake).isNaN()&&api.BigNumber(premiumBaseStake).gte(0)&&(params.premiumBaseStake=premiumBaseStake),stakePerMarket&&"string"==typeof stakePerMarket&&!api.BigNumber(stakePerMarket).isNaN()&&api.BigNumber(stakePerMarket).gte(0)&&(params.stakePerMarket=stakePerMarket),basicDurationBlocks&&"number"==typeof basicDurationBlocks&&Number.isInteger(basicDurationBlocks)&&basicDurationBlocks>=0&&(params.basicDurationBlocks=basicDurationBlocks),basicCooldownBlocks&&"number"==typeof basicCooldownBlocks&&Number.isInteger(basicCooldownBlocks)&&basicCooldownBlocks>=0&&(params.basicCooldownBlocks=basicCooldownBlocks),basicMinTickIntervalBlocks&&"number"==typeof basicMinTickIntervalBlocks&&Number.isInteger(basicMinTickIntervalBlocks)&&basicMinTickIntervalBlocks>=0&&(params.basicMinTickIntervalBlocks=basicMinTickIntervalBlocks),premiumMinTickIntervalBlocks&&"number"==typeof premiumMinTickIntervalBlocks&&Number.isInteger(premiumMinTickIntervalBlocks)&&premiumMinTickIntervalBlocks>=0&&(params.premiumMinTickIntervalBlocks=premiumMinTickIntervalBlocks),basicMaxTicksPerBlock&&"number"==typeof basicMaxTicksPerBlock&&Number.isInteger(basicMaxTicksPerBlock)&&basicMaxTicksPerBlock>=0&&(params.basicMaxTicksPerBlock=basicMaxTicksPerBlock),premiumMaxTicksPerBlock&&"number"==typeof premiumMaxTicksPerBlock&&Number.isInteger(premiumMaxTicksPerBlock)&&premiumMaxTicksPerBlock>=0&&(params.premiumMaxTicksPerBlock=premiumMaxTicksPerBlock),await api.db.update("params",params)};const getCurrentTimestamp=()=>{const blockTimestamp=api.steemBlockTimestamp;return new Date(blockTimestamp+".000Z").getTime()},upgradeDataSchema=async()=>{const params=await api.db.findOne("params",{});let usersToCheck=await api.db.find("users",{timeLimitBlocks:{$exists:!0}}),nbUsers=usersToCheck.length;for(;nbUsers>0;){for(let index=0;index<nbUsers;index+=1){const user=usersToCheck[index];user.lastTickTimestamp=getCurrentTimestamp(),user.lastTickBlock=api.blockNumber,user.timeLimit=3*params.basicDurationBlocks*1e3,delete user.timeLimitBlocks,await api.db.update("users",user,{timeLimitBlocks:""})}usersToCheck=await api.db.find("users",{timeLimitBlocks:{$exists:!0}}),nbUsers=usersToCheck.length}},isTokenTransferVerified=(result,from,to,symbol,quantity,eventStr)=>!(void 0!==result.errors||!result.events||void 0===result.events.find(el=>"tokens"===el.contract&&el.event===eventStr&&el.data.from===from&&el.data.to===to&&el.data.quantity===quantity&&el.data.symbol===symbol)),countDecimals=value=>api.BigNumber(value).dp(),verifyUtilityTokenStake=async(amount,account)=>{if(api.BigNumber(amount).lte(0))return!0;const utilityTokenStake=await api.db.findOneInTable("tokens","balances",{account:account,symbol:"ENG"});return!(!utilityTokenStake||!api.BigNumber(utilityTokenStake.stake).gte(amount))},verifyUtilityTokenBalance=async(amount,account)=>{if(api.BigNumber(amount).lte(0))return!0;const utilityTokenBalance=await api.db.findOneInTable("tokens","balances",{account:account,symbol:"ENG"});return!(!utilityTokenBalance||!api.BigNumber(utilityTokenBalance.balance).gte(amount))},burnFee=async(amount,isSignedWithActiveKey)=>{if(api.BigNumber(amount).gt(0)){const res=await api.executeSmartContract("tokens","transfer",{to:"null",symbol:"ENG",quantity:amount,isSignedWithActiveKey:isSignedWithActiveKey});if(result=res,from=api.sender,to="null",symbol="ENG",quantity=amount,eventStr="transfer",void 0!==result.errors||!result.events||void 0===result.events.find(el=>"tokens"===el.contract&&el.event===eventStr&&el.data.from===from&&el.data.to===to&&el.data.quantity===quantity&&el.data.symbol===symbol))return!1}var result,from,to,symbol,quantity,eventStr;return!0};actions.createSSC=async()=>{if(!1===await api.db.tableExists("users")){await api.db.createTable("users",["account","lastTickBlock"]),await api.db.createTable("markets",["account","symbol"]),await api.db.createTable("params");const params={basicFee:"100",basicSettingsFee:"1",premiumFee:"100",premiumBaseStake:"1000",stakePerMarket:"200",basicDurationBlocks:403200,basicCooldownBlocks:403200,basicMinTickIntervalBlocks:200,premiumMinTickIntervalBlocks:100,basicMaxTicksPerBlock:20,premiumMaxTicksPerBlock:30};await api.db.insert("params",params)}else await upgradeDataSchema()};const tickUsers=async(params,users,currentTimestamp)=>{const marketList=[];for(let i=0;i<users.length;i+=1){const user=users[i];let userBalance=null;if(user.isPremium){userBalance=await api.db.findOneInTable("tokens","balances",{account:user.account,symbol:"ENG"});userBalance&&api.BigNumber(userBalance.stake).gte(params.premiumBaseStake)||(user.isPremium=!1)}else{const tickInterval=currentTimestamp-user.lastTickTimestamp;user.timeLimit-=tickInterval,user.timeLimit<=0&&(user.timeLimit=0,user.isOnCooldown=!0,user.isEnabled=!1)}if(user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,!user.isEnabled||user.enabledMarkets<1){await api.db.update("users",user);continue}const authorizedAction=user.isPremium||1===user.markets;let hasEnoughStakeForMarkets=!1;if(authorizedAction){userBalance||(userBalance=await api.db.findOneInTable("tokens","balances",{account:user.account,symbol:"ENG"}));let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake)),hasEnoughStakeForMarkets=userBalance&&api.BigNumber(userBalance.stake).gte(requiredStake)}const markets=await api.db.find("markets",{account:user.account,isEnabled:!0},user.markets,0,[{index:"symbol",descending:!1},{index:"_id",descending:!1}]);if(authorizedAction&&hasEnoughStakeForMarkets)await api.db.update("users",user),markets.forEach(m=>marketList.push(m));else{for(let j=0;j<markets.length;j+=1){const market=markets[j];market.isEnabled=!1,await api.db.update("markets",market)}user.enabledMarkets=0,await api.db.update("users",user)}}marketList.length>0&&await api.executeSmartContract("marketmaker","tick",{markets:marketList})};actions.tick=async()=>{if(api.assert("null"===api.sender,"not authorized")){const params=await api.db.findOne("params",{}),currentTimestamp=getCurrentTimestamp(),cutoffBasic=currentTimestamp-3*params.basicMinTickIntervalBlocks*1e3,cutoffPremium=currentTimestamp-3*params.premiumMinTickIntervalBlocks*1e3,pendingBasicTicks=await api.db.find("users",{isEnabled:!0,isPremium:!1,lastTickTimestamp:{$lte:cutoffBasic}},params.basicMaxTicksPerBlock,0,[{index:"lastTickBlock",descending:!1},{index:"_id",descending:!1}]);await tickUsers(params,pendingBasicTicks,currentTimestamp);const pendingPremiumTicks=await api.db.find("users",{isEnabled:!0,isPremium:!0,lastTickTimestamp:{$lte:cutoffPremium}},params.premiumMaxTicksPerBlock,0,[{index:"lastTickBlock",descending:!1},{index:"_id",descending:!1}]);await tickUsers(params,pendingPremiumTicks,currentTimestamp)}},actions.upgrade=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{}),hasEnoughStake=await verifyUtilityTokenStake(params.premiumBaseStake,api.sender);if(api.assert(hasEnoughStake,"you do not have enough tokens staked")&&api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")&&api.assert(!user.isPremium,"user is already premium")){if(!user.isPremiumFeePaid){const authorizedUpgrade=await verifyUtilityTokenBalance(params.premiumFee,api.sender);if(!api.assert(authorizedUpgrade,"you must have enough tokens to cover the premium upgrade fee"))return!1;if(!await burnFee(params.premiumFee,isSignedWithActiveKey))return!1}return user.isPremiumFeePaid=!0,user.isPremium=!0,user.isOnCooldown&&(user.timeLimit=3*params.basicDurationBlocks*1e3),user.isOnCooldown=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=getCurrentTimestamp(),await api.db.update("users",user),api.emit("upgrade",{account:api.sender}),!0}}return!1},actions.turnOff=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")&&api.assert(user.isEnabled,"account already turned off")){const currentTimestamp=getCurrentTimestamp();if(!user.isPremium){const tickInterval=currentTimestamp-user.lastTickTimestamp;user.timeLimit-=tickInterval,user.timeLimit<=0&&(user.timeLimit=0,user.isOnCooldown=!0)}user.isEnabled=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,await api.db.update("users",user),api.emit("turnOff",{account:api.sender})}}},actions.turnOn=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{});if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const currentTimestamp=getCurrentTimestamp(),tickInterval=currentTimestamp-user.lastTickTimestamp;api.assert(!user.isEnabled,"account already turned on")&&api.assert(user.isPremium||!user.isOnCooldown||user.isOnCooldown&&tickInterval>=3*params.basicCooldownBlocks*1e3,"cooldown duration not expired")&&(user.isEnabled=!0,user.isOnCooldown&&(user.timeLimit=3*params.basicDurationBlocks*1e3),user.isOnCooldown=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,await api.db.update("users",user),api.emit("turnOn",{account:api.sender}))}}},actions.disableMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});api.assert(null!==market,"market must exist")&&market.isEnabled&&(market.isEnabled=!1,await api.db.update("markets",market),user.enabledMarkets-=1,await api.db.update("users",user),api.emit("disableMarket",{account:api.sender,symbol:symbol}))}}},actions.enableMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null!==market,"market must exist")&&!market.isEnabled){const authorizedAction=user.isPremium||1===user.markets;if(api.assert(authorizedAction,"user has too many markets; premium upgrade required")){const params=await api.db.findOne("params",{});let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake));const hasEnoughStake=await verifyUtilityTokenStake(requiredStake,api.sender);api.assert(hasEnoughStake,"must stake more ENG to enable market")&&(market.isEnabled=!0,await api.db.update("markets",market),user.enabledMarkets+=1,await api.db.update("users",user),api.emit("enableMarket",{account:api.sender,symbol:symbol}))}}}}},actions.removeMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});api.assert(null!==market,"market must exist")&&(user.markets-=1,market.isEnabled&&(user.enabledMarkets-=1),await api.db.update("users",user),await api.db.remove("markets",market),api.emit("removeMarket",{account:api.sender,symbol:symbol}))}}};const updateMarketInternal=async(payload,market,shouldPayFee,params)=>{const{maxBidPrice:maxBidPrice,minSellPrice:minSellPrice,maxBaseToSpend:maxBaseToSpend,minBaseToSpend:minBaseToSpend,maxTokensToSell:maxTokensToSell,minTokensToSell:minTokensToSell,priceIncrement:priceIncrement,minSpread:minSpread}=payload;if(void 0===maxBidPrice&&void 0===minSellPrice&&void 0===maxBaseToSpend&&void 0===minBaseToSpend&&void 0===maxTokensToSell&&void 0===minTokensToSell&&void 0===priceIncrement&&void 0===minSpread)return!1;if(api.assert(void 0===maxBidPrice||maxBidPrice&&"string"==typeof maxBidPrice&&!api.BigNumber(maxBidPrice).isNaN()&&api.BigNumber(maxBidPrice).gt(0)&&countDecimals(maxBidPrice)<=8,"invalid maxBidPrice")&&api.assert(void 0===minSellPrice||minSellPrice&&"string"==typeof minSellPrice&&!api.BigNumber(minSellPrice).isNaN()&&api.BigNumber(minSellPrice).gt(0)&&countDecimals(minSellPrice)<=8,"invalid minSellPrice")&&api.assert(void 0===maxBaseToSpend||maxBaseToSpend&&"string"==typeof maxBaseToSpend&&!api.BigNumber(maxBaseToSpend).isNaN()&&api.BigNumber(maxBaseToSpend).gt(0)&&countDecimals(maxBaseToSpend)<=8,"invalid maxBaseToSpend")&&api.assert(void 0===minBaseToSpend||minBaseToSpend&&"string"==typeof minBaseToSpend&&!api.BigNumber(minBaseToSpend).isNaN()&&api.BigNumber(minBaseToSpend).gt(0)&&countDecimals(minBaseToSpend)<=8,"invalid minBaseToSpend")&&api.assert(void 0===maxTokensToSell||maxTokensToSell&&"string"==typeof maxTokensToSell&&!api.BigNumber(maxTokensToSell).isNaN()&&api.BigNumber(maxTokensToSell).gt(0)&&countDecimals(maxTokensToSell)<=market.precision,"invalid maxTokensToSell")&&api.assert(void 0===minTokensToSell||minTokensToSell&&"string"==typeof minTokensToSell&&!api.BigNumber(minTokensToSell).isNaN()&&api.BigNumber(minTokensToSell).gt(0)&&countDecimals(minTokensToSell)<=market.precision,"invalid minTokensToSell")&&api.assert(void 0===priceIncrement||priceIncrement&&"string"==typeof priceIncrement&&!api.BigNumber(priceIncrement).isNaN()&&api.BigNumber(priceIncrement).gt(0)&&countDecimals(priceIncrement)<=8,"invalid priceIncrement")&&api.assert(void 0===minSpread||minSpread&&"string"==typeof minSpread&&!api.BigNumber(minSpread).isNaN()&&api.BigNumber(minSpread).gt(0)&&countDecimals(minSpread)<=8,"invalid minSpread")){if(shouldPayFee&&!await burnFee(params.basicSettingsFee,!0))return!1;const update={account:market.account,symbol:market.symbol};return maxBidPrice&&(update.oldMaxBidPrice=market.maxBidPrice,market.maxBidPrice=maxBidPrice,update.newMaxBidPrice=maxBidPrice),minSellPrice&&(update.oldMinSellPrice=market.minSellPrice,market.minSellPrice=minSellPrice,update.newMinSellPrice=minSellPrice),maxBaseToSpend&&(update.oldMaxBaseToSpend=market.maxBaseToSpend,market.maxBaseToSpend=maxBaseToSpend,update.newMaxBaseToSpend=maxBaseToSpend),minBaseToSpend&&(update.oldMinBaseToSpend=market.minBaseToSpend,market.minBaseToSpend=minBaseToSpend,update.newMinBaseToSpend=minBaseToSpend),maxTokensToSell&&(update.oldMaxTokensToSell=market.maxTokensToSell,market.maxTokensToSell=maxTokensToSell,update.newMaxTokensToSell=maxTokensToSell),minTokensToSell&&(update.oldMinTokensToSell=market.minTokensToSell,market.minTokensToSell=minTokensToSell,update.newMinTokensToSell=minTokensToSell),priceIncrement&&(update.oldPriceIncrement=market.priceIncrement,market.priceIncrement=priceIncrement,update.newPriceIncrement=priceIncrement),minSpread&&(update.oldMinSpread=market.minSpread,market.minSpread=minSpread,update.newMinSpread=minSpread),await api.db.update("markets",market),api.emit("updateMarket",update),!0}return!1};actions.updateMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const params=await api.db.findOne("params",{});let authorizedAction=!1;if(authorizedAction=!!user.isPremium||await verifyUtilityTokenBalance(params.basicSettingsFee,api.sender),api.assert(authorizedAction,"you must have enough tokens to cover the settings change fee")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null!==market,"market must exist")){return await updateMarketInternal(payload,market,!user.isPremium,params)}}}}return!1},actions.addMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(null!==token,"symbol must exist")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null===market,"market already added")){const authorizedAddition=user.isPremium||0===user.markets;if(api.assert(authorizedAddition,"not allowed to add another market")){const params=await api.db.findOne("params",{});let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets+1);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake));const hasEnoughStake=await verifyUtilityTokenStake(requiredStake,api.sender);if(api.assert(hasEnoughStake,"must stake more ENG to add a market")){const newMarket={account:api.sender,symbol:symbol,precision:token.precision,strategy:1,maxBidPrice:"1000",minSellPrice:"0.00000001",maxBaseToSpend:"100",minBaseToSpend:"1",maxTokensToSell:"100",minTokensToSell:"1",priceIncrement:"0.00001",minSpread:"0.00000001",isEnabled:!0,creationTimestamp:getCurrentTimestamp(),creationBlock:api.blockNumber},addedMarket=await api.db.insert("markets",newMarket);api.emit("addMarket",{account:api.sender,symbol:symbol}),user.markets+=1,user.enabledMarkets+=1,await api.db.update("users",user),await updateMarketInternal(payload,addedMarket,!1,params)}}}}}}},actions.register=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{}),authorizedRegistration=await verifyUtilityTokenBalance(params.basicFee,api.sender);if(api.assert(authorizedRegistration,"you must have enough tokens to cover the registration fee")&&api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null===user,"user already registered")){if(!await burnFee(params.basicFee,isSignedWithActiveKey))return!1;const creationTimestamp=getCurrentTimestamp(),newUser={account:api.sender,isPremium:!1,isPremiumFeePaid:!1,isOnCooldown:!1,isEnabled:!0,markets:0,enabledMarkets:0,timeLimit:3*params.basicDurationBlocks*1e3,lastTickTimestamp:creationTimestamp,lastTickBlock:api.blockNumber,creationTimestamp:creationTimestamp,creationBlock:api.blockNumber};return await api.db.insert("users",newUser),api.emit("register",{account:api.sender}),!0}}return!1};
  • const UTILITY_TOKEN_SYMBOL="ENG",BASE_SYMBOL="STEEMP",BASE_SYMBOL_PRECISION=8,CHAIN_TYPE="STEEM";actions.updateParams=async payload=>{if(api.sender!==api.owner)return;const{basicFee:basicFee,basicSettingsFee:basicSettingsFee,premiumFee:premiumFee,premiumBaseStake:premiumBaseStake,stakePerMarket:stakePerMarket,basicDurationBlocks:basicDurationBlocks,basicCooldownBlocks:basicCooldownBlocks,basicMinTickIntervalBlocks:basicMinTickIntervalBlocks,premiumMinTickIntervalBlocks:premiumMinTickIntervalBlocks,basicMaxTicksPerBlock:basicMaxTicksPerBlock,premiumMaxTicksPerBlock:premiumMaxTicksPerBlock}=payload,params=await api.db.findOne("params",{});basicFee&&"string"==typeof basicFee&&!api.BigNumber(basicFee).isNaN()&&api.BigNumber(basicFee).gte(0)&&(params.basicFee=basicFee),basicSettingsFee&&"string"==typeof basicSettingsFee&&!api.BigNumber(basicSettingsFee).isNaN()&&api.BigNumber(basicSettingsFee).gte(0)&&(params.basicSettingsFee=basicSettingsFee),premiumFee&&"string"==typeof premiumFee&&!api.BigNumber(premiumFee).isNaN()&&api.BigNumber(premiumFee).gte(0)&&(params.premiumFee=premiumFee),premiumBaseStake&&"string"==typeof premiumBaseStake&&!api.BigNumber(premiumBaseStake).isNaN()&&api.BigNumber(premiumBaseStake).gte(0)&&(params.premiumBaseStake=premiumBaseStake),stakePerMarket&&"string"==typeof stakePerMarket&&!api.BigNumber(stakePerMarket).isNaN()&&api.BigNumber(stakePerMarket).gte(0)&&(params.stakePerMarket=stakePerMarket),basicDurationBlocks&&"number"==typeof basicDurationBlocks&&Number.isInteger(basicDurationBlocks)&&basicDurationBlocks>=0&&(params.basicDurationBlocks=basicDurationBlocks),basicCooldownBlocks&&"number"==typeof basicCooldownBlocks&&Number.isInteger(basicCooldownBlocks)&&basicCooldownBlocks>=0&&(params.basicCooldownBlocks=basicCooldownBlocks),basicMinTickIntervalBlocks&&"number"==typeof basicMinTickIntervalBlocks&&Number.isInteger(basicMinTickIntervalBlocks)&&basicMinTickIntervalBlocks>=0&&(params.basicMinTickIntervalBlocks=basicMinTickIntervalBlocks),premiumMinTickIntervalBlocks&&"number"==typeof premiumMinTickIntervalBlocks&&Number.isInteger(premiumMinTickIntervalBlocks)&&premiumMinTickIntervalBlocks>=0&&(params.premiumMinTickIntervalBlocks=premiumMinTickIntervalBlocks),basicMaxTicksPerBlock&&"number"==typeof basicMaxTicksPerBlock&&Number.isInteger(basicMaxTicksPerBlock)&&basicMaxTicksPerBlock>=0&&(params.basicMaxTicksPerBlock=basicMaxTicksPerBlock),premiumMaxTicksPerBlock&&"number"==typeof premiumMaxTicksPerBlock&&Number.isInteger(premiumMaxTicksPerBlock)&&premiumMaxTicksPerBlock>=0&&(params.premiumMaxTicksPerBlock=premiumMaxTicksPerBlock),await api.db.update("params",params)};const getCurrentTimestamp=()=>{const blockTimestamp=api.steemBlockTimestamp;return new Date(blockTimestamp+".000Z").getTime()},upgradeUserSchema=async()=>{const params=await api.db.findOne("params",{});let usersToCheck=await api.db.find("users",{timeLimitBlocks:{$exists:!0}}),nbUsers=usersToCheck.length;for(;nbUsers>0;){for(let index=0;index<nbUsers;index+=1){const user=usersToCheck[index];user.lastTickTimestamp=getCurrentTimestamp(),user.lastTickBlock=api.blockNumber,user.timeLimit=3*params.basicDurationBlocks*1e3,delete user.timeLimitBlocks,await api.db.update("users",user,{timeLimitBlocks:""})}usersToCheck=await api.db.find("users",{timeLimitBlocks:{$exists:!0}}),nbUsers=usersToCheck.length}},upgradeMarketSchema=async()=>{let mktsToCheck=await api.db.find("markets",{maxDistFromNext:{$exists:!1}}),nbMkts=mktsToCheck.length;for(;nbMkts>0;){for(let index=0;index<nbMkts;index+=1){const market=mktsToCheck[index];market.maxDistFromNext="0.0001",await api.db.update("markets",market)}mktsToCheck=await api.db.find("markets",{maxDistFromNext:{$exists:!1}}),nbMkts=mktsToCheck.length}},isTokenTransferVerified=(result,from,to,symbol,quantity,eventStr)=>!(void 0!==result.errors||!result.events||void 0===result.events.find(el=>"tokens"===el.contract&&el.event===eventStr&&el.data.from===from&&el.data.to===to&&el.data.quantity===quantity&&el.data.symbol===symbol)),countDecimals=value=>api.BigNumber(value).dp(),verifyUtilityTokenStake=async(amount,account)=>{if(api.BigNumber(amount).lte(0))return!0;const utilityTokenStake=await api.db.findOneInTable("tokens","balances",{account:account,symbol:"ENG"});return!(!utilityTokenStake||!api.BigNumber(utilityTokenStake.stake).gte(amount))},verifyUtilityTokenBalance=async(amount,account)=>{if(api.BigNumber(amount).lte(0))return!0;const utilityTokenBalance=await api.db.findOneInTable("tokens","balances",{account:account,symbol:"ENG"});return!(!utilityTokenBalance||!api.BigNumber(utilityTokenBalance.balance).gte(amount))},burnFee=async(amount,isSignedWithActiveKey)=>{if(api.BigNumber(amount).gt(0)){const res=await api.executeSmartContract("tokens","transfer",{to:"null",symbol:"ENG",quantity:amount,isSignedWithActiveKey:isSignedWithActiveKey});if(result=res,from=api.sender,to="null",symbol="ENG",quantity=amount,eventStr="transfer",void 0!==result.errors||!result.events||void 0===result.events.find(el=>"tokens"===el.contract&&el.event===eventStr&&el.data.from===from&&el.data.to===to&&el.data.quantity===quantity&&el.data.symbol===symbol))return!1}var result,from,to,symbol,quantity,eventStr;return!0};actions.createSSC=async()=>{if(!1===await api.db.tableExists("users")){await api.db.createTable("users",["account","lastTickBlock"]),await api.db.createTable("markets",["account","symbol"]),await api.db.createTable("params");const params={basicFee:"100",basicSettingsFee:"1",premiumFee:"100",premiumBaseStake:"1000",stakePerMarket:"200",basicDurationBlocks:403200,basicCooldownBlocks:403200,basicMinTickIntervalBlocks:200,premiumMinTickIntervalBlocks:100,basicMaxTicksPerBlock:20,premiumMaxTicksPerBlock:30};await api.db.insert("params",params)}else await upgradeUserSchema(),await upgradeMarketSchema()};const tickUsers=async(params,users,currentTimestamp)=>{const marketList=[];for(let i=0;i<users.length;i+=1){const user=users[i];let userBalance=null;if(user.isPremium){userBalance=await api.db.findOneInTable("tokens","balances",{account:user.account,symbol:"ENG"});userBalance&&api.BigNumber(userBalance.stake).gte(params.premiumBaseStake)||(user.isPremium=!1)}else{const tickInterval=currentTimestamp-user.lastTickTimestamp;user.timeLimit-=tickInterval,user.timeLimit<=0&&(user.timeLimit=0,user.isOnCooldown=!0,user.isEnabled=!1)}if(user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,!user.isEnabled||user.enabledMarkets<1){await api.db.update("users",user);continue}const authorizedAction=user.isPremium||1===user.markets;let hasEnoughStakeForMarkets=!1;if(authorizedAction){userBalance||(userBalance=await api.db.findOneInTable("tokens","balances",{account:user.account,symbol:"ENG"}));let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake)),hasEnoughStakeForMarkets=userBalance&&api.BigNumber(userBalance.stake).gte(requiredStake)}const markets=await api.db.find("markets",{account:user.account,isEnabled:!0},user.markets,0,[{index:"symbol",descending:!1},{index:"_id",descending:!1}]);if(authorizedAction&&hasEnoughStakeForMarkets)await api.db.update("users",user),markets.forEach(m=>marketList.push(m));else{for(let j=0;j<markets.length;j+=1){const market=markets[j];market.isEnabled=!1,await api.db.update("markets",market)}user.enabledMarkets=0,await api.db.update("users",user)}}marketList.length>0&&await api.executeSmartContract("marketmaker","tick",{markets:marketList,txIdBase:`${api.blockNumber}-${api.transactionId}`})};actions.tick=async()=>{if(api.assert("null"===api.sender,"not authorized")){const params=await api.db.findOne("params",{}),currentTimestamp=getCurrentTimestamp(),cutoffBasic=currentTimestamp-3*params.basicMinTickIntervalBlocks*1e3,cutoffPremium=currentTimestamp-3*params.premiumMinTickIntervalBlocks*1e3,pendingBasicTicks=await api.db.find("users",{isEnabled:!0,isPremium:!1,lastTickTimestamp:{$lte:cutoffBasic}},params.basicMaxTicksPerBlock,0,[{index:"lastTickBlock",descending:!1},{index:"_id",descending:!1}]);await tickUsers(params,pendingBasicTicks,currentTimestamp);const pendingPremiumTicks=await api.db.find("users",{isEnabled:!0,isPremium:!0,lastTickTimestamp:{$lte:cutoffPremium}},params.premiumMaxTicksPerBlock,0,[{index:"lastTickBlock",descending:!1},{index:"_id",descending:!1}]);await tickUsers(params,pendingPremiumTicks,currentTimestamp)}},actions.upgrade=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{}),hasEnoughStake=await verifyUtilityTokenStake(params.premiumBaseStake,api.sender);if(api.assert(hasEnoughStake,"you do not have enough tokens staked")&&api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")&&api.assert(!user.isPremium,"user is already premium")){if(!user.isPremiumFeePaid){const authorizedUpgrade=await verifyUtilityTokenBalance(params.premiumFee,api.sender);if(!api.assert(authorizedUpgrade,"you must have enough tokens to cover the premium upgrade fee"))return!1;if(!await burnFee(params.premiumFee,isSignedWithActiveKey))return!1}return user.isPremiumFeePaid=!0,user.isPremium=!0,user.isOnCooldown&&(user.timeLimit=3*params.basicDurationBlocks*1e3),user.isOnCooldown=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=getCurrentTimestamp(),await api.db.update("users",user),api.emit("upgrade",{account:api.sender}),!0}}return!1},actions.turnOff=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")&&api.assert(user.isEnabled,"account already turned off")){const currentTimestamp=getCurrentTimestamp();if(!user.isPremium){const tickInterval=currentTimestamp-user.lastTickTimestamp;user.timeLimit-=tickInterval,user.timeLimit<=0&&(user.timeLimit=0,user.isOnCooldown=!0)}user.isEnabled=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,await api.db.update("users",user),api.emit("turnOff",{account:api.sender})}}},actions.turnOn=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{});if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const currentTimestamp=getCurrentTimestamp(),tickInterval=currentTimestamp-user.lastTickTimestamp;api.assert(!user.isEnabled,"account already turned on")&&api.assert(user.isPremium||!user.isOnCooldown||user.isOnCooldown&&tickInterval>=3*params.basicCooldownBlocks*1e3,"cooldown duration not expired")&&(user.isEnabled=!0,user.isOnCooldown&&(user.timeLimit=3*params.basicDurationBlocks*1e3),user.isOnCooldown=!1,user.lastTickBlock=api.blockNumber,user.lastTickTimestamp=currentTimestamp,await api.db.update("users",user),api.emit("turnOn",{account:api.sender}))}}},actions.disableMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});api.assert(null!==market,"market must exist")&&market.isEnabled&&(market.isEnabled=!1,await api.db.update("markets",market),user.enabledMarkets-=1,await api.db.update("users",user),api.emit("disableMarket",{account:api.sender,symbol:symbol}))}}},actions.enableMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null!==market,"market must exist")&&!market.isEnabled){const authorizedAction=user.isPremium||1===user.markets;if(api.assert(authorizedAction,"user has too many markets; premium upgrade required")){const params=await api.db.findOne("params",{});let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake));const hasEnoughStake=await verifyUtilityTokenStake(requiredStake,api.sender);api.assert(hasEnoughStake,"must stake more ENG to enable market")&&(market.isEnabled=!0,await api.db.update("markets",market),user.enabledMarkets+=1,await api.db.update("users",user),api.emit("enableMarket",{account:api.sender,symbol:symbol}))}}}}},actions.removeMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});api.assert(null!==market,"market must exist")&&(user.markets-=1,market.isEnabled&&(user.enabledMarkets-=1),await api.db.update("users",user),await api.db.remove("markets",market),api.emit("removeMarket",{account:api.sender,symbol:symbol}))}}};const updateMarketInternal=async(payload,market,shouldPayFee,params)=>{const{maxBidPrice:maxBidPrice,minSellPrice:minSellPrice,maxBaseToSpend:maxBaseToSpend,minBaseToSpend:minBaseToSpend,maxTokensToSell:maxTokensToSell,minTokensToSell:minTokensToSell,priceIncrement:priceIncrement,minSpread:minSpread,maxDistFromNext:maxDistFromNext}=payload;if(void 0===maxBidPrice&&void 0===minSellPrice&&void 0===maxBaseToSpend&&void 0===minBaseToSpend&&void 0===maxTokensToSell&&void 0===minTokensToSell&&void 0===priceIncrement&&void 0===minSpread&&void 0===maxDistFromNext)return!1;if(api.assert(void 0===maxBidPrice||maxBidPrice&&"string"==typeof maxBidPrice&&!api.BigNumber(maxBidPrice).isNaN()&&api.BigNumber(maxBidPrice).gt(0)&&countDecimals(maxBidPrice)<=8,"invalid maxBidPrice")&&api.assert(void 0===minSellPrice||minSellPrice&&"string"==typeof minSellPrice&&!api.BigNumber(minSellPrice).isNaN()&&api.BigNumber(minSellPrice).gt(0)&&countDecimals(minSellPrice)<=8,"invalid minSellPrice")&&api.assert(void 0===maxBaseToSpend||maxBaseToSpend&&"string"==typeof maxBaseToSpend&&!api.BigNumber(maxBaseToSpend).isNaN()&&api.BigNumber(maxBaseToSpend).gt(0)&&countDecimals(maxBaseToSpend)<=8,"invalid maxBaseToSpend")&&api.assert(void 0===minBaseToSpend||minBaseToSpend&&"string"==typeof minBaseToSpend&&!api.BigNumber(minBaseToSpend).isNaN()&&api.BigNumber(minBaseToSpend).gt(0)&&countDecimals(minBaseToSpend)<=8,"invalid minBaseToSpend")&&api.assert(void 0===maxTokensToSell||maxTokensToSell&&"string"==typeof maxTokensToSell&&!api.BigNumber(maxTokensToSell).isNaN()&&api.BigNumber(maxTokensToSell).gt(0)&&countDecimals(maxTokensToSell)<=market.precision,"invalid maxTokensToSell")&&api.assert(void 0===minTokensToSell||minTokensToSell&&"string"==typeof minTokensToSell&&!api.BigNumber(minTokensToSell).isNaN()&&api.BigNumber(minTokensToSell).gt(0)&&countDecimals(minTokensToSell)<=market.precision,"invalid minTokensToSell")&&api.assert(void 0===priceIncrement||priceIncrement&&"string"==typeof priceIncrement&&!api.BigNumber(priceIncrement).isNaN()&&api.BigNumber(priceIncrement).gt(0)&&countDecimals(priceIncrement)<=8,"invalid priceIncrement")&&api.assert(void 0===minSpread||minSpread&&"string"==typeof minSpread&&!api.BigNumber(minSpread).isNaN()&&api.BigNumber(minSpread).gt(0)&&countDecimals(minSpread)<=8,"invalid minSpread")&&api.assert(void 0===maxDistFromNext||maxDistFromNext&&"string"==typeof maxDistFromNext&&!api.BigNumber(maxDistFromNext).isNaN()&&api.BigNumber(maxDistFromNext).gt(0)&&countDecimals(maxDistFromNext)<=8,"invalid maxDistFromNext")){if(shouldPayFee&&!await burnFee(params.basicSettingsFee,!0))return!1;const update={account:market.account,symbol:market.symbol};return maxBidPrice&&(update.oldMaxBidPrice=market.maxBidPrice,market.maxBidPrice=maxBidPrice,update.newMaxBidPrice=maxBidPrice),minSellPrice&&(update.oldMinSellPrice=market.minSellPrice,market.minSellPrice=minSellPrice,update.newMinSellPrice=minSellPrice),maxBaseToSpend&&(update.oldMaxBaseToSpend=market.maxBaseToSpend,market.maxBaseToSpend=maxBaseToSpend,update.newMaxBaseToSpend=maxBaseToSpend),minBaseToSpend&&(update.oldMinBaseToSpend=market.minBaseToSpend,market.minBaseToSpend=minBaseToSpend,update.newMinBaseToSpend=minBaseToSpend),maxTokensToSell&&(update.oldMaxTokensToSell=market.maxTokensToSell,market.maxTokensToSell=maxTokensToSell,update.newMaxTokensToSell=maxTokensToSell),minTokensToSell&&(update.oldMinTokensToSell=market.minTokensToSell,market.minTokensToSell=minTokensToSell,update.newMinTokensToSell=minTokensToSell),priceIncrement&&(update.oldPriceIncrement=market.priceIncrement,market.priceIncrement=priceIncrement,update.newPriceIncrement=priceIncrement),minSpread&&(update.oldMinSpread=market.minSpread,market.minSpread=minSpread,update.newMinSpread=minSpread),maxDistFromNext&&(update.oldMaxDistFromNext=market.maxDistFromNext,market.maxDistFromNext=maxDistFromNext,update.newMaxDistFromNext=maxDistFromNext),await api.db.update("markets",market),api.emit("updateMarket",update),!0}return!1};actions.updateMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const params=await api.db.findOne("params",{});let authorizedAction=!1;if(authorizedAction=!!user.isPremium||await verifyUtilityTokenBalance(params.basicSettingsFee,api.sender),api.assert(authorizedAction,"you must have enough tokens to cover the settings change fee")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null!==market,"market must exist")){return await updateMarketInternal(payload,market,!user.isPremium,params)}}}}return!1},actions.addMarket=async payload=>{const{symbol:symbol,isSignedWithActiveKey:isSignedWithActiveKey}=payload;if(api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")&&api.assert(symbol&&"string"==typeof symbol&&"STEEMP"!==symbol,"invalid params")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null!==user,"user not registered")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(null!==token,"symbol must exist")){const market=await api.db.findOne("markets",{account:api.sender,symbol:symbol});if(api.assert(null===market,"market already added")){const authorizedAddition=user.isPremium||0===user.markets;if(api.assert(authorizedAddition,"not allowed to add another market")){const params=await api.db.findOne("params",{});let requiredStake=api.BigNumber(params.stakePerMarket).multipliedBy(user.markets+1);user.isPremium&&(requiredStake=requiredStake.plus(params.premiumBaseStake));const hasEnoughStake=await verifyUtilityTokenStake(requiredStake,api.sender);if(api.assert(hasEnoughStake,"must stake more ENG to add a market")){const newMarket={account:api.sender,symbol:symbol,precision:token.precision,strategy:1,maxBidPrice:"1000",minSellPrice:"0.00000001",maxBaseToSpend:"100",minBaseToSpend:"1",maxTokensToSell:"100",minTokensToSell:"1",priceIncrement:"0.00001",minSpread:"0.00000001",maxDistFromNext:"0.0001",isEnabled:!0,creationTimestamp:getCurrentTimestamp(),creationBlock:api.blockNumber},addedMarket=await api.db.insert("markets",newMarket);api.emit("addMarket",{account:api.sender,symbol:symbol}),user.markets+=1,user.enabledMarkets+=1,await api.db.update("users",user),await updateMarketInternal(payload,addedMarket,!1,params)}}}}}}},actions.register=async payload=>{const{isSignedWithActiveKey:isSignedWithActiveKey}=payload,params=await api.db.findOne("params",{}),authorizedRegistration=await verifyUtilityTokenBalance(params.basicFee,api.sender);if(api.assert(authorizedRegistration,"you must have enough tokens to cover the registration fee")&&api.assert(!0===isSignedWithActiveKey,"you must use a custom_json signed with your active key")){const user=await api.db.findOne("users",{account:api.sender});if(api.assert(null===user,"user already registered")){if(!await burnFee(params.basicFee,isSignedWithActiveKey))return!1;const creationTimestamp=getCurrentTimestamp(),newUser={account:api.sender,isPremium:!1,isPremiumFeePaid:!1,isOnCooldown:!1,isEnabled:!0,markets:0,enabledMarkets:0,timeLimit:3*params.basicDurationBlocks*1e3,lastTickTimestamp:creationTimestamp,lastTickBlock:api.blockNumber,creationTimestamp:creationTimestamp,creationBlock:api.blockNumber};return await api.db.insert("users",newUser),api.emit("register",{account:api.sender}),!0}}return!1};