Transaction

eebf4b25eaa66a8c7b7f25217a8cf224c1cedc3ec1ebcb9f6540841e18677b56
( - )
224,057
2020-10-09 16:18:12
1
33,347 B

6 Outputs

Total Output:
  • jrun b1b605103eM´{"in":0,"ref":["native://Jig"],"out":["38465971eef697e9406283e31c4b2d285b594e3e6700504e9f717c26989797f9","e04daca130fa03a4bd86208ce14258b65e4370cd87c8b7a77317f0552569b71d"],"del":[],"cre":["mwG9KT17nu9TDC5aWaCjWHfBGU5tqY4ciz","mwG9KT17nu9TDC5aWaCjWHfBGU5tqY4ciz"],"exec":[{"op":"DEPLOY","data":["class TokenContract extends Jig {\r\n //\r\n // with RUN 0.6 preview version \r\n //\r\n init (timestamp, ...tokens) {\r\n this.classname = \"TokenContract: \"\r\n const function_id = this.classname+\"init(): \"\r\n //\r\n // The base Token class cannot be created on its own\r\n if (Object.getPrototypeOf(this.constructor) === Jig) {\r\n throw new Error(function_id+'must be extended')\r\n }\r\n //\r\n this._checkTimestamp(timestamp) // important to check here because we can't do it in static functions\r\n //\r\n // Case: Combine\r\n if(caller==null) { //called by user\r\n const function_id_combine = function_id+\" combine : \"\r\n try {\r\n // it is very important to block combine if contract is frozen/backedup/revoked to prevent honest users from mixing their coins with hacked ones\r\n if(this.constructor.isRevoked()) throw new Error(function_id+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id+\"token contract has been frozen\") // check frozen as last always!\r\n // we cannot check if this.owner got blacklisted as we are in init where owner is not yet defined, but we can check for each token.owner\r\n if (!Array.isArray(tokens) || tokens.length < 2) {\r\n throw new Error(function_id_combine+\"Invalid tokens to combine : \"+JSON.Stringify(tokens))\r\n }\r\n // Each token to combine must all be of this type\r\n if (tokens.some(token => token.constructor !== this.constructor)) {\r\n throw new Error(function_id_combine+\"Cannot combine different token classes\")\r\n }\r\n // Check for duplicate tokens in the array\r\n const countOf = token => tokens.reduce((count, next) => next === token ? count + 1 : count, 0)\r\n if (tokens.some(token => countOf(token) > 1)) throw new Error(function_id_combine+\"Cannot combine duplicate tokens\")\r\n // Destroy each token, absorbing it into this one\r\n this.amount = 0\r\n var oldest_mint_count = null\r\n tokens.forEach(token => {\r\n if(this.isBlacklisted(token.owner)) throw new Error(function_id_combine+\"your address got blacklisted: \"+token.owner)\r\n this.amount += token.amount\r\n if(oldest_mint_count == null || token.oldest_mint_count < oldest_mint_count) oldest_mint_count = token.oldest_mint_count\r\n token.destroy() // this ensures only the owner of the tokens can combine because this needs to be signed by the owner of the tokens to combine\r\n })\r\n this.action = \"combine\"\r\n this.timestamp = timestamp\r\n this.oldest_mint_count = oldest_mint_count\r\n // Make sure our new amount is within safe range\r\n this._checkAmount(this.amount)\r\n }catch(error){\r\n throw new Error(function_id_combine+error) // didn't show the error with ',', see if '+' is better\r\n }\r\n return\r\n }\r\n\r\n // Cases: Mint & Convert\r\n if (caller === this.constructor) { // call from static function of this class\r\n //\r\n if(caller.mintAmount) { // we could also check that 'tokens' array from parameters is empty\r\n //\r\n // Case: Mint\r\n const function_id_mint = function_id+\" mint : \"\r\n try{\r\n if(this.constructor.isRevoked()) throw new Error(function_id_mint+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id_mint+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id_mint+\"token contract has been frozen\") // check frozen as last always!\r\n // no need to check blacklist in case of mint since we can't blacklist ourselves\r\n this._checkAmount(caller.mintAmount) // important to check here\r\n this.amount = caller.mintAmount\r\n this.action = \"mint\"\r\n //\r\n //console.log(function_id_mint+\" \\n caller.mintAmount=\"+caller.mintAmount+\"\\n timestamp=\"+timestamp+\"\\n caller=\",caller,\"\\n \"+caller)\r\n //\r\n this.timestamp = timestamp\r\n this.oldest_mint_count = this.constructor.nonce // TODO see if we should add +1 here ?\r\n //console.log(function_id_mint+\" this.oldest_mint_count = \"+this.oldest_mint_count)\r\n }catch(error){\r\n throw new Error(function_id_mint+error)\r\n }\r\n return\r\n }\r\n else {\r\n //\r\n // Case: Convert\r\n const function_id_convert = function_id+\" convert : \"\r\n try{\r\n if(!tokens) throw new Error(function_id_convert+\" tokens array from parameters not found \")\r\n if(tokens.length != 1) throw new Error(function_id_convert+\" tokens array from parameters has length \"+tokens.length+\", length should be 1\")\r\n //\r\n if(this.constructor.isRevoked()) throw new Error(function_id_convert+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id_convert+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id_convert+\"token contract has been frozen\") // check frozen as last always!\r\n //\r\n if(!this.constructor.accepted_conversions) throw new Error( function_id_convert+\"no accepted_conversions found in the token contract\")\r\n //\r\n // check not blacklisted in current contract\r\n var checknotblacklisted = this._checkNotBlacklisted(this.owner)\r\n if(checknotblacklisted == false) throw new Error(function_id_convert+\"your address got blacklisted : \"+this.owner)\r\n // because the new contract copied the blacklist before the revoke_count, checking in the current contract is enough and there is no need to check blacklist from the previous contract\r\n // vv\r\n // remark: we don't care about entries after the revoke_count in the multlist here because we only deal with basic, raw amounts\r\n //\r\n //\r\n if(!timestamp) throw function_id_convert+\"timestamp missing: \"+timestamp\r\n this._checkTimestamp(timestamp)\r\n //\r\n const old_token = tokens[0] // we already checked that there is only one token to convert given in parameters so this is safe\r\n //\r\n /*\r\n // CHECK FOR REVOKED MINTS based on oldest_mint_count\r\n if(old_token.constructor.isRevoked()) // if previous contract was revoked\r\n {\r\n const prev_revokercontract = old_token.constructor.revokercontract\r\n if(!prev_revokercontract) throw new Error(function_id_convert+\" no contract.revokercontract found in the contract of the token to convert\")\r\n const revoke_count = prev_revokercontract.revoke_count\r\n if(old_token.oldest_mint_count >= revoke_count) throw new Error(function_id_convert+\"cannot convert revoked tokens\")\r\n }*/\r\n //\r\n if(timestamp <= old_token.timestamp) throw new Error(function_id_convert+\"you cannot convert with a timestamp that is before the old_token's timestamp: \"+timestamp+\" <= \"+old_token.timestamp)\r\n //\r\n if(!old_token) throw new Error(function_id_convert+\"you must give an old token to convert for a new one, old_token=\"+old_token)\r\n //\r\n // brenton: you can check if typeof myjig === 'function' because for now, all function parameters coming from outside the jig have to be code jigs (modifié) \r\n if( !(typeof old_token === 'object' || typeof old_token === 'function') ) { \r\n throw new Error(function_id_convert+'bad old_token type')\r\n } \r\n const accepted_conversions = this.constructor.accepted_conversions\r\n var old_token_origin_is_in_accepted_conversions = false\r\n accepted_conversions.forEach((acceptedorigin)=>{ // need to extract [0] because [1] is the revoke_count\r\n if(acceptedorigin[0] == old_token.constructor.origin){\r\n // we need to check if the token was a revoked one\r\n if(acceptedorigin[1] != null && old_token.oldest_mint_count >= acceptedorigin[1]) throw new Error(function_id_convert+\"cannot convert revoked tokens\")\r\n else old_token_origin_is_in_accepted_conversions = true\r\n }\r\n })\r\n if(old_token_origin_is_in_accepted_conversions==false) throw new Error (function_id_convert+\" wrong old_token origin: current token_contract.accepted_conversions doesn't include \"+old_token.constructor.origin)\r\n //\r\n if(this.owner != old_token.owner) throw new Error(function_id_convert+\"you cannot convert an old_token that you do not own\")\r\n //\r\n // ALL CHECKS PASSED\r\n //\r\n // now we have a valid timestamp and some valid old_token that are validated as such in the token_contract & the caller is also the owner of this old_token => we can convert it\r\n //\r\n // to prevent converting ad infinitum of the old tokens, we don't mint new ones if we cannot destroy the old tokens first \r\n var old_token_amount = old_token.amount // save it because destroy will set amount to 0\r\n old_token.destroy() // destroy the old token, this also ensure that only the old_token owner can convert (good to have a double check)\r\n //\r\n // now that the old token has been burned we can mint the new one\r\n this.action = \"convert\"\r\n this.amount = old_token_amount\r\n this.timestamp = timestamp\r\n this.oldest_mint_count = 0 //all the new converted tokens have an oldest_mint_count of 0. another solution would be to make them at old_token.oldest_mint_count and restart the new adminCounter from the value of the old one\r\n }\r\n catch(error){\r\n throw new Error(function_id_convert+error)\r\n }\r\n return\r\n }\r\n }\r\n //\r\n // Case: Send\r\n if (caller && caller.constructor === this.constructor) { // call from a non-static function of this class\r\n const function_id_send = function_id+\" send : \"\r\n try {\r\n if(this.constructor.isRevoked()) throw new Error(function_id_send+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id_send+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id_send+\"token contract has been frozen\") // check frozen as last always!\r\n if(this.isBlacklisted(caller.sendSender)) throw new Error(function_id_send+\"your address got blacklisted: \"+caller.sendSender)\r\n if(this.isBlacklisted(caller.sendOwner)) throw new Error(function_id_send+\"the address you are trying to send to got blacklisted: \"+caller.sendOwner)\r\n this._checkAmount(caller.sendAmount)\r\n //\r\n this.amount = caller.sendAmount\r\n this.owner = caller.sendOwner\r\n this.timestamp = timestamp\r\n this.sender = caller.sendSender\r\n this.oldest_mint_count = caller.send_oldest_mint_count\r\n this.action = \"send\"\r\n }\r\n catch(error){\r\n throw new Error(function_id_send+error)\r\n }\r\n return\r\n }\r\n }\r\n\r\n // idea: we could only allow after checking if this.nonce < 5 to add some kind of timing / action limit\r\n static allow_convert_from_previous_contract(previous_tokencontract){\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": allow_convert_from_previous_contract(): \"\r\n //\r\n try{\r\n if(this.accepted_conversions) throw new Error(function_id+\"already called this function, it can only be called once.\")\r\n // we can't allow calling more than once otherwise a hacker could add other contracts to it\r\n this.accepted_conversions = [] // init\r\n //\r\n // brenton: you can check if typeof myjig === 'function' because for now, all function parameters coming from outside the jig have to be code jigs (modifié) \r\n if( !(typeof previous_tokencontract === 'object' || typeof previous_tokencontract === 'function') ) {\r\n throw new Error(function_id+\" parameter previous_tokencontract must be a Jig\")\r\n }\r\n if( previous_tokencontract.origin == this.origin) throw new Error(function_id+\"cannot link a contract to itself\")\r\n //\r\n if(!previous_tokencontract.revokercontract) throw new Error(function_id+\" previous_tokencontract.revokercontract not found\")\r\n if(!previous_tokencontract.multlist) throw new Error(function_id+\" previous_tokencontract.multlist not found\")\r\n if(!previous_tokencontract.blacklist) throw new Error(function_id+\" previous_tokencontract.blacklist not found\")\r\n if(!previous_tokencontract.mint_action_numbers) throw new Error(function_id+\" previous_tokencontract.mint_action_numbers not found\")\r\n if(!previous_tokencontract.mint_list) throw new Error(function_id+\" previous_tokencontract.mint_list not found\")\r\n //\r\n const previous_isrevoked = previous_tokencontract.revokercontract.is_revoked\r\n var prev_revoke_count = null\r\n var validsupply_toadd = 0\r\n var i = 0\r\n if(previous_isrevoked)\r\n {\r\n prev_revoke_count = previous_tokencontract.revokercontract.revoke_count\r\n if(!prev_revoke_count) throw new Error(function_id+\" contract was revoked but revoke_count not found in \"+previous_tokencontract.revokercontract.location)\r\n // let's calculate supply by adding all the mints before the prev_revoke_count\r\n var mint_actionnums = previous_tokencontract.mint_action_numbers\r\n var mintlist = previous_tokencontract.mint_list \r\n i = 0\r\n if(mint_actionnums && mint_actionnums.length > 0){\r\n while(mint_actionnums[i] < prev_revoke_count) {\r\n validsupply_toadd += mintlist[i]\r\n i += 1\r\n }\r\n }\r\n }\r\n else {\r\n // the previous contract was not revoked so we can just add the entire supply \r\n validsupply_toadd = previous_tokencontract.supply\r\n }\r\n this.supply += validsupply_toadd\r\n //\r\n // now add to accepted_conversions the origin of the previous_tokencontract with its prev_revoke_count\r\n this.accepted_conversions.push([previous_tokencontract.origin, prev_revoke_count])\r\n // now we add the list of accepted_conversions from the previous_tokencontract\r\n const prev_conversions = previous_tokencontract.accepted_conversions\r\n if(prev_conversions){ // seulement si le contrat d'avant en avait\r\n for(i = 0; i < prev_conversions.length; i++){\r\n this.accepted_conversions.push(prev_conversions[i])\r\n } \r\n }\r\n //console.log(function_id+\" this.accepted_conversions = \", this.accepted_conversions)\r\n //\r\n // now copy previous mintuples & blacklist\r\n var prev_multlist = previous_tokencontract.multlist\r\n var prev_blacklist = previous_tokencontract.blacklist\r\n //\r\n // copy mintuples\r\n if(prev_multlist.list_action_numbers && prev_multlist.list_action_numbers.length > 0) {\r\n for(i = 0; i < prev_multlist.list_action_numbers.length; i++){\r\n if(prev_revoke_count == null || prev_multlist.list_action_numbers[i] < prev_revoke_count) {\r\n var newmult = prev_multlist.list[i]\r\n var newtimestamp = prev_multlist.timestamps[i]\r\n this.multlist.publish(newmult, newtimestamp)\r\n }\r\n }\r\n }\r\n //console.log(function_id+\" this.multlist.list = \", this.multlist.list)\r\n //\r\n // copy blacklist\r\n if(prev_blacklist.list_action_numbers && prev_blacklist.list_action_numbers.length > 0) {\r\n for(i = 0; i < prev_blacklist.list_action_numbers.length; i++){\r\n if(prev_revoke_count == null || prev_blacklist.list_action_numbers[i] < prev_revoke_count) {\r\n var newaddress = prev_blacklist.list[i]\r\n var newtimestamp = prev_blacklist.timestamps[i]\r\n this.blacklist.blacklist(newaddress, newtimestamp)\r\n }\r\n }\r\n }\r\n //console.log(function_id+\" this.blacklist.list = \", this.blacklist.list)\r\n } catch(error){\r\n throw(function_id+error)\r\n }\r\n this.action = \"allow_convert_from_previous_contract\" \r\n }\r\n\r\n static mint (amount, timestamp) {\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": mint(): \"\r\n //\r\n try{\r\n this.mintAmount = amount // this ensures only the class owner can mint because others cannot create class properties\r\n\r\n // we cannot call _checkTimestamp in a static function because it's private but make sure to check in the init()\r\n // same for isBlacklisted, isFrozen\r\n // => mais on pourrait tout mettre en static ?\r\n\r\n // init mint lists if not already done => this will serve to calculate the supply with the revoke_count if the contract was revoked\r\n if(!this.mint_list) this.mint_list = []\r\n if(!this.mint_timestamps) this.mint_timestamps = [] // timestamps are not really necessary for revoking but why not save them too\r\n if(!this.mint_action_numbers) this.mint_action_numbers = []\r\n this.mint_list.push(amount)\r\n this.mint_timestamps.push(timestamp)\r\n this.mint_action_numbers.push(this.nonce) // todo see if we should add +1 here ?\r\n \r\n const token = new this(timestamp)\r\n delete this.mintAmount\r\n this.supply += amount\r\n\r\n return token\r\n }\r\n catch(error){\r\n throw(function_id+error)\r\n }\r\n }\r\n //\r\n // fonction redondante mais pourquoi pas la garder pour la praticité\r\n static combine (timestamp, ...tokens) {\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": combine(): \"\r\n\r\n // we cannot call _checkTimestamp in a static function because it's private but make sure to check in the init()\r\n // same for isBlacklisted, isFrozen\r\n // => mais on pourrait tout mettre en static ?\r\n\r\n const token = new this(timestamp, ...tokens)\r\n\r\n return token\r\n }\r\n static convert_tokens(timestamp, ...tokenstoconvert){\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": convert_tokens(): \"\r\n //\r\n // this allows exchanging old tokens for these new ones after they have been revoked or backedup\r\n //\r\n // no need to check blacklisted or timestamp or anything else here because it will be done in the call below\r\n // + this is a static method so it doesn't have access to this & other functions\r\n //\r\n var new_tokens = []\r\n try {\r\n tokenstoconvert.forEach( (curr_oldtoken) => {\r\n var newtoken = new this(timestamp, curr_oldtoken)\r\n new_tokens.push(newtoken)\r\n })\r\n }catch(error){\r\n throw new Error(function_id+error)\r\n }\r\n return new_tokens\r\n }\r\n\r\n static reduceSupplyIfAdmin(amount){\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": reduceSupplyIfAdmin(): \"\r\n\r\n // working if you call it from destroy() with this.constructor.reduceSupplyIfAdmin(this.amount)\r\n\r\n if( !(caller instanceof this)) throw function_id+\" this function cannot be called by a user (it is static but also private in a way)\"\r\n\r\n // checkAmount, we have to redo it step by step here since we cannot call private functions from static ones\r\n if (typeof amount !== 'number') throw new Error(function_id+'amount is not a number : '+amount) // using throw gives better error trace than expect()\r\n if (!Number.isInteger(amount)) throw new Error(function_id+'amount must be an integer : '+amount) \r\n if (amount <= 0) throw new Error(function_id+'amount must be positive : '+amount)\r\n if (amount > Number.MAX_SAFE_INTEGER) throw new Error(function_id+'amount too large : '+amount)\r\n\r\n if(amount > this.supply) throw function_id+\" amount too large, you are trying to reduce supply by \"+amount+\" but supply is \"+this.supply\r\n\r\n //console.log(function_id+\" caller = \\n \"+caller+\"\\n \",caller)\r\n this.supply -= amount // this can only be done by the class' owner so it enforces that only the admin can call this function\r\n }\r\n\r\n // redefining the one from extended Jig class\r\n destroy () { // BEWARE for admin YOU SHOULD RATHER USE BURN TO ATTACH A PRECISE TIMESTAMP TO THIS ACTION\r\n const function_id = this.classname+\"destroy(): \"\r\n\r\n // beware destroy() should remain possible even if the contract was revoked, backedup, or frozen (to allow converting coins to new contract)\r\n\r\n // beware destroy applies to all coins so it doesn't decrease supply if someone that isn't the class' owner destroys his coins\r\n\r\n //console.log(function_id+\" caller : \"+caller+\"\\n\",caller)\r\n \r\n if(caller == null && this.owner == this.constructor.owner){ // caller is user and is the owner of the token's class\r\n // user called destroy(). we are not combining or sending tokens\r\n //console.log(function_id+\" destroy from admin detected : try to reduce token supply\")\r\n this.constructor.reduceSupplyIfAdmin(this.amount)\r\n } \r\n\r\n super.destroy()\r\n this.action = \"destroy\"\r\n this.amount = 0\r\n }\r\n\r\n burn(timestamp, amount_to_burn){ // amount_to_burn is optional, if not given will burn all\r\n const function_id = this.classname+\"burn(): \"\r\n //\r\n try{\r\n if(this.constructor.isRevoked()) throw new Error(function_id+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id+\"token contract has been frozen\") // check frozen as last always!\r\n \r\n if(this.owner != this.constructor.owner) throw(function_id+\" Only \"+this.classname+\" class owner can burn\\n class owner is \"+this.constructor.owner+\" and you are \"+this.owner )\r\n\r\n //if no amount_to_burn specified, burn all\r\n amount_to_burn = typeof amount_to_burn === 'undefined' ? this.amount : amount_to_burn \r\n\r\n this._checkAmount(amount_to_burn)\r\n if(amount_to_burn > this.amount) throw(function_id+\" not enough funds. Trying to burn \"+amount_to_burn+\" & this.amount=\"+this.amount)\r\n\r\n if(!timestamp) throw function_id+\" timestamp missing: \"+timestamp\r\n this._checkTimestamp(timestamp)\r\n\r\n // all checks passed\r\n this.timestamp = timestamp\r\n this.action = \"burn\" \r\n this.amount_burnt = amount_to_burn\r\n this.action_count = this.constructor.nonce // TODO see if we should do +1 ?\r\n\r\n if(amount_to_burn == this.amount) {\r\n //this.destroy() // TODO maybe we should use that instead ?\r\n this.amount = 0\r\n } else {\r\n this.amount -= amount_to_burn // decrease amount\r\n }\r\n //console.log(function_id+\" state of parent class : \",this.constructor) // logging jigs for now creates bugs\r\n this.constructor.reduceSupplyIfAdmin(amount_to_burn)\r\n }\r\n catch(error){\r\n throw(function_id+error)\r\n }\r\n }\r\n\r\n send (to, timestamp, amount = this.amount) {\r\n const function_id = this.classname+\"send(): \"\r\n try {\r\n\r\n if(this.constructor.isRevoked()) throw new Error(function_id+\"token contract has been revoked\")\r\n if(this.constructor.isBackedup()) throw new Error(function_id+\"token contract has been backedup\")\r\n if(this.constructor.isFrozen()) throw new Error(function_id+\"token contract has been frozen\") // check frozen as last always!\r\n\r\n this._checkAmount(amount)\r\n if (amount > this.amount) {\r\n throw new Error(function_id+\"Not enough funds\")//, trying to send \"+amount+\" but this UTXO only has \"+this.amount)\r\n }\r\n this._checkTimestamp(timestamp)\r\n if(timestamp <= this.timestamp) throw function_id+\"you cannot send with a timestamp that is before the coin to send's timestamp: \"+timestamp+\" <= \"+this.timestamp\r\n\r\n if(this.isBlacklisted(this.owner)) throw function_id+\"your address got blacklisted: \"+this.owner\r\n if(this.isBlacklisted(to)) throw function_id+\" the address you are trying to send to got blacklisted: \"+to\r\n\r\n this.sendAmount = amount\r\n this.sendOwner = to\r\n this.sendSender = this.owner\r\n this.send_oldest_mint_count = this.oldest_mint_count\r\n\r\n const sent = new this.constructor(timestamp)\r\n delete this.sendAmount\r\n delete this.sendOwner\r\n delete this.sendSender\r\n delete this.send_oldest_mint_count\r\n\r\n if (this.amount === amount){ // || (this.amount - amount) <= 10) { // change token with amount below 10 with decimals=8 is useless and happens too often\r\n this.destroy()\r\n } else {\r\n this.amount -= amount\r\n this.action = \"change\"\r\n }\r\n return sent\r\n }catch(e){\r\n throw(function_id+e)\r\n }\r\n\r\n }\r\n isBlacklisted(address){\r\n var blacklist = this.constructor.blacklist\r\n if(!blacklist) throw function_id+\" no blacklist found\"\r\n return blacklist.isBlacklisted(address)\r\n }\r\n _checkNotBlacklisted(addresstocheck){ // addresstocheck can be this.owner for example\r\n const function_id = this.classname+\"_checkNotBlacklisted(): \"\r\n var blacklist = this.constructor.blacklist\r\n if(!blacklist) throw new Error(function_id+\" no blacklist found\")\r\n if(blacklist.list.includes(addresstocheck)) throw new Error(function_id+\"address got blacklisted: \"+addresstocheck)\r\n return true\r\n }\r\n // BEWARE WE CAN FREEZE AND STILL BACKUP OR REVOKE AFTER\r\n static isFrozen(){ // static version so that multlist & blacklist can also call it\r\n const function_id = this.classname+\"isFrozen(): \"\r\n if(!this.revokercontract) throw new Error(function_id+\"no revokercontract found\")\r\n return this.revokercontract.is_frozen\r\n }\r\n static isBackedup(){ // static version so that multlist & blacklist can also call it\r\n const function_id = this.classname+\"isBackedup(): \"\r\n if(!this.revokercontract) throw new Error(function_id+\"no revokercontract found\")\r\n return this.revokercontract.is_backedup\r\n }\r\n static isRevoked(){ // static version so that multlist & blacklist can also call it\r\n const function_id = this.classname+\"isRevoked(): \"\r\n if(!this.revokercontract) throw new Error(function_id+\"no revokercontract found\")\r\n return this.revokercontract.is_revoked\r\n }\r\n _checkRevokeState(){\r\n const function_id = this.classname+\"_checkRevokeState(): \"\r\n var revokercontract = this.constructor.revokercontract\r\n if(!revokercontract) throw new Error(function_id+\" no revokercontract found\")\r\n if(revokercontract.is_backedup) throw new Error(function_id+\" token contract has been backedup\")\r\n if(revokercontract.is_revoked) throw new Error(function_id+\" token contract has been revoked\")\r\n if(revokercontract.is_frozen) throw new Error(function_id+\" token contract has been frozen\") // check frozen as last\r\n return true\r\n }\r\n _checkAmount (amount) {\r\n const function_id = this.classname+\": _checkAmount(): \"\r\n if (typeof amount !== 'number') throw new Error(function_id+'amount is not a number : '+amount) // using throw gives better error trace than expect()\r\n if (!Number.isInteger(amount)) throw new Error(function_id+'amount must be an integer : '+amount) \r\n if (amount <= 0) throw new Error(function_id+'amount must be positive : '+amount)\r\n if (amount > Number.MAX_SAFE_INTEGER) throw new Error(function_id+'amount too large : '+amount)\r\n }\r\n _checkNum (number) { // check that number is a positive number (but can be float)\r\n const function_id = this.classname+\": _checkNum(): \"\r\n if (typeof number !== 'number') throw(function_id+'number is not a number : '+number)\r\n if (!(number > 0)) throw new Error(function_id+'number must be positive : '+number)\r\n if (number > Number.MAX_SAFE_INTEGER) throw new Error(function_id+'number too large : '+number)\r\n }\r\n _checkTimestamp (timestamp) {\r\n const function_id = this.classname+\": _checkTimestamp(): \"\r\n try {\r\n this._checkAmount(timestamp) // applies as well to timestamp\r\n } catch(e) { throw(function_id+e) }\r\n if( !(timestamp > 1600939295117) ) throw(function_id+': timestamp must be older than 1600939295117 : '+timestamp) // make sure the timestamp here is in ms !!\r\n }\r\n}",{"decimals":8,"deps":{"Jig":{"$jig":0},"expect":{"$jig":2}},"icon":{"emoji":null},"sealed":false,"supply":0,"symbol":null},"function expect(t){let e=!1;const n=t=>{if(\"object\"!=typeof t||!t)return t;try{return JSON.stringify(t)}catch(e){return t.toString()}};function r(r,o,i){if(e?r:!r)throw new Error(i||`expected value${e?\" not\":\"\"} to be ${o} but was ${n(t)}`)}function o(t,e){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(\"object\"!=typeof t)return!1;if(null===t||null===e)return!1;if(Object.getPrototypeOf(t)!==Object.getPrototypeOf(e))return!1;if(Object.keys(t).length!==Object.keys(e).length)return!1;if(!Object.keys(t).every((n=>o(t[n],e[n]))))return!1;if(t instanceof Set){if(t.size!==e.size)return!1;if(!o(Array.from(t.entries()),Array.from(e.entries())))return!1}if(t instanceof Map){if(t.size!==e.size)return!1;if(!o(Array.from(t.entries()),Array.from(e.entries())))return!1}return!0}function i(t,e){if(\"function\"!=typeof t)return!1;if(\"function\"!=typeof e)return!1;for(;t;)if((t=Object.getPrototypeOf(t))===e)return!0;return!1}return{get not(){return e=!e,this},toBe:(e,o)=>r(t===e,\"\"+n(e),o),toEqual:(e,i)=>r(o(t,e),\"equal to \"+n(e),i),toBeInstanceOf:(e,n)=>r(t&&t instanceof e,\"an instance of \"+(e&&e.name),n),toBeDefined:e=>r(void 0!==t,\"defined\",e),toBeNull:e=>r(null===t,\"null\",e),toBeNumber:e=>r(\"number\"==typeof t,\"a number\",e),toBeInteger:e=>r(Number.isInteger(t),\"an integer\",e),toBeLessThan:(e,n)=>r(t<e&&\"number\"==typeof t&&\"number\"==typeof e,\"less than \"+e,n),toBeLessThanOrEqualTo:(e,n)=>r(t<=e&&\"number\"==typeof t&&\"number\"==typeof e,\"less than or equal to \"+e,n),toBeGreaterThan:(e,n)=>r(t>e&&\"number\"==typeof t&&\"number\"==typeof e,\"greater than \"+e,n),toBeGreaterThanOrEqualTo:(e,n)=>r(t>=e&&\"number\"==typeof t&&\"number\"==typeof e,\"greater than or equal to \"+e,n),toBeBoolean:e=>r(\"boolean\"==typeof t,\"a boolean\",e),toBeString:e=>r(\"string\"==typeof t,\"a string\",e),toBeObject:e=>r(t&&\"object\"==typeof t,\"an object\",e),toBeArray:e=>r(Array.isArray(t),\"an array\",e),toBeSet:e=>r(t instanceof Set,\"a set\",e),toBeMap:e=>r(t instanceof Map,\"a map\",e),toBeUint8Array:e=>r(t instanceof Uint8Array,\"a uint8array\",e),toBeClass:e=>r(\"function\"==typeof t&&t.toString().startsWith(\"class\"),\"a class\",e),toBeFunction:e=>r(\"function\"==typeof t&&!t.toString().startsWith(\"class\"),\"a function\",e),toBeJigClass:e=>r(\"function\"==typeof t&&t.toString().startsWith(\"class\")&&i(t,Jig),\"a jig class\",e),toExtendFrom:(e,n)=>r(i(t,e),\"an extension of \"+(e&&e.name),n)}}",{"deps":{"Jig":{"$dup":["1","deps","Jig"]}}}]}]}
    https://whatsonchain.com/tx/eebf4b25eaa66a8c7b7f25217a8cf224c1cedc3ec1ebcb9f6540841e18677b56
Total Output: