Settings

Blockchain
Network
Unit
Language
Theme
Sound New Block

Transaction

0ce4615a1ed7f31e794dd94bef00fea9d529a72bc9e98889fdebc875c690fe74
Timestamp (utc)
2020-09-29 22:41:07
Fee Paid
0.00008506 BSV
(
0.00101248 BSV
-
0.00092742 BSV
)
Fee Rate
500 sat/KB
Version
1
Confirmations
343,852
Size Stats
17,009 B

4 Outputs

Total Output:
0.00092742 BSV
  • jrun b1b605103eMMA{"in":0,"ref":["native://Jig"],"out":["5fc3cf5593ab53c6d723800f094a9e2fe7a1cbb6c596060b6559f0bea02c790b","f6a4d8fdb3f1a202c6b4d8fe3533811aa032623e2f3b332f4f6373dbb52d48e2"],"del":[],"cre":["n2UeYD39YQ9pzxkbJfkDtNBRiV8PoNvLbq","n2UeYD39YQ9pzxkbJfkDtNBRiV8PoNvLbq"],"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 // beware even freeze should not apply to exchange_old_tokens() ! we want to remain able to exchange later\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 if(this._isFrozen()) throw new Error(function_id+\"token contract has been frozen\")\r\n if(this._isBlacklisted(this.owner)) throw new Error(function_id_combine+\"your address got blacklisted: \"+this.owner)\r\n if (!Array.isArray(tokens) || tokens.length < 2) {\r\n throw new Error(function_id_combine+\"Invalid tokens to combine : \",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 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(e){\r\n throw(function_id_combine+e)\r\n }\r\n return\r\n }\r\n\r\n // Case: Mint\r\n if (caller === this.constructor) { // call from static function of this class\r\n const function_id_mint = function_id+\" mint : \"\r\n try{\r\n if(this._isFrozen()) throw new Error(function_id+\"token contract has been frozen\")\r\n if(this._isBlacklisted(this.owner)) throw new Error(function_id_mint+\"your address got blacklisted: \"+this.owner)\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(e){\r\n throw(function_id_mint+e)\r\n }\r\n return\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._isFrozen()) throw new Error(function_id+\"token contract has been frozen\")\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(e){\r\n throw(function_id_send+e)\r\n }\r\n\r\n return\r\n }\r\n }\r\n\r\n static mint (amount, timestamp) {\r\n const function_id = this.name+(this.tokenName?\" (\"+this.tokenName+\")\":\"\")+\": mint(): \"\r\n this.mintAmount = amount\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 \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 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\r\n const token = new this(timestamp, ...tokens)\r\n\r\n return token\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 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 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 if(this._isFrozen()) throw new Error(function_id+\"token contract has been frozen\")\r\n if(this.owner != this.constructor.owner) throw(function_id+\" Only \"+this.classname+\"'s 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)\r\n this.constructor.reduceSupplyIfAdmin(amount_to_burn)\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 if(this._isFrozen()) throw new Error(function_id+\"token contract has been frozen\")\r\n this._checkAmount(amount)\r\n if (amount > this.amount) {\r\n throw new Error(function_id+'Not enough funds')\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 _isFrozen(){\r\n const function_id = this.classname+\"_isFrozen(): \"\r\n var revokercontract = this.constructor.revokercontract\r\n if(!revokercontract) throw new Error(function_id+\"no revokercontract found\")\r\n return revokercontract.is_frozen\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_frozen) throw new Error(function_id+\" token contract has been frozen\")\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 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/0ce4615a1ed7f31e794dd94bef00fea9d529a72bc9e98889fdebc875c690fe74