├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist └── atomicassets.js ├── lib └── float.js ├── package.json ├── src ├── API │ ├── Explorer │ │ ├── Enums.ts │ │ ├── Objects.ts │ │ ├── Params.ts │ │ └── index.ts │ └── Rpc │ │ ├── Asset.ts │ │ ├── BaseCache.ts │ │ ├── Collection.ts │ │ ├── Offer.ts │ │ ├── Queue.ts │ │ ├── RpcCache.ts │ │ ├── Schema.ts │ │ ├── Template.ts │ │ └── index.ts ├── Actions │ ├── Explorer.ts │ ├── Generator.ts │ └── Rpc.ts ├── Errors │ ├── ApiError.ts │ ├── DeserializationError.ts │ ├── ExplorerError.ts │ ├── RpcError.ts │ ├── SchemaError.ts │ └── SerializationError.ts ├── Schema │ ├── MappingSchema.ts │ ├── ValueSchema.ts │ ├── VectorSchema.ts │ └── index.ts ├── Serialization │ ├── Binary.ts │ ├── Coders │ │ └── Base.ts │ ├── State.ts │ ├── TypeParser │ │ ├── BooleanParser.ts │ │ ├── ByteParser.ts │ │ ├── FixedIntegerParser.ts │ │ ├── FixedParser.ts │ │ ├── FloatingParser.ts │ │ ├── IPFSParser.ts │ │ ├── StringParser.ts │ │ ├── VariableIntegerParser.ts │ │ ├── VariableParser.ts │ │ └── index.ts │ ├── Types.ts │ └── index.ts └── index.ts ├── test ├── binary.test.ts ├── explorerapi.test.ts ├── rpcapi.test.ts ├── serialization_advanced.test.ts ├── serialization_basic.test.ts ├── serialization_contract.test.ts └── serialization_utf8.test.ts ├── tsconfig.json ├── tsconfig.web.json ├── tslint.json ├── webpack.prod.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | #dist/ 4 | build/ 5 | docs/ 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | docs/ 4 | src/ 5 | dist/ 6 | test/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present pink.network and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AtomicAssets JavaScript 2 | 3 | JS Library to read data from the atomicassets NFT standard. 4 | 5 | Contract / General Documentation can be found on [https://github.com/pink.gg/atomicassets-contract/wiki](https://github.com/pink.gg/atomicassets-contract/wiki) 6 | 7 | ## Usage 8 | 9 | This is a [Node.js](https://nodejs.org/en/) module available through the 10 | [npm registry](https://www.npmjs.com/). Installation is done using the 11 | [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): 12 | 13 | ```sh 14 | $ npm install atomicassets 15 | ``` 16 | 17 | ### Initialize 18 | 19 | Web library can be found in the [dist](https://github.com/pink.gg/atomicassets-js/blob/master/dist/atomicassets.js) folder 20 | 21 | ```javascript 22 | // standard import 23 | const {ExplorerApi, RpcApi} = require("atomicassets"); 24 | // ES6 import 25 | import {ExplorerApi, RpcApi} from "atomicassets" 26 | ``` 27 | 28 | ### Serialization 29 | 30 | AtomicAssets uses serialization to store data on the blockchain more efficiently. 31 | The API classes will handle this for you but if you need to manually parse the data, 32 | the library provides you a serialize and deserialize function 33 | 34 | More information can be found [here](https://github.com/pink.gg/atomicassets-contract/wiki/Serialization) 35 | 36 | #### Example 37 | ```javascript 38 | import {serialize, deserialize, ObjectSchema} from "atomicassets" 39 | 40 | // this schema is used for serialisation / deserialization 41 | const schema = ObjectSchema([ 42 | {"name": "attr1", "type": "int32"}, // int8, int16, int32, int64 43 | {"name": "attr2", "type": "uint32"}, // uint8, uint16, uint32, uint64 44 | {"name": "attr3", "type": "fixed32"}, // fixed8, fixed16, fixed32, fixed64 45 | 46 | {"name": "attr4", "type": "bool"}, 47 | 48 | {"name": "attr5", "type": "bytes"}, // variable length raw bytes (UInt8Array) 49 | {"name": "attr6", "type": "string"}, // variable length string 50 | 51 | {"name": "attr7", "type": "ipfs"}, // ipfs hash 52 | {"name": "attr7", "type": "float"}, 53 | {"name": "attr9", "type": "double"}, 54 | 55 | {"name": "arr1", "type": "int32[]"}, // you can add [] to define a type array 56 | ]); 57 | 58 | // the object which will be serialized does not need to have all attributes 59 | // and only the ones who are set, are transferred 60 | const rawObject = { 61 | "attr1": -5843 62 | }; 63 | 64 | // serialize 65 | const serializedData = serialize(rawObject, schema); 66 | // deserialize 67 | const deserializedData = deserialize(serializedData, schema); 68 | // deserializedData === rawObject 69 | ``` 70 | 71 | ## Documentation 72 | 73 | There are two methods available to fetch data from the blockchain. 74 | 75 | * **ExplorerAPI**: uses an hosted API which proves simple and fast REST API endpoints 76 | * **RpcAPI**: uses only native nodeos calls 77 | 78 | ### Explorer API 79 | 80 | The explorer API uses [eosio-contract-api](https://github.com/pink.gg/eosio-contract-api) to query data about the NFTs. 81 | A documentation of each endpoint and its responses can be found [here](https://test.wax.api.atomicassets.io/atomicassets/docs/#/). 82 | It is recommended to self-host the API for the best performance. 83 | 84 | 85 | #### Example 86 | ```javascript 87 | // init Explorer Api 88 | // endpoint: server where atomicassets api is deployed 89 | // namespace: used namespace for the API 90 | // options: 91 | // - fetch: either node-fetch module or the browser equivalent 92 | const api = new ExplorerApi("https://test.wax.api.atomicassets.io", "atomicassets", {fetch}); 93 | 94 | const asset = await api.getAsset("1099511627786"); 95 | 96 | // create the action to mint an asset 97 | const actions = (await api.action).mintasset( 98 | [{actor: "pink.gg", permission: "active"}], 99 | "pink.gg", "collection", "schema", -1, "pink.gg", {"name": "test"}, {"species": "test2"} 100 | ) 101 | ``` 102 | 103 | #### Methods 104 | 105 | ##### Config 106 | `async getConfig(): Promise` 107 | 108 | ##### Assets 109 | `async getAssets(options, page: number = 1, limit: number = 100, data = {}): Promise` 110 | options 111 | * **owner**: string 112 | * **collection_name**: string 113 | * **schema_name**: string 114 | * **template_id**: number 115 | * **match**: search for input in name 116 | * **authorized_account**: string 117 | * **order**: field which is used to sort result 118 | * **sort**: asc | desc 119 | 120 | data 121 | * query for specific asset attributes 122 | 123 | `async getAsset(id: string): Promise` 124 | 125 | `async getAssetLogs(id: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise` 126 | 127 | ##### Collections 128 | `async getCollections(options, page: number = 1, limit: number = 100): Promise` 129 | 130 | options 131 | * **author**: string 132 | * **match**: search for input in name 133 | * **authorized_account**: string 134 | * **notify_account**: string 135 | * **order**: field which is used to sort result 136 | * **sort**: asc | desc 137 | 138 | `async getCollection(name: string): Promise` 139 | 140 | `async getCollectionLogs(name: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise` 141 | 142 | ##### Schemas 143 | `async getSchemas(options, page: number = 1, limit: number = 100): Promise` 144 | 145 | options 146 | * **collection_name**: string 147 | * **schema_name**: string 148 | * **match**: search for input in name 149 | * **authorized_account**: string 150 | * **order**: field which is used to sort result 151 | * **sort**: asc | desc 152 | 153 | `async getSchema(collection: string, name: string): Promise` 154 | 155 | `async getSchemaStats(collection: string, name: string): Promise` 156 | 157 | `async getSchemaLogs(collection: string, name: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise` 158 | 159 | ##### Templates 160 | `async getTemplates(options, page: number = 1, limit: number = 100, data = {}): Promise` 161 | 162 | options 163 | * **collection_name**: string 164 | * **schema_name**: string 165 | * **authorized_account**: string 166 | * **order**: field which is used to sort result 167 | * **sort**: asc | desc 168 | 169 | data 170 | * filter for specific template attributes 171 | 172 | `async getTemplate(collection: string, id: string): Promise` 173 | 174 | `async getTemplateStats(collection: string, id: string): Promise` 175 | 176 | `async getTemplateLogs(collection: string, id: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise` 177 | 178 | ##### Trading 179 | `async getTransfers(options, page: number = 1, limit: number = 100): Promise` 180 | 181 | options 182 | * **account**: string 183 | * **sender**: string 184 | * **recipient**: string 185 | * **asset_id**: asset id which should be included in the offer 186 | * **order**: field which is used to sort result 187 | * **sort**: asc | desc 188 | 189 | `async getOffers(options, page: number = 1, limit: number = 100): Promise` 190 | 191 | options 192 | * **account**: notified account 193 | * **sender**: sender of offer 194 | * **recipient**: recipient of offer 195 | * **is_recipient_contract**: filter if recipient is contract or not 196 | * **asset_id**: asset_id included in offer 197 | * **order**: field which is used to sort result 198 | * **sort**: asc | desc 199 | 200 | `async getOffer(id: string): Promise` 201 | 202 | ##### Accounts 203 | 204 | `async getAccounts(account: string): Promise` 205 | 206 | `async getAccount(account: string): Promise` 207 | 208 | `async getAccountCollection(account: string): Promise` 209 | 210 | #### ExplorerActionGenerator 211 | 212 | The Explorer API has an `action` attribute which contains a helper class to construct contract actions 213 | which can be pushed on chain with eosjs. 214 | 215 | Detailed information about each action can be found [here](https://github.com/pink.gg/atomicassets-contract/wiki/Actions) 216 | 217 | #### Types 218 | 219 | Each method returns the unmodified response from the API call. For more information look at the Models 220 | on [the documentation](https://test.wax.api.atomicassets.io/atomicassets/docs/#/) 221 | 222 | ### RpcApi 223 | 224 | This API only uses native nodeos api calls to fetch data about NFTs. 225 | It is recommended to use the Explorer API for production or applications which require fast load times. 226 | 227 | #### Example 228 | ```javascript 229 | // init RPC Api 230 | // node: standard rpc node which will be used to fetch data (no v1 or v2 history needed) 231 | // contract: account name where the contract is deployed 232 | // options: 233 | // - fetch: either node-fetch module or the browser equivalent 234 | // - rateLimit: defines how much requests per second can be made to not exceed the rate limit of the node 235 | const api = new RpcApi("https://testnet.wax.pink.gg", "atomicassets", {fetch, rateLimit: 4}); 236 | 237 | const asset = await api.getAsset("leonleonleon", "1099511627786"); 238 | 239 | // create the action to mint an asset 240 | const actions = api.action.mintasset( 241 | [{actor: "pink.gg", permission: "active"}], 242 | "collection", "schema", -1, "pink.gg", {"name": "test"}, {"species": "test2"} 243 | ) 244 | ``` 245 | 246 | #### Methods 247 | 248 | Caching can be disabled by explicitly setting cache to false 249 | 250 | `async getAsset(owner: string, id: string, cache: boolean = true): Promise` 251 | 252 | *Gets data about a specific asset owned by owner* 253 | 254 | `async getTemplate(id: string, cache: boolean = true): Promise` 255 | 256 | *Gets a specific template by id* 257 | 258 | `async getSchema(collection: string, name: string, cache: boolean = true): Promise` 259 | 260 | *Get a schema by its name* 261 | 262 | `async getCollection(name: string, cache: boolean = true): Promise` 263 | 264 | *Gets an offer by its id* 265 | 266 | `async getCollectionTemplates(collection: string, cache: boolean = true): Promise` 267 | 268 | *Gets all templates of a collection* 269 | 270 | `async getCollectionSchemas(collection: string, cache: boolean = true): Promise` 271 | 272 | *Gets all schemas of a collection* 273 | 274 | `async getOffer(id: string, cache: boolean = true): Promise` 275 | 276 | *Gets an offer by its id* 277 | 278 | `async getAccountOffers(account: string, cache: boolean = true): Promise` 279 | 280 | *Get all offers which are sent or received by an account* 281 | 282 | `async getAccountAssets(account: string, cache: boolean = true): Promise` 283 | 284 | *Gets the complete inventory of a specific account (may take long for bigger inventories)* 285 | 286 | #### RpcActionGenerator 287 | 288 | The RPC API has an `action` attribute which contains a helper class to construct contract actions 289 | which can be pushed on chain with eosjs. 290 | 291 | Detailed information about each action can be found [here](https://github.com/pink.gg/atomicassets-contract/wiki/Actions) 292 | 293 | #### Types 294 | 295 | These classes represent table rows of the contract and consist of getter methods 296 | which return the deserialized data. 297 | The method `toObject` returns a JavaScript object representation of the class. 298 | 299 | ##### RpcAsset 300 | 301 | `async collection(): Promise` 302 | 303 | `async schema(): Promise` 304 | 305 | `async template(): Promise` 306 | 307 | `async backedTokens(): Promise` 308 | 309 | `async immutableData(): Promise` 310 | 311 | `async mutableData(): Promise` 312 | 313 | `async data(): Promise` 314 | 315 | `async toObject(): Promise` 316 | 317 | 318 | ##### RpcTemplate 319 | 320 | `async collection(): Promise` 321 | 322 | `async schema(): Promise` 323 | 324 | `async immutableData(): Promise` 325 | 326 | `async isTransferable(): Promise` 327 | 328 | `async isBurnable(): Promise` 329 | 330 | `async maxSupply(): Promise` 331 | 332 | `async circulation(): Promise` 333 | 334 | `async toObject(): Promise` 335 | 336 | 337 | ##### RpcSchema 338 | `async collection(): Promise` 339 | 340 | `async format(): Promise` 341 | 342 | `async toObject(): Promise` 343 | 344 | 345 | ##### RpcCollection 346 | `async author(): Promise` 347 | 348 | `async allowNotify(): Promise` 349 | 350 | `async authorizedAccounts(): Promise` 351 | 352 | `async notifyAccounts(): Promise` 353 | 354 | `async marketFee(): Promise` 355 | 356 | `async data(): Promise` 357 | 358 | `async toObject(): Promise` 359 | 360 | 361 | ##### RpcOffer 362 | `async sender(): Promise` 363 | 364 | `async recipient(): Promise` 365 | 366 | `async senderAssets(): Promise>` 367 | 368 | *If element is a string, the asset is not owned by the sender anymore and the offer is invalid* 369 | 370 | `async recipientAssets(): Promise>` 371 | 372 | *If element is a string, the asset is not owned by the recipient anymore and the offer is invalid* 373 | 374 | `async memo(): Promise` 375 | 376 | `async toObject(): Promise` 377 | -------------------------------------------------------------------------------- /dist/atomicassets.js: -------------------------------------------------------------------------------- 1 | var atomicassets;(()=>{var t={751:function(t){"use strict";var e,r,n,i,a,o,s,u,c,l,p,h,f=!1;function d(t,e,r){var n=t[e++],i=t[e++],a=t[e++],o=t[e];return"bige"===r?256*(256*(256*n+i)+a)+o:256*(256*(256*o+a)+i)+n}function y(t,e,r,n){var i=e>>>24&255,a=e>>16&255,o=e>>8&255,s=255&e;"bige"===n?(t[r++]=i,t[r++]=a,t[r++]=o,t[r]=s):(t[r++]=s,t[r++]=o,t[r++]=a,t[r]=i)}function m(t,e,r,n,i){"bige"===i?(y(t,e,n,i),y(t,r,n+4,i)):(y(t,r,n,i),y(t,e,n+4,i))}"function"==typeof Float32Array&&(c=new Float32Array(1),l=new Uint8Array(c.buffer),c[0]=-1,f=0===l[3],e=function(t,e){return(e=e||0)<0||e+4>t.length?0:(l[0]=t[e++],l[1]=t[e++],l[2]=t[e++],l[3]=t[e],c[0])},n=function(t,e){return(e=e||0)<0||e+4>t.length?0:(l[3]=t[e++],l[2]=t[e++],l[1]=t[e++],l[0]=t[e],c[0])},r=function(t,e,r){r=r||0,c[0]=e,t[r++]=l[0],t[r++]=l[1],t[r++]=l[2],t[r]=l[3]},i=function(t,e,r){r=r||0,c[0]=e,t[r++]=l[3],t[r++]=l[2],t[r++]=l[1],t[r]=l[0]}),"function"==typeof Float64Array&&(p=new Float64Array(1),h=new Uint8Array(p.buffer),a=function(t,e){return(e=e||0)<0||e+8>t.length?0:(h[0]=t[e+0],h[1]=t[e+1],h[2]=t[e+2],h[3]=t[e+3],h[4]=t[e+4],h[5]=t[e+5],h[6]=t[e+6],h[7]=t[e+7],p[0])},s=function(t,e){return(e=e||0)<0||e+8>t.length?0:(h[7]=t[e+0],h[6]=t[e+1],h[5]=t[e+2],h[4]=t[e+3],h[3]=t[e+4],h[2]=t[e+5],h[1]=t[e+6],h[0]=t[e+7],p[0])},o=function(t,e,r){r=r||0,p[0]=e,t[r+0]=h[0],t[r+1]=h[1],t[r+2]=h[2],t[r+3]=h[3],t[r+4]=h[4],t[r+5]=h[5],t[r+6]=h[6],t[r+7]=h[7]},u=function(t,e,r){r=r||0,p[0]=e,t[r+0]=h[7],t[r+1]=h[6],t[r+2]=h[5],t[r+3]=h[4],t[r+4]=h[3],t[r+5]=h[2],t[r+6]=h[1],t[r+7]=h[0]});for(var v=new Array,w=0;w<1200;w++)v[w]=Math.pow(2,w);var _=new Array;for(w=0;w<1200;w++)_[w]=Math.pow(2,-w);function g(t){return t>=0?v[t]:_[-t]}function b(t,e,r){var n,i,a=d(t,e,r),o=d(t,e+4,r);"bige"===r?(n=a,i=o):(n=o,i=a);var s=4294967296*(1048575&n)+i,u=(2146435072&n)>>>20;return(n>>31||1)*(0===u?s?s*g(-1074):0:u<2047?s>=0?(1+2220446049250313e-31*s)*g(u-1023):0:s?NaN:1/0)}g(-1023);var A=Math.pow(2,-23),E=Math.pow(2,-127);function M(t,e,r){var n=d(t,e,r),i=8388607&n,a=(2139095040&n)>>>23;return(n>>31||1)*(0===a?i?i*A*2*E:0:a<255?(1+i*A)*g(a-127):i?NaN:1/0)}var O={exp:0,mant:0};function S(t){var e=0;return t>=2?(t*=g(-(e=P(1,t))))>=2&&(t/=2,e+=1):t<1&&((e=P(t,2))<=1023?t*=g(e):(t*=g(e-100),t*=g(100)),e=-e),O.exp=e,O.mant=t,O}var z=Math.pow(2,192);function P(t,e){for(var r=0;t*z=8388608&&(i.mant-=8388608,i.exp+=1)):(i.mant=x(i.mant-1,8388608),i.mant>=8388608&&(i.mant-=8388608,i.exp+=1),i.exp>254&&(i.mant=0,i.exp=255)),y(t,a|i.exp<<23|i.mant,r,n)):y(t,0===e?1/e<0?2147483648:0:e===1/0?2139095040|a:2143289344,r,n)}new Uint8Array(8);var q=Math.pow(2,52);function k(t,e,r,n){var i,a,o,s=0;e<0&&(s=2147483648,e=-e),e&&e<1/0?((i=S(e)).exp+=1023,i.exp<=0?(i.mant*=g(51+i.exp),i.exp=0):i.mant=(i.mant-1)*q,m(t,a=s|i.exp<<20|i.mant/4294967296,o=i.mant>>>0,r,n)):(0===e?(a=1/e<0?2147483648:0,o=0):e===1/0?(a=s+2146435072,o=0):(a=2146959360,o=0),m(t,a,o,r,n))}(function c(){var l=t.exports||this;l.readWord=d,l.writeWord=y,l.writeDoubleWord=m,l.readFloat=M,l.writeFloat=j,l.readDouble=b,l.writeDouble=k,l._useFloatArray=function(t){l._usingFloatArray=t,t?("full"==t&&(l.readFloatLE=f?n:e),l.writeFloatLE=f?i:r,"full"==t&&(l.readFloatBE=f?e:n),l.writeFloatBE=f?r:i,l.readDoubleLE=f?s:a,l.writeDoubleLE=f?u:o,l.readDoubleBE=f?a:s,l.writeDoubleBE=f?o:u):(l._usingFloatArray="",l.readFloatLE=function(t,e){return l.readFloat(t,e||0,"le")},l.writeFloatLE=function(t,e,r){l.writeFloat(t,e,r||0,"le")},l.readFloatBE=function(t,e){return l.readFloat(t,e||0,"bige")},l.writeFloatBE=function(t,e,r){l.writeFloat(t,e,r||0,"bige")},l.readDoubleLE=function(t,e){return l.readDouble(t,e||0,"le")},l.writeDoubleLE=function(t,e,r){l.writeDouble(t,e,r||0,"le")},l.readDoubleBE=function(t,e){return l.readDouble(t,e||0,"bige")},l.writeDoubleBE=function(t,e,r){l.writeDouble(t,e,r||0,"bige")})},l._getBigeCpu=function(){return f},l._setBigeCpu=function(t){f=t},l._useFloatArray(!1),l._useFloatArray(e&&a&&"fastest"),c.prototype=l}).call(this)},736:(t,e,r)=>{var n;t=r.nmd(t);var i=function(t){"use strict";var e=1e7,r=9007199254740992,n=h(r),a="0123456789abcdefghijklmnopqrstuvwxyz",o="function"==typeof BigInt;function s(t,e,r,n){return void 0===t?s[0]:void 0===e||10==+e&&!r?V(t):Z(t,e,r,n)}function u(t,e){this.value=t,this.sign=e,this.isSmall=!1}function c(t){this.value=t,this.sign=t<0,this.isSmall=!0}function l(t){this.value=t}function p(t){return-r0?Math.floor(t):Math.ceil(t)}function v(t,r){var n,i,a=t.length,o=r.length,s=new Array(a),u=0,c=e;for(i=0;i=c?1:0,s[i]=n-u*c;for(;i0&&s.push(u),s}function w(t,e){return t.length>=e.length?v(t,e):v(e,t)}function _(t,r){var n,i,a=t.length,o=new Array(a),s=e;for(i=0;i0;)o[i++]=r%s,r=Math.floor(r/s);return o}function g(t,r){var n,i,a=t.length,o=r.length,s=new Array(a),u=0,c=e;for(n=0;n0;)o[i++]=u%s,u=Math.floor(u/s);return o}function M(t,e){for(var r=[];e-- >0;)r.push(0);return r.concat(t)}function O(t,e){var r=Math.max(t.length,e.length);if(r<=30)return A(t,e);r=Math.ceil(r/2);var n=t.slice(r),i=t.slice(0,r),a=e.slice(r),o=e.slice(0,r),s=O(i,o),u=O(n,a),c=O(w(i,n),w(o,a)),l=w(w(s,M(g(g(c,s),u),r)),M(u,2*r));return d(l),l}function S(t,r,n){return new u(t=0;--r)i=(a=1e7*i+t[r])-(n=m(a/e))*e,s[r]=0|n;return[s,0|i]}function x(t,r){var n,i=V(r);if(o)return[new l(t.value/i.value),new l(t.value%i.value)];var a,p=t.value,v=i.value;if(0===v)throw new Error("Cannot divide by zero");if(t.isSmall)return i.isSmall?[new c(m(p/v)),new c(p%v)]:[s[0],t];if(i.isSmall){if(1===v)return[t,s[0]];if(-1==v)return[t.negate(),s[0]];var w=Math.abs(v);if(w=0;i--){for(n=h-1,w[i+p]!==m&&(n=Math.floor((w[i+p]*h+w[i+p-1])/m)),a=0,o=0,u=_.length,s=0;sc&&(a=(a+1)*h),n=Math.ceil(a/o);do{if(j(s=E(r,n),p)<=0)break;n--}while(n);l.push(n),p=g(p,s)}return l.reverse(),[f(l),f(p)]}(p,v),a=n[0];var A=t.sign!==i.sign,M=n[1],O=t.sign;return"number"==typeof a?(A&&(a=-a),a=new c(a)):a=new u(a,A),"number"==typeof M?(O&&(M=-M),M=new c(M)):M=new u(M,O),[a,M]}function j(t,e){if(t.length!==e.length)return t.length>e.length?1:-1;for(var r=t.length-1;r>=0;r--)if(t[r]!==e[r])return t[r]>e[r]?1:-1;return 0}function q(t){var e=t.abs();return!e.isUnit()&&(!!(e.equals(2)||e.equals(3)||e.equals(5))||!(e.isEven()||e.isDivisibleBy(3)||e.isDivisibleBy(5))&&(!!e.lesser(49)||void 0))}function k(t,e){for(var r,n,a,o=t.prev(),s=o,u=0;s.isEven();)s=s.divide(2),u++;t:for(n=0;n=0?n=g(t,e):(n=g(e,t),r=!r),"number"==typeof(n=f(n))?(r&&(n=-n),new c(n)):new u(n,r)}(r,n,this.sign)},u.prototype.minus=u.prototype.subtract,c.prototype.subtract=function(t){var e=V(t),r=this.value;if(r<0!==e.sign)return this.add(e.negate());var n=e.value;return e.isSmall?new c(r-n):b(n,Math.abs(r),r>=0)},c.prototype.minus=c.prototype.subtract,l.prototype.subtract=function(t){return new l(this.value-V(t).value)},l.prototype.minus=l.prototype.subtract,u.prototype.negate=function(){return new u(this.value,!this.sign)},c.prototype.negate=function(){var t=this.sign,e=new c(-this.value);return e.sign=!t,e},l.prototype.negate=function(){return new l(-this.value)},u.prototype.abs=function(){return new u(this.value,!1)},c.prototype.abs=function(){return new c(Math.abs(this.value))},l.prototype.abs=function(){return new l(this.value>=0?this.value:-this.value)},u.prototype.multiply=function(t){var r,n,i,a=V(t),o=this.value,c=a.value,l=this.sign!==a.sign;if(a.isSmall){if(0===c)return s[0];if(1===c)return this;if(-1===c)return this.negate();if((r=Math.abs(c))0?O(o,c):A(o,c),l)},u.prototype.times=u.prototype.multiply,c.prototype._multiplyBySmall=function(t){return p(t.value*this.value)?new c(t.value*this.value):S(Math.abs(t.value),h(Math.abs(this.value)),this.sign!==t.sign)},u.prototype._multiplyBySmall=function(t){return 0===t.value?s[0]:1===t.value?this:-1===t.value?this.negate():S(Math.abs(t.value),this.value,this.sign!==t.sign)},c.prototype.multiply=function(t){return V(t)._multiplyBySmall(this)},c.prototype.times=c.prototype.multiply,l.prototype.multiply=function(t){return new l(this.value*V(t).value)},l.prototype.times=l.prototype.multiply,u.prototype.square=function(){return new u(z(this.value),!1)},c.prototype.square=function(){var t=this.value*this.value;return p(t)?new c(t):new u(z(h(Math.abs(this.value))),!1)},l.prototype.square=function(t){return new l(this.value*this.value)},u.prototype.divmod=function(t){var e=x(this,t);return{quotient:e[0],remainder:e[1]}},l.prototype.divmod=c.prototype.divmod=u.prototype.divmod,u.prototype.divide=function(t){return x(this,t)[0]},l.prototype.over=l.prototype.divide=function(t){return new l(this.value/V(t).value)},c.prototype.over=c.prototype.divide=u.prototype.over=u.prototype.divide,u.prototype.mod=function(t){return x(this,t)[1]},l.prototype.mod=l.prototype.remainder=function(t){return new l(this.value%V(t).value)},c.prototype.remainder=c.prototype.mod=u.prototype.remainder=u.prototype.mod,u.prototype.pow=function(t){var e,r,n,i=V(t),a=this.value,o=i.value;if(0===o)return s[1];if(0===a)return s[0];if(1===a)return s[1];if(-1===a)return i.isEven()?s[1]:s[-1];if(i.sign)return s[0];if(!i.isSmall)throw new Error("The exponent "+i.toString()+" is too large.");if(this.isSmall&&p(e=Math.pow(a,o)))return new c(m(e));for(r=this,n=s[1];!0&o&&(n=n.times(r),--o),0!==o;)o/=2,r=r.square();return n},c.prototype.pow=u.prototype.pow,l.prototype.pow=function(t){var e=V(t),r=this.value,n=e.value,i=BigInt(0),a=BigInt(1),o=BigInt(2);if(n===i)return s[1];if(r===i)return s[0];if(r===a)return s[1];if(r===BigInt(-1))return e.isEven()?s[1]:s[-1];if(e.isNegative())return new l(i);for(var u=this,c=s[1];(n&a)===a&&(c=c.times(u),--n),n!==i;)n/=o,u=u.square();return c},u.prototype.modPow=function(t,e){if(t=V(t),(e=V(e)).isZero())throw new Error("Cannot take modPow with modulus 0");var r=s[1],n=this.mod(e);for(t.isNegative()&&(t=t.multiply(s[-1]),n=n.modInv(e));t.isPositive();){if(n.isZero())return s[0];t.isOdd()&&(r=r.multiply(n).mod(e)),t=t.divide(2),n=n.square().mod(e)}return r},l.prototype.modPow=c.prototype.modPow=u.prototype.modPow,u.prototype.compareAbs=function(t){var e=V(t),r=this.value,n=e.value;return e.isSmall?1:j(r,n)},c.prototype.compareAbs=function(t){var e=V(t),r=Math.abs(this.value),n=e.value;return e.isSmall?r===(n=Math.abs(n))?0:r>n?1:-1:-1},l.prototype.compareAbs=function(t){var e=this.value,r=V(t).value;return(e=e>=0?e:-e)===(r=r>=0?r:-r)?0:e>r?1:-1},u.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=V(t),r=this.value,n=e.value;return this.sign!==e.sign?e.sign?1:-1:e.isSmall?this.sign?-1:1:j(r,n)*(this.sign?-1:1)},u.prototype.compareTo=u.prototype.compare,c.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=V(t),r=this.value,n=e.value;return e.isSmall?r==n?0:r>n?1:-1:r<0!==e.sign?r<0?-1:1:r<0?1:-1},c.prototype.compareTo=c.prototype.compare,l.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=this.value,r=V(t).value;return e===r?0:e>r?1:-1},l.prototype.compareTo=l.prototype.compare,u.prototype.equals=function(t){return 0===this.compare(t)},l.prototype.eq=l.prototype.equals=c.prototype.eq=c.prototype.equals=u.prototype.eq=u.prototype.equals,u.prototype.notEquals=function(t){return 0!==this.compare(t)},l.prototype.neq=l.prototype.notEquals=c.prototype.neq=c.prototype.notEquals=u.prototype.neq=u.prototype.notEquals,u.prototype.greater=function(t){return this.compare(t)>0},l.prototype.gt=l.prototype.greater=c.prototype.gt=c.prototype.greater=u.prototype.gt=u.prototype.greater,u.prototype.lesser=function(t){return this.compare(t)<0},l.prototype.lt=l.prototype.lesser=c.prototype.lt=c.prototype.lesser=u.prototype.lt=u.prototype.lesser,u.prototype.greaterOrEquals=function(t){return this.compare(t)>=0},l.prototype.geq=l.prototype.greaterOrEquals=c.prototype.geq=c.prototype.greaterOrEquals=u.prototype.geq=u.prototype.greaterOrEquals,u.prototype.lesserOrEquals=function(t){return this.compare(t)<=0},l.prototype.leq=l.prototype.lesserOrEquals=c.prototype.leq=c.prototype.lesserOrEquals=u.prototype.leq=u.prototype.lesserOrEquals,u.prototype.isEven=function(){return 0==(1&this.value[0])},c.prototype.isEven=function(){return 0==(1&this.value)},l.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)},u.prototype.isOdd=function(){return 1==(1&this.value[0])},c.prototype.isOdd=function(){return 1==(1&this.value)},l.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)},u.prototype.isPositive=function(){return!this.sign},c.prototype.isPositive=function(){return this.value>0},l.prototype.isPositive=c.prototype.isPositive,u.prototype.isNegative=function(){return this.sign},c.prototype.isNegative=function(){return this.value<0},l.prototype.isNegative=c.prototype.isNegative,u.prototype.isUnit=function(){return!1},c.prototype.isUnit=function(){return 1===Math.abs(this.value)},l.prototype.isUnit=function(){return this.abs().value===BigInt(1)},u.prototype.isZero=function(){return!1},c.prototype.isZero=function(){return 0===this.value},l.prototype.isZero=function(){return this.value===BigInt(0)},u.prototype.isDivisibleBy=function(t){var e=V(t);return!e.isZero()&&(!!e.isUnit()||(0===e.compareAbs(2)?this.isEven():this.mod(e).isZero()))},l.prototype.isDivisibleBy=c.prototype.isDivisibleBy=u.prototype.isDivisibleBy,u.prototype.isPrime=function(e){var r=q(this);if(r!==t)return r;var n=this.abs(),a=n.bitLength();if(a<=64)return k(n,[2,3,5,7,11,13,17,19,23,29,31,37]);for(var o=Math.log(2)*a.toJSNumber(),s=Math.ceil(!0===e?2*Math.pow(o,2):o),u=[],c=0;c-r?new c(t-1):new u(n,!0)},l.prototype.prev=function(){return new l(this.value-BigInt(1))};for(var D=[1];2*D[D.length-1]<=e;)D.push(2*D[D.length-1]);var N=D.length,B=D[N-1];function T(t){return Math.abs(t)<=e}function C(t,e,r){e=V(e);for(var n=t.isNegative(),a=e.isNegative(),o=n?t.not():t,s=a?e.not():e,u=0,c=0,l=null,p=null,h=[];!o.isZero()||!s.isZero();)u=(l=x(o,B))[1].toJSNumber(),n&&(u=B-1-u),c=(p=x(s,B))[1].toJSNumber(),a&&(c=B-1-c),o=l[0],s=p[0],h.push(r(u,c));for(var f=0!==r(n?1:0,a?1:0)?i(-1):i(0),d=h.length-1;d>=0;d-=1)f=f.multiply(B).add(i(h[d]));return f}u.prototype.shiftLeft=function(t){var e=V(t).toJSNumber();if(!T(e))throw new Error(String(e)+" is too large for shifting.");if(e<0)return this.shiftRight(-e);var r=this;if(r.isZero())return r;for(;e>=N;)r=r.multiply(B),e-=N-1;return r.multiply(D[e])},l.prototype.shiftLeft=c.prototype.shiftLeft=u.prototype.shiftLeft,u.prototype.shiftRight=function(t){var e,r=V(t).toJSNumber();if(!T(r))throw new Error(String(r)+" is too large for shifting.");if(r<0)return this.shiftLeft(-r);for(var n=this;r>=N;){if(n.isZero()||n.isNegative()&&n.isUnit())return n;n=(e=x(n,B))[1].isNegative()?e[0].prev():e[0],r-=N-1}return(e=x(n,D[r]))[1].isNegative()?e[0].prev():e[0]},l.prototype.shiftRight=c.prototype.shiftRight=u.prototype.shiftRight,u.prototype.not=function(){return this.negate().prev()},l.prototype.not=c.prototype.not=u.prototype.not,u.prototype.and=function(t){return C(this,t,(function(t,e){return t&e}))},l.prototype.and=c.prototype.and=u.prototype.and,u.prototype.or=function(t){return C(this,t,(function(t,e){return t|e}))},l.prototype.or=c.prototype.or=u.prototype.or,u.prototype.xor=function(t){return C(this,t,(function(t,e){return t^e}))},l.prototype.xor=c.prototype.xor=u.prototype.xor;var F=1<<30;function L(t){var r=t.value,n="number"==typeof r?r|F:"bigint"==typeof r?r|BigInt(F):r[0]+r[1]*e|1073758208;return n&-n}function U(t,e){if(e.compareTo(t)<=0){var r=U(t,e.square(e)),n=r.p,a=r.e,o=n.multiply(e);return o.compareTo(t)<=0?{p:o,e:2*a+1}:{p:n,e:2*a}}return{p:i(1),e:0}}function I(t,e){return t=V(t),e=V(e),t.greater(e)?t:e}function J(t,e){return t=V(t),e=V(e),t.lesser(e)?t:e}function R(t,e){if(t=V(t).abs(),e=V(e).abs(),t.equals(e))return t;if(t.isZero())return e;if(e.isZero())return t;for(var r,n,i=s[1];t.isEven()&&e.isEven();)r=J(L(t),L(e)),t=t.divide(r),e=e.divide(r),i=i.multiply(r);for(;t.isEven();)t=t.divide(L(t));do{for(;e.isEven();)e=e.divide(L(e));t.greater(e)&&(n=e,e=t,t=n),e=e.subtract(t)}while(!e.isZero());return i.isUnit()?t:t.multiply(i)}u.prototype.bitLength=function(){var t=this;return t.compareTo(i(0))<0&&(t=t.negate().subtract(i(1))),0===t.compareTo(i(0))?i(0):i(U(t,i(2)).e).add(i(1))},l.prototype.bitLength=c.prototype.bitLength=u.prototype.bitLength;var Z=function(t,e,r,n){r=r||a,t=String(t),n||(t=t.toLowerCase(),r=r.toLowerCase());var i,o=t.length,s=Math.abs(e),u={};for(i=0;i=s){if("1"===p&&1===s)continue;throw new Error(p+" is not a valid digit in base "+e+".")}e=V(e);var c=[],l="-"===t[0];for(i=l?1:0;i"!==t[i]&&i=0;n--)i=i.add(t[n].times(a)),a=a.times(e);return r?i.negate():i}function W(t,e){if((e=i(e)).isZero()){if(t.isZero())return{value:[0],isNegative:!1};throw new Error("Cannot convert nonzero numbers to base 0.")}if(e.equals(-1)){if(t.isZero())return{value:[0],isNegative:!1};if(t.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-t.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:!1};var r=Array.apply(null,Array(t.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);return r.unshift([1]),{value:[].concat.apply([],r),isNegative:!1}}var n=!1;if(t.isNegative()&&e.isPositive()&&(n=!0,t=t.abs()),e.isUnit())return t.isZero()?{value:[0],isNegative:!1}:{value:Array.apply(null,Array(t.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:n};for(var a,o=[],s=t;s.isNegative()||s.compareAbs(e)>=0;){a=s.divmod(e),s=a.quotient;var u=a.remainder;u.isNegative()&&(u=e.minus(u).abs(),s=s.next()),o.push(u.toJSNumber())}return o.push(s.toJSNumber()),{value:o.reverse(),isNegative:n}}function H(t,e,r){var n=W(t,e);return(n.isNegative?"-":"")+n.value.map((function(t){return function(t,e){return t<(e=e||a).length?e[t]:"<"+t+">"}(t,r)})).join("")}function $(t){if(p(+t)){var e=+t;if(e===m(e))return o?new l(BigInt(e)):new c(e);throw new Error("Invalid integer: "+t)}var r="-"===t[0];r&&(t=t.slice(1));var n=t.split(/e/i);if(n.length>2)throw new Error("Invalid integer: "+n.join("e"));if(2===n.length){var i=n[1];if("+"===i[0]&&(i=i.slice(1)),(i=+i)!==m(i)||!p(i))throw new Error("Invalid integer: "+i+" is not a valid exponent.");var a=n[0],s=a.indexOf(".");if(s>=0&&(i-=a.length-s-1,a=a.slice(0,s)+a.slice(s+1)),i<0)throw new Error("Cannot include negative exponent part for integers");t=a+=new Array(i+1).join("0")}if(!/^([0-9][0-9]*)$/.test(t))throw new Error("Invalid integer: "+t);if(o)return new l(BigInt(r?"-"+t:t));for(var h=[],f=t.length,y=f-7;f>0;)h.push(+t.slice(y,f)),(y-=7)<0&&(y=0),f-=7;return d(h),new u(h,r)}function V(t){return"number"==typeof t?function(t){if(o)return new l(BigInt(t));if(p(t)){if(t!==m(t))throw new Error(t+" is not an integer.");return new c(t)}return $(t.toString())}(t):"string"==typeof t?$(t):"bigint"==typeof t?new l(t):t}u.prototype.toArray=function(t){return W(this,t)},c.prototype.toArray=function(t){return W(this,t)},l.prototype.toArray=function(t){return W(this,t)},u.prototype.toString=function(e,r){if(e===t&&(e=10),10!==e)return H(this,e,r);for(var n,i=this.value,a=i.length,o=String(i[--a]);--a>=0;)n=String(i[a]),o+="0000000".slice(n.length)+n;return(this.sign?"-":"")+o},c.prototype.toString=function(e,r){return e===t&&(e=10),10!=e?H(this,e,r):String(this.value)},l.prototype.toString=c.prototype.toString,l.prototype.toJSON=u.prototype.toJSON=c.prototype.toJSON=function(){return this.toString()},u.prototype.valueOf=function(){return parseInt(this.toString(),10)},u.prototype.toJSNumber=u.prototype.valueOf,c.prototype.valueOf=function(){return this.value},c.prototype.toJSNumber=c.prototype.valueOf,l.prototype.valueOf=l.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};for(var K=0;K<1e3;K++)s[K]=V(K),K>0&&(s[-K]=V(-K));return s.one=s[1],s.zero=s[0],s.minusOne=s[-1],s.max=I,s.min=J,s.gcd=R,s.lcm=function(t,e){return t=V(t).abs(),e=V(e).abs(),t.divide(R(t,e)).multiply(e)},s.isInstance=function(t){return t instanceof u||t instanceof c||t instanceof l},s.randBetween=function(t,r,n){t=V(t),r=V(r);var i=n||Math.random,a=J(t,r),o=I(t,r).subtract(a).add(1);if(o.isSmall)return a.add(Math.floor(i()*o));for(var u=W(o,e).value,c=[],l=!0,p=0;pnew i.default((await this.getConfig()).contract,this))()}async getConfig(){return await this.fetchEndpoint("/v1/config",{})}async getAssets(t={},e=1,r=100,n=[]){return await this.fetchEndpoint("/v1/assets",Object.assign({page:e,limit:r},o(t,n)))}async countAssets(t,e=[]){return await this.countEndpoint("/v1/assets",o(t,e))}async getAsset(t){return await this.fetchEndpoint("/v1/assets/"+t,{})}async getAssetStats(t){return await this.fetchEndpoint("/v1/assets/"+t+"/stats",{})}async getAssetLogs(t,e=1,r=100,n="desc"){return await this.fetchEndpoint("/v1/assets/"+t+"/logs",{page:e,limit:r,order:n})}async getCollections(t={},e=1,r=100){return await this.fetchEndpoint("/v1/collections",Object.assign({page:e,limit:r},t))}async countCollections(t={}){return await this.countEndpoint("/v1/collections",t)}async getCollection(t){return await this.fetchEndpoint("/v1/collections/"+t,{})}async getCollectionStats(t){return await this.fetchEndpoint("/v1/collections/"+t+"/stats",{})}async getCollectionLogs(t,e=1,r=100,n="desc"){return await this.fetchEndpoint("/v1/collections/"+t+"/logs",{page:e,limit:r,order:n})}async getSchemas(t={},e=1,r=100){return await this.fetchEndpoint("/v1/schemas",Object.assign({page:e,limit:r},t))}async countSchemas(t={}){return await this.countEndpoint("/v1/schemas",t)}async getSchema(t,e){return await this.fetchEndpoint("/v1/schemas/"+t+"/"+e,{})}async getSchemaStats(t,e){return await this.fetchEndpoint("/v1/schemas/"+t+"/"+e+"/stats",{})}async getSchemaLogs(t,e,r=1,n=100,i="desc"){return await this.fetchEndpoint("/v1/schemas/"+t+"/"+e+"/logs",{page:r,limit:n,order:i})}async getTemplates(t={},e=1,r=100,n=[]){return await this.fetchEndpoint("/v1/templates",Object.assign({page:e,limit:r},o(t,n)))}async countTemplates(t={},e=[]){return await this.countEndpoint("/v1/templates",o(t,e))}async getTemplate(t,e){return await this.fetchEndpoint("/v1/templates/"+t+"/"+e,{})}async getTemplateStats(t,e){return await this.fetchEndpoint("/v1/templates/"+t+"/"+e+"/stats",{})}async getTemplateLogs(t,e,r=1,n=100,i="desc"){return await this.fetchEndpoint("/v1/templates/"+t+"/"+e+"/logs",{page:r,limit:n,order:i})}async getTransfers(t={},e=1,r=100){return await this.fetchEndpoint("/v1/transfers",Object.assign({page:e,limit:r},t))}async countTransfers(t={}){return await this.countEndpoint("/v1/transfers",t)}async getOffers(t={},e=1,r=100){return await this.fetchEndpoint("/v1/offers",Object.assign({page:e,limit:r},t))}async countOffers(t={}){return await this.countEndpoint("/v1/offers",t)}async getOffer(t){return await this.fetchEndpoint("/v1/offers/"+t,{})}async getAccounts(t={},e=1,r=100){return await this.fetchEndpoint("/v1/accounts",Object.assign({page:e,limit:r},t))}async getBurns(t={},e=1,r=100){return await this.fetchEndpoint("/v1/burns",Object.assign({page:e,limit:r},t))}async countAccounts(t={}){return await this.countEndpoint("/v1/accounts",t)}async getAccount(t,e={}){return await this.fetchEndpoint("/v1/accounts/"+t,e)}async getAccountCollection(t,e){return await this.fetchEndpoint("/v1/accounts/"+t+"/"+e,{})}async getAccountBurns(t,e={}){return await this.fetchEndpoint("/v1/burns/"+t,e)}async fetchEndpoint(t,e){let r,n;const i=this.fetchBuiltin,o=Object.keys(e).map((t=>{let r=e[t];return!0===r&&(r="true"),!1===r&&(r="false"),t+"="+encodeURIComponent(r)})).join("&");try{r=o.length<1e3?await i(this.endpoint+"/"+this.namespace+t+(o.length>0?"?"+o:"")):await i(this.endpoint+"/"+this.namespace+t,{headers:{accept:"*.*","content-type":"application/json"},method:"POST",body:JSON.stringify(e)}),n=await r.json()}catch(t){throw new a.default(t.message,500)}if(200!==r.status)throw new a.default(n.message,r.status);if(!n.success)throw new a.default(n.message,r.status);return n.data}async countEndpoint(t,e){const r=await this.fetchEndpoint(t+"/_count",e);return parseInt(r,10)}}},509:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=r(85),a=n(r(147)),o=n(r(909)),s=n(r(162));e.default=class{constructor(t,e,r,n,i,u,c,l=!0){this.api=t,this.owner=e,this.id=r,this._data=new Promise((async(i,a)=>{if(n)i(n);else try{i(await t.queue.fetchAsset(e,r,l))}catch(t){a(t)}})),this._template=new Promise((async(e,r)=>{if(c)e(c);else try{const r=await this._data;if(Number(r.template_id)<0)return e(null);e(new s.default(t,r.collection_name,r.template_id,void 0,void 0,l))}catch(t){r(t)}})),this._collection=new Promise((async(e,r)=>{if(i)e(i);else try{const r=await this._data;e(new a.default(t,r.collection_name,void 0,l))}catch(t){r(t)}})),this._schema=new Promise((async(e,r)=>{if(u)e(u);else try{const r=await this._data;e(new o.default(t,r.collection_name,r.schema_name,void 0,l))}catch(t){r(t)}}))}async template(){return await this._template}async collection(){return await this._collection}async schema(){return await this._schema}async backedTokens(){return(await this._data).backed_tokens}async immutableData(){const t=await this.schema(),e=await this._data;return(0,i.deserialize)(e.immutable_serialized_data,await t.format())}async mutableData(){const t=await this.schema(),e=await this._data;return(0,i.deserialize)(e.mutable_serialized_data,await t.format())}async data(){const t=await this.mutableData(),e=await this.immutableData(),r=await this.template(),n=r?await r.immutableData():{};return Object.assign({},t,e,n)}async toObject(){const t=await this.template(),e=await this.collection(),r=await this.schema();return{asset_id:this.id,collection:await e.toObject(),schema:await r.toObject(),template:t?await t.toObject():null,backedTokens:await this.backedTokens(),immutableData:await this.immutableData(),mutableData:await this.mutableData(),data:await this.data()}}}},43:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.MemoryCache=void 0,e.MemoryCache=class{constructor(){this.store={}}get(t){const e=this.store[t];if(void 0===e)return null;const[r,n]=e;return new Date>n?(delete this.store[t],null):r}put(t,e,r){const n=new Date(Date.now()+r);this.store[t]=[e,n]}remove(t){delete this.store[t]}}},147:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(577),i=r(85);e.default=class{constructor(t,e,r,n=!0){this.api=t,this.name=e,this._data=new Promise((async(i,a)=>{if(r)i(r);else try{i(await t.queue.fetchCollection(e,n))}catch(t){a(t)}}))}async author(){return(await this._data).author}async allowNotify(){return(await this._data).allow_notify}async authorizedAccounts(){return(await this._data).authorized_accounts}async notifyAccounts(){return(await this._data).notify_accounts}async marketFee(){return Number((await this._data).market_fee)}async data(){return(0,i.deserialize)((await this._data).serialized_data,(0,n.ObjectSchema)((await this.api.config()).collection_format))}async toObject(){return{collection_name:this.name,author:await this.author(),allowNotify:await this.allowNotify(),authorizedAccounts:await this.authorizedAccounts(),notifyAccounts:await this.notifyAccounts(),marketFee:await this.marketFee(),data:await this.data()}}}},715:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(509));e.default=class{constructor(t,e,r,n,a,o=!0){this.api=t,this.id=e,this._data=new Promise((async(t,n)=>{if(r)t(r);else try{t(await this.api.queue.fetchOffer(e,o))}catch(t){n(t)}})),this._senderAssets=new Promise((async(t,e)=>{if(n)t(n);else try{const e=await this._data,r=await this.api.queue.fetchAccountAssets(e.sender);return t(e.sender_asset_ids.map((t=>{const n=r.find((e=>e.asset_id===t));return n?new i.default(this.api,e.sender,t,n,void 0,void 0,void 0,o):t})))}catch(t){return e(t)}})),this._recipientAssets=new Promise((async(t,e)=>{if(a)t(a);else try{const e=await this._data,r=await this.api.queue.fetchAccountAssets(e.recipient);return t(e.recipient_asset_ids.map((t=>{const n=r.find((e=>e.asset_id===t));return n?new i.default(this.api,e.recipient,t,n,void 0,void 0,void 0,o):t})))}catch(t){return e(t)}}))}async sender(){return(await this._data).sender}async recipient(){return(await this._data).recipient}async senderAssets(){return await this._senderAssets}async recipientAssets(){return await this._recipientAssets}async memo(){return(await this._data).memo}async toObject(){return{offer_id:this.id,sender:{account:await this.sender(),assets:await Promise.all((await this.senderAssets()).map((async t=>"string"==typeof t?t:await t.toObject())))},recipient:{account:await this.recipient(),assets:await Promise.all((await this.recipientAssets()).map((async t=>"string"==typeof t?t:await t.toObject())))},memo:await this.memo()}}}},141:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=class{constructor(t,e=4){this.api=t,this.requestLimit=e,this.elements=[],this.interval=null,this.preloadedCollections={}}async fetchAsset(t,e,r=!0){return await this.fetch_single_row("assets",t,e,(t=>r||void 0!==t?this.api.cache.getAsset(e,t):null))}async fetchAccountAssets(t){return(await this.fetch_all_rows("assets",t,"asset_id")).map((t=>this.api.cache.getAsset(t.asset_id,t)))}async fetchTemplate(t,e,r=!0){return await this.fetch_single_row("templates",t,e,(n=>r||void 0!==n?this.api.cache.getTemplate(t,e,n):null))}async fetchSchema(t,e,r=!0){return await this.fetch_single_row("schemas",t,e,(n=>r||void 0!==n?this.api.cache.getSchema(t,e,n):null))}async fetchCollection(t,e=!0){return await this.fetch_single_row("collections",this.api.contract,t,(r=>e||void 0!==r?this.api.cache.getCollection(t,r):null))}async fetchCollectionSchemas(t){return(await this.fetch_all_rows("schemas",t,"schema_name")).map((e=>this.api.cache.getSchema(t,e.schema_name,e)))}async fetchCollectionTemplates(t){return(await this.fetch_all_rows("templates",t,"template_id")).map((e=>this.api.cache.getTemplate(t,String(e.template_id),e)))}async preloadCollection(t,e=!0){(!e||!this.preloadedCollections[t]||this.preloadedCollections[t]+9e5e||void 0!==r?this.api.cache.getOffer(t,r):null))}async fetchAccountOffers(t){const e=await Promise.all([this.fetch_all_rows("offers",this.api.contract,"offer_sender",t,t,2,"name"),this.fetch_all_rows("offers",this.api.contract,"offer_recipient",t,t,3,"name")]);return e[0].concat(e[1]).map((t=>this.api.cache.getOffer(t.offer_id,t)))}dequeue(){this.interval||(this.interval=setInterval((async()=>{this.elements.length>0?this.elements.shift()():(clearInterval(this.interval),this.interval=null)}),Math.ceil(1e3/this.requestLimit)))}async fetch_single_row(t,e,r,n,i=1,a=""){return new Promise(((o,s)=>{let u=n();if(null!==u)return o(u);this.elements.push((async()=>{if(u=n(),null!==u)return o(u);try{const u={code:this.api.contract,table:t,scope:e,limit:1,lower_bound:r,upper_bound:r,index_position:i,key_type:a},c=await this.api.getTableRows(u);return 0===c.rows.length?s(new Error("Row not found for "+JSON.stringify(u))):o(n(c.rows[0]))}catch(t){return s(t)}})),this.dequeue()}))}async fetch_all_rows(t,e,r,n="",i="",a=1,o=""){return new Promise((async(s,u)=>{this.elements.push((async()=>{const c=await this.api.getTableRows({code:this.api.contract,scope:e,table:t,lower_bound:n,upper_bound:i,limit:1e3,index_position:a,key_type:o});c.more&&1===a?(this.elements.unshift((async()=>{try{const n=await this.fetch_all_rows(t,e,r,c.rows[c.rows.length-1][r],i,a,o);n.length>0&&n.shift(),s(c.rows.concat(n))}catch(t){u(t)}})),this.dequeue()):s(c.rows)})),this.dequeue()}))}}},869:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(43);e.default=class{constructor(){this.cache=new n.MemoryCache}getAsset(t,e){return e&&(e.mutable_serialized_data=new Uint8Array(e.mutable_serialized_data),e.immutable_serialized_data=new Uint8Array(e.immutable_serialized_data)),this.access("assets",t,e)}deleteAsset(t){this.delete("assets",t)}getTemplate(t,e,r){return r&&(r.immutable_serialized_data=new Uint8Array(r.immutable_serialized_data)),this.access("templates",t+":"+e,r)}deleteTemplate(t,e){this.delete("templates",t+":"+e)}getSchema(t,e,r){return this.access("schemas",t+":"+e,r)}deleteSchema(t,e){this.delete("schemas",t+":"+e)}getCollection(t,e){return this.access("collections",t,e)}deleteCollection(t){this.delete("collections",t)}getOffer(t,e){return this.access("offers",t,e)}deleteOffer(t){this.delete("offers",t)}access(t,e,r){if(void 0===r){const r=this.cache.get(t+":"+e);return null===r?null:r}return this.cache.put(t+":"+e,r,9e5),r}delete(t,e){this.cache.remove(t+":"+e)}}},909:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=r(577),a=n(r(147));e.default=class{constructor(t,e,r,n,i=!0){this.api=t,this.collection=e,this.name=r,this._data=new Promise((async(a,o)=>{if(n)a(n);else try{a(await t.queue.fetchSchema(e,r,i))}catch(t){o(t)}})),this._collection=new Promise((async(r,n)=>{try{r(new a.default(t,e,void 0,i))}catch(t){n(t)}}))}async format(){return(0,i.ObjectSchema)((await this._data).format)}async rawFormat(){return(await this._data).format}async toObject(){return{collection_name:this.collection,schema_name:this.name,format:await this.rawFormat()}}}},162:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=r(85),a=n(r(909));e.default=class{constructor(t,e,r,n,i,o=!0){this.api=t,this.collection=e,this.id=r,this._data=new Promise((async(i,a)=>{if(n)i(n);else try{i(await t.queue.fetchTemplate(e,r,o))}catch(t){a(t)}})),this._schema=new Promise((async(t,r)=>{if(i)t(i);else try{const r=await this._data;t(new a.default(this.api,e,r.schema_name,void 0,o))}catch(t){r(t)}}))}async schema(){return await this._schema}async immutableData(){const t=await this._schema;return(0,i.deserialize)((await this._data).immutable_serialized_data,await t.format())}async isTransferable(){return(await this._data).transferable}async isBurnable(){return(await this._data).burnable}async maxSupply(){return(await this._data).max_supply}async circulation(){return(await this._data).issued_supply}async toObject(){return{collection_name:this.collection,template_id:this.id,schema:await(await this.schema()).toObject(),immutableData:await this.immutableData(),transferable:await this.isTransferable(),burnable:await this.isBurnable(),maxSupply:await this.maxSupply(),circulation:await this.circulation()}}}},707:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(700)),a=n(r(116)),o=n(r(509)),s=n(r(869)),u=n(r(147)),c=n(r(715)),l=n(r(141)),p=n(r(909)),h=n(r(162));e.default=class{constructor(t,e,n={rateLimit:4}){this.endpoint=t,this.contract=e,n.fetch?this.fetchBuiltin=n.fetch:this.fetchBuiltin=r.g.fetch,this.queue=new l.default(this,n.rateLimit),this.cache=new s.default,this.action=new i.default(this),this._config=new Promise((async(t,e)=>{try{const r=await this.getTableRows({code:this.contract,scope:this.contract,table:"config"});return 1!==r.rows.length?e("invalid config"):t(r.rows[0])}catch(t){e(t)}}))}async config(){return await this._config}async getAsset(t,e,r=!0){r||this.cache.deleteAsset(e);const n=await this.queue.fetchAsset(t,e,r);return new o.default(this,t,e,n,void 0,void 0,void 0,r)}async getTemplate(t,e,r=!0){r||this.cache.deleteTemplate(t,e);const n=await this.queue.fetchTemplate(t,e,r);return new h.default(this,t,e,n,void 0,r)}async getCollection(t,e=!0){e||this.cache.deleteCollection(t);const r=await this.queue.fetchCollection(t,e);return new u.default(this,t,r,e)}async getCollectionTemplates(t){return(await this.queue.fetchCollectionTemplates(t)).map((e=>new h.default(this,t,String(e.template_id),e,void 0)))}async getCollectionsSchemas(t){return(await this.queue.fetchCollectionSchemas(t)).map((e=>new p.default(this,t,e.schema_name,void 0)))}async getSchema(t,e,r=!0){r||this.cache.deleteSchema(t,e);const n=await this.queue.fetchSchema(t,e,r);return new p.default(this,t,e,n,r)}async getOffer(t,e=!0){e||this.cache.deleteOffer(t);const r=await this.queue.fetchOffer(t,e);return new c.default(this,t,r,void 0,void 0,e)}async getAccountOffers(t){return(await this.queue.fetchAccountOffers(t)).map((t=>new c.default(this,t.offer_id,t,void 0,void 0)))}async getAccountAssets(t){return(await this.queue.fetchAccountAssets(t)).map((e=>new o.default(this,t,e.asset_id,e,void 0,void 0,void 0)))}async getCollectionInventory(t,e){return await this.queue.preloadCollection(t,!0),(await this.queue.fetchAccountAssets(e)).filter((e=>e.collection_name===t)).map((t=>new o.default(this,e,t.asset_id,t,void 0,void 0,void 0)))}async preloadCollection(t,e=!0){await this.queue.preloadCollection(t,e)}async getTableRows({code:t,scope:e,table:r,table_key:n="",lower_bound:i="",upper_bound:a="",index_position:o=1,key_type:s=""}){return await this.fetchRpc("/v1/chain/get_table_rows",{code:t,scope:e,table:r,table_key:n,lower_bound:i,upper_bound:a,index_position:o,key_type:s,limit:101,reverse:!1,show_payer:!1,json:!0})}async fetchRpc(t,e){let r,n;try{const i=this.fetchBuiltin;r=await i(this.endpoint+t,{body:JSON.stringify(e),method:"POST"}),n=await r.json()}catch(t){throw t.isFetchError=!0,t}if(n.processed&&n.processed.except||!r.ok)throw new a.default(n);return n}}},311:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(135);class i extends n.ActionGenerator{constructor(t,e){super(t),this.api=e,this.config=e.getConfig()}async createcol(t,e,r,i,a,o,s,u){return super.createcol(t,e,r,i,a,o,s,(0,n.toAttributeMap)(u,(await this.config).collection_format))}async createtempl(t,e,r,i,a,o,s,u){const c=await this.api.getSchema(r,i),l=(0,n.toAttributeMap)(u,c.format);return super.createtempl(t,e,r,i,a,o,s,l)}async mintasset(t,e,r,i,a,o,s,u,c){const l=await this.api.getSchema(r,i),p=(0,n.toAttributeMap)(s,l.format),h=(0,n.toAttributeMap)(u,l.format);return super.mintasset(t,e,r,i,a,o,p,h,c)}async setassetdata(t,e,r,i,a){const o=await this.api.getAsset(i),s=(0,n.toAttributeMap)(a,o.schema.format);return super.setassetdata(t,e,r,i,s)}async setcoldata(t,e,r){const i=(0,n.toAttributeMap)(r,(await this.config).collection_format);return super.setcoldata(t,e,i)}}e.default=i},135:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.toAttributeMap=e.ActionGenerator=void 0;const i=n(r(72));e.ActionGenerator=class{constructor(t){this.contract=t}async acceptoffer(t,e){return this._pack(t,"acceptoffer",{offer_id:e})}async addcolauth(t,e,r){return this._pack(t,"addcolauth",{collection_name:e,account_to_add:r})}async addconftoken(t,e,r){return this._pack(t,"addconftoken",{token_contract:e,token_symbol:r})}async addnotifyacc(t,e,r){return this._pack(t,"addnotifyacc",{collection_name:e,account_to_add:r})}async announcedepo(t,e,r){return this._pack(t,"announcedepo",{owner:e,symbol_to_announce:r})}async backasset(t,e,r,n,i){return this._pack(t,"backasset",{payer:e,asset_owner:r,asset_id:n,token_to_back:i})}async burnasset(t,e,r){return this._pack(t,"burnasset",{asset_owner:e,asset_id:r})}async canceloffer(t,e){return this._pack(t,"canceloffer",{offer_id:e})}async createcol(t,e,r,n,i,a,o,s){return this._pack(t,"createcol",{author:e,collection_name:r,allow_notify:n,authorized_accounts:i,notify_accounts:a,market_fee:o,data:s})}async createoffer(t,e,r,n,i,a){return this._pack(t,"createoffer",{sender:e,recipient:r,sender_asset_ids:n,recipient_asset_ids:i,memo:a})}async createtempl(t,e,r,n,i,a,o,s){return this._pack(t,"createtempl",{authorized_creator:e,collection_name:r,schema_name:n,transferable:i,burnable:a,max_supply:o,immutable_data:s})}async createschema(t,e,r,n,i){return this._pack(t,"createschema",{authorized_creator:e,collection_name:r,schema_name:n,schema_format:i})}async declineoffer(t,e){return this._pack(t,"declineoffer",{offer_id:e})}async extendschema(t,e,r,n,i){return this._pack(t,"extendschema",{authorized_editor:e,collection_name:r,schema_name:n,schema_format_extension:i})}async forbidnotify(t,e){return this._pack(t,"forbidnotify",{collection_name:e})}async locktemplate(t,e,r,n){return this._pack(t,"locktemplate",{authorized_editor:e,collection_name:r,template_id:n})}async mintasset(t,e,r,n,i,a,o,s,u){return this._pack(t,"mintasset",{authorized_minter:e,collection_name:r,schema_name:n,template_id:i,new_asset_owner:a,immutable_data:o,mutable_data:s,tokens_to_back:u})}async payofferram(t,e,r){return this._pack(t,"payofferram",{payer:e,offer_id:r})}async remcolauth(t,e,r){return this._pack(t,"remcolauth",{collection_name:e,account_to_remove:r})}async remnotifyacc(t,e,r){return this._pack(t,"remnotifyacc",{collection_name:e,account_to_remove:r})}async setassetdata(t,e,r,n,i){return this._pack(t,"setassetdata",{authorized_editor:e,asset_owner:r,asset_id:n,new_mutable_data:i})}async setcoldata(t,e,r){return this._pack(t,"setcoldata",{collection_name:e,data:r})}async setmarketfee(t,e,r){return this._pack(t,"setmarketfee",{collection_name:e,market_fee:r})}async transfer(t,e,r,n,i){return this._pack(t,"transfer",{from:e,to:r,asset_ids:n,memo:i})}async withdraw(t,e,r){return this._pack(t,"withdraw",{owner:e,token_to_withdraw:r})}_pack(t,e,r){return[{account:this.contract,name:e,authorization:t,data:r}]}},e.toAttributeMap=function(t,e){const r={},n=[];for(const t of e)r[t.name]=t.type;const a=Object.keys(t);for(const e of a){if(void 0===r[e])throw new i.default("field not defined in schema");n.push({key:e,value:[r[e],t[e]]})}return n}},700:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(135);class i extends n.ActionGenerator{constructor(t){super(t.contract),this.api=t}async createcol(t,e,r,i,a,o,s,u){const c=await this.api.config();return super.createcol(t,e,r,i,a,o,s,(0,n.toAttributeMap)(u,c.collection_format))}async createtempl(t,e,r,i,a,o,s,u){const c=await this.api.getSchema(r,i),l=(0,n.toAttributeMap)(u,await c.rawFormat());return super.createtempl(t,e,r,i,a,o,s,l)}async mintasset(t,e,r,i,a,o,s,u,c){const l=await this.api.getTemplate(r,a),p=(0,n.toAttributeMap)(s,await(await l.schema()).rawFormat()),h=(0,n.toAttributeMap)(u,await(await l.schema()).rawFormat());return super.mintasset(t,e,r,i,a,o,p,h,c)}async setassetdata(t,e,r,i,a){const o=await this.api.getAsset(r,i),s=await o.schema(),u=(0,n.toAttributeMap)(a,await s.rawFormat());return super.setassetdata(t,e,r,i,u)}async setcoldata(t,e,r){const i=(0,n.toAttributeMap)(r,(await this.api.config()).collection_format);return super.setcoldata(t,e,i)}}e.default=i},843:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});class r extends Error{constructor(t,e){super(t),this.message=t,this.status=e,this.isApiError=!0}}e.default=r},528:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});class r extends Error{}e.default=r},116:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});class r extends Error{constructor(t){t.error&&t.error.details&&t.error.details.length&&t.error.details[0].message?super(t.error.details[0].message):t.processed&&t.processed.except&&t.processed.except.message?super(t.processed.except.message):super(t.message),this.json=t}}e.default=r},698:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});class r extends Error{}e.default=r},72:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});class r extends Error{}e.default=r},748:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(698)),a=r(685);e.default=class{constructor(t){this.attributes=t,this.reserved=4}deserialize(t,e=!1){const r={};for(;t.position=this.attributes.length))return this.attributes[Number(r)];if(e)throw new i.default("attribute does not exists")}}},484:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(698)),a=r(432);e.default=class{constructor(t){if(void 0===a.ParserTypes[t])throw new i.default(`attribute type '${t}' not defined`);this.parser=a.ParserTypes[t]}deserialize(t){return this.parser.deserialize(t)}serialize(t){return this.parser.serialize(t)}}},817:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(685);e.default=class{constructor(t){this.element=t}deserialize(t){const e=(0,n.varint_decode)(t).toJSNumber(),r=[];for(let n=0;n=t.data.length)throw new a.default("failed to unpack integer");const n=(0,i.default)(t.data[t.position]);if(t.position+=1,n.lesser(128)){e=e.plus(n.shiftLeft(7*r));break}e=e.plus(n.and(127).shiftLeft(7*r))}return e},e.integer_sign=function(t,e){const r=(0,i.default)(t);if(r.greaterOrEquals((0,i.default)(2).pow(8*e-1)))throw new Error("cannot sign integer: too big");return r.greaterOrEquals(0)?r:r.negate().xor((0,i.default)(2).pow(8*e).minus(1)).plus(1)},e.integer_unsign=function(t,e){const r=(0,i.default)(t);if(r.greater((0,i.default)(2).pow(8*e)))throw new Error("cannot unsign integer: too big");return r.greater((0,i.default)(2).pow(8*e-1))?r.minus(1).xor((0,i.default)(2).pow(8*e).minus(1)).negate():r},e.zigzag_encode=function(t){const e=(0,i.default)(t);return e.lesser(0)?e.plus(1).multiply(-2).plus(1):e.multiply(2)},e.zigzag_decode=function(t){const e=(0,i.default)(t);return e.mod(2).equals(0)?e.divmod(2).quotient:e.divmod(2).quotient.multiply(-1).minus(1)};const u=new s.default("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");e.base58_decode=function(t){return u.decode(t)},e.base58_encode=function(t){return u.encode(t)},e.hex_decode=function(t){const e=t.match(/.{1,2}/g);return e?new Uint8Array(e.map((t=>parseInt(t,16)))):new Uint8Array(0)},e.hex_encode=function(t){return t.reduce(((t,e)=>t+e.toString(16).padStart(2,"0")),"")},e.concat_byte_arrays=function(t){const e=new Uint8Array(t.reduce(((t,e)=>t+e.length),0));let r=0;for(const n of t)e.set(n,r),r+=n.length;return e},e.int_to_byte_vector=function(t){const e=[];let r=(0,i.default)(t);for(;r.notEquals(0);)e.push(r.and(255).toJSNumber()),r=r.shiftRight(8);return new Uint8Array(e)},e.byte_vector_to_int=function(t){let e=(0,i.default)(0);for(let r=0;r{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=class{constructor(t){if(this.ALPHABET=t,t.length>=255)throw new TypeError("Alphabet too long");this.BASE_MAP=new Uint8Array(256);for(let t=0;t>>0,o=new Uint8Array(a);for(;n!==i;){let e=t[n],i=0;for(let t=a-1;(0!==e||i>>0,o[t]=e%this.BASE>>>0,e=e/this.BASE>>>0;if(0!==e)throw new Error("Non-zero carry");r=i,n++}let s=a-r;for(;s!==a&&0===o[s];)s++;let u=this.LEADER.repeat(e);for(;s>>0,a=new Uint8Array(i);for(;t[e];){let r=this.BASE_MAP[t.charCodeAt(e)];if(255===r)return new Uint8Array(0);let o=0;for(let t=i-1;(0!==r||o>>0,a[t]=r%256>>>0,r=r/256>>>0;if(0!==r)throw new Error("Non-zero carry");n=o,e++}if(" "===t[e])return new Uint8Array(0);let o=i-n;for(;o!==i&&0===a[o];)o++;const s=new Uint8Array(r+(i-o));s.fill(0,0,r);let u=r;for(;o!==i;)s[u++]=a[o++];return s}}},110:(t,e)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.prepare=void 0;class r{constructor(t,e=0){this.data=t,this.position=e}}e.default=r,e.prepare=function(t){return new r(t,0)}},74:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(337));class a extends i.default{constructor(){super(1)}deserialize(t){return 1===super.deserialize(t)[0]?1:0}serialize(t){return super.serialize(new Uint8Array([t?1:0]))}}e.default=a},111:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0}),e.ByteParser=void 0;const i=n(r(836));class a extends i.default{deserialize(t){return super.deserialize(t)}serialize(t){return super.serialize(t)}}e.ByteParser=a},324:function(t,e,r){"use strict";var n=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});const i=n(r(736)),a=n(r(337));class o extends a.default{deserialize(t){const e=super.deserialize(t).reverse();let r=(0,i.default)(0);for(const t of e)r=r.shiftLeft(8),r=r.plus(t);return this.size<=6?r.toJSNumber():r.toString()}serialize(t){let e=(0,i.default)(t);const r=[];for(let t=0;t(t.paths=[],t.children||(t.children=[]),t);var n=r(432);atomicassets=n})(); -------------------------------------------------------------------------------- /lib/float.js: -------------------------------------------------------------------------------- 1 | /** 2 | * pure javascript functions to read and write 32-bit and 64-bit IEEE 754 floating-point 3 | * 4 | * Copyright (C) 2017-2019 Andras Radics 5 | * Licensed under the Apache License, Version 2.0 6 | */ 7 | // removed buffer 8 | 9 | 'use strict'; 10 | 11 | var isBigeCpu = false; 12 | var readFloat32Array, writeFloat32Array, readFloat32ArrayRev, writeFloat32ArrayRev; 13 | var readFloat64Array, writeFloat64Array, readFloat64ArrayRev, writeFloat64ArrayRev; 14 | 15 | 16 | // test FloatArray existence with && to not throw off code coverage 17 | (typeof Float32Array === 'function') && (function(){ 18 | var _fp32 = new Float32Array(1); 19 | var _b32 = new Uint8Array(_fp32.buffer); 20 | 21 | _fp32[0] = -1; 22 | isBigeCpu = _b32[3] === 0; 23 | 24 | readFloat32Array = function readFloat32Array( buf, pos ) { 25 | pos = pos || 0; 26 | if (pos < 0 || pos + 4 > buf.length) return 0; 27 | _b32[0] = buf[pos++]; _b32[1] = buf[pos++]; _b32[2] = buf[pos++];_b32[3] = buf[pos]; 28 | //_b32[0] = buf[pos+0]; _b32[1] = buf[pos+1]; _b32[2] = buf[pos+2]; _b32[3] = buf[pos+3]; 29 | return _fp32[0]; 30 | } 31 | 32 | readFloat32ArrayRev = function readFloat32ArrayRev( buf, pos ) { 33 | pos = pos || 0; 34 | if (pos < 0 || pos + 4 > buf.length) return 0; 35 | _b32[3] = buf[pos++]; _b32[2] = buf[pos++]; _b32[1] = buf[pos++]; _b32[0] = buf[pos]; 36 | //_b32[3] = buf[pos+0]; _b32[2] = buf[pos+1]; _b32[1] = buf[pos+2]; _b32[0] = buf[pos+3]; 37 | return _fp32[0]; 38 | } 39 | 40 | writeFloat32Array = function writeFloat32Array( buf, v, pos ) { 41 | pos = pos || 0; 42 | _fp32[0] = v; 43 | buf[pos++] = _b32[0]; buf[pos++] = _b32[1]; buf[pos++] = _b32[2]; buf[pos] = _b32[3]; 44 | //buf[pos+0] = _b32[0]; buf[pos+1] = _b32[1]; buf[pos+2] = _b32[2]; buf[pos+3] = _b32[3]; 45 | } 46 | 47 | writeFloat32ArrayRev = function writeFloat32ArrayRev( buf, v, pos ) { 48 | pos = pos || 0; 49 | _fp32[0] = v; 50 | buf[pos++] = _b32[3]; buf[pos++] = _b32[2]; buf[pos++] = _b32[1]; buf[pos] = _b32[0]; 51 | //buf[pos+0] = _b32[3]; buf[pos+1] = _b32[2]; buf[pos+2] = _b32[1]; buf[pos+3] = _b32[0]; 52 | } 53 | })(); 54 | 55 | (typeof Float64Array === 'function') && (function(){ 56 | var _fp64 = new Float64Array(1); 57 | var _b64 = new Uint8Array(_fp64.buffer); 58 | 59 | readFloat64Array = function readFloat64Array( buf, pos ) { 60 | pos = pos || 0; 61 | if (pos < 0 || pos + 8 > buf.length) return 0; 62 | //_b64[0] = buf[pos++]; _b64[1] = buf[pos++]; _b64[2] = buf[pos++]; _b64[3] = buf[pos++]; 63 | //_b64[4] = buf[pos++]; _b64[5] = buf[pos++]; _b64[6] = buf[pos++]; _b64[7] = buf[pos]; 64 | _b64[0] = buf[pos+0]; _b64[1] = buf[pos+1]; _b64[2] = buf[pos+2]; _b64[3] = buf[pos+3]; 65 | _b64[4] = buf[pos+4]; _b64[5] = buf[pos+5]; _b64[6] = buf[pos+6]; _b64[7] = buf[pos+7]; 66 | return _fp64[0]; 67 | } 68 | 69 | readFloat64ArrayRev = function readFloat64ArrayRev( buf, pos ) { 70 | pos = pos || 0; 71 | if (pos < 0 || pos + 8 > buf.length) return 0; 72 | //_b64[7] = buf[pos++]; _b64[6] = buf[pos++]; _b64[5] = buf[pos++]; _b64[4] = buf[pos++]; 73 | //_b64[3] = buf[pos++]; _b64[2] = buf[pos++]; _b64[1] = buf[pos++]; _b64[0] = buf[pos]; 74 | _b64[7] = buf[pos+0]; _b64[6] = buf[pos+1]; _b64[5] = buf[pos+2]; _b64[4] = buf[pos+3]; 75 | _b64[3] = buf[pos+4]; _b64[2] = buf[pos+5]; _b64[1] = buf[pos+6]; _b64[0] = buf[pos+7]; 76 | return _fp64[0]; 77 | } 78 | 79 | writeFloat64Array = function writeFloat64Array( buf, v, pos ) { 80 | pos = pos || 0; 81 | _fp64[0] = v; 82 | buf[pos + 0] = _b64[0]; buf[pos + 1] = _b64[1]; buf[pos + 2] = _b64[2]; buf[pos + 3] = _b64[3]; 83 | buf[pos + 4] = _b64[4]; buf[pos + 5] = _b64[5]; buf[pos + 6] = _b64[6]; buf[pos + 7] = _b64[7]; 84 | } 85 | 86 | writeFloat64ArrayRev = function writeFloat64ArrayRev( buf, v, pos ) { 87 | pos = pos || 0; 88 | _fp64[0] = v; 89 | buf[pos + 0] = _b64[7]; buf[pos + 1] = _b64[6]; buf[pos + 2] = _b64[5]; buf[pos + 3] = _b64[4]; 90 | buf[pos + 4] = _b64[3]; buf[pos + 5] = _b64[2]; buf[pos + 6] = _b64[1]; buf[pos + 7] = _b64[0]; 91 | } 92 | })(); 93 | 94 | 95 | // arithmetic operations preserve NaN, but logical ops (, >>, etc) convert them to zero 96 | // Assemble the word to generate NaN if any reads are undefined (outside the bounds of the array). 97 | function readWord( buf, offs, dirn ) { 98 | var a = buf[offs++], b = buf[offs++], c = buf[offs++], d = buf[offs]; 99 | return (dirn === 'bige') 100 | ? (((((a * 256) + b) * 256) + c) * 256) + d 101 | : (((((d * 256) + c) * 256) + b) * 256) + a; 102 | } 103 | 104 | function writeWord( buf, v, offs, dirn ) { 105 | var a = (v >>> 24) & 0xff, b = (v >> 16) & 0xff, c = (v >> 8) & 0xff, d = (v) & 0xff; 106 | (dirn === 'bige') 107 | ? (buf[offs++] = a, buf[offs++] = b, buf[offs++] = c, buf[offs] = d) 108 | : (buf[offs++] = d, buf[offs++] = c, buf[offs++] = b, buf[offs] = a) 109 | } 110 | 111 | // write the two-word value [hi,lo] where hi holds the 32 msb bits and lo the 32 lsb bits 112 | function writeDoubleWord( buf, hi, lo, offs, dirn ) { 113 | if (dirn === 'bige') { 114 | writeWord(buf, hi, offs, dirn); 115 | writeWord(buf, lo, offs + 4, dirn); 116 | } 117 | else { 118 | writeWord(buf, lo, offs, dirn); 119 | writeWord(buf, hi, offs + 4, dirn); 120 | } 121 | } 122 | 123 | // given an exponent n, return 2**n 124 | // n is always an integer, faster to shift when possible 125 | // Note that nodejs Math.pow() is faster than a lookup table (may be caching) 126 | var _2eXp = new Array(); for (var i=0; i<1200; i++) _2eXp[i] = Math.pow(2, i); 127 | var _2eXn = new Array(); for (var i=0; i<1200; i++) _2eXn[i] = Math.pow(2, -i); 128 | function pow2( exp ) { 129 | return (exp >= 0) ? _2eXp[exp] : _2eXn[-exp]; 130 | //return (exp >= 0) ? (exp < 31 ? (1 << exp) : Math.pow(2, exp)) 131 | // : (exp > -31 ? (1 / (1 << -exp)) : Math.pow(2, exp)); 132 | } 133 | 134 | 135 | // getFloat() from qbson, https://github.com/andrasq/node-qbson: 136 | /* 137 | * extract the 64-bit little-endian ieee 754 floating-point value 138 | * see http://en.wikipedia.org/wiki/Double-precision_floating-point_format 139 | * 1 bit sign + 11 bits exponent + (1 implicit mantissa 1 bit) + 52 mantissa bits 140 | */ 141 | var _rshift32 = (1 / 0x100000000); // >> 32 for floats 142 | var _rshift20 = (1 / 0x100000); // >> 20 for floats 143 | var _lshift32 = (1 * 0x100000000); // << 32 144 | var _rshift52 = (1 * _rshift32 * _rshift20); // >> 52 145 | var _rshift1023 = pow2(-1023); // 2^-1023 146 | function readDouble( buf, offset, dirn ) { 147 | var w0 = readWord(buf, offset, dirn); 148 | var w1 = readWord(buf, offset + 4, dirn); 149 | var highWord, lowWord; 150 | (dirn === 'bige') ? (highWord = w0, lowWord = w1) : (highWord = w1, lowWord = w0); 151 | 152 | var mantissa = (highWord & 0x000FFFFF) * _lshift32 + lowWord; 153 | var exponent = (highWord & 0x7FF00000) >>> 20; 154 | var sign = (highWord >> 31) || 1; // -1, 1, or 1 if NaN 155 | 156 | var value; 157 | if (exponent === 0x000) { 158 | // zero if !mantissa, else subnormal (non-normalized reduced precision small value) 159 | // recover negative zero -0.0 as distinct from 0.0 160 | // subnormals do not have an implied leading 1 bit and are positioned 1 bit to the left 161 | value = mantissa ? (mantissa * pow2(-52 + 1 -1023)) : 0.0; 162 | } 163 | else if (exponent < 0x7ff) { 164 | // normalized value with an implied leading 1 bit and 1023 biased exponent 165 | // test for NaN with (mantissa >= 0), and return 0 if NaN ie read from outside buffer bounds 166 | value = (mantissa >= 0) ? (1 + mantissa * _rshift52) * pow2(exponent - 1023) : 0.0; 167 | } 168 | else { 169 | // Infinity if zero mantissa (+/- per sign), NaN if nonzero mantissa 170 | value = mantissa ? NaN : Infinity; 171 | } 172 | 173 | return sign * value; 174 | } 175 | 176 | // 177 | // Note: node-v9 prefers +28% (sign * value), node v6 doesnt care, node v8 likes +16% (-value : value) 178 | // 179 | // float32: 1 sign + 8 exponent + 24 mantissa (23 stored, 1 implied) 180 | // see https://en.wikipedia.org/wiki/Single-precision_floating-point_format 181 | // 182 | // Exponent Mantissa == 0 Mantissa > 0 Value 183 | // 00 +0, -0 denormalized 2^( 1-127) * (0. + (mantissa / 2^23)) 184 | // 00.. FE normalized 2^(exp-127) * (1. + (mantissa / 2^23)) 185 | // FF +/-Infinity NaN - 186 | // 187 | var _rshift23 = Math.pow(2, -23); // >> 23 for floats 188 | var _rshift127 = Math.pow(2, -127); // 2^-127 189 | function readFloat( buf, offset, dirn ) { 190 | var word = readWord(buf, offset, dirn); 191 | var mantissa = (word & 0x007FFFFF); 192 | var exponent = (word & 0x7F800000) >>> 23; 193 | var sign = (word >> 31) || 1; // -1, 1, or 1 if NaN 194 | 195 | var value; 196 | if (exponent === 0x000) { 197 | value = mantissa ? mantissa * _rshift23 * 2 * _rshift127 : 0.0; 198 | } 199 | else if (exponent < 0xff) { 200 | value = (1 + mantissa * _rshift23) * pow2(exponent - 127) // * _rshift127; 201 | } 202 | else { 203 | value = mantissa ? NaN : Infinity; 204 | } 205 | 206 | return sign * value; 207 | //return (word >>> 31) ? -value : value; 208 | } 209 | 210 | // given a positive value v, normalize it to between 1 and less than 2 with a binary exponent 211 | // The exponent is the number of bit places it was shifted, positive if v was >= 2. 212 | // The special values 0, -0, NaN, +Infinity and -Infinity are not handled here. 213 | // Looping is faster than (Math.log(v) / Math.LN2) in node-v6, v8, and v9. 214 | // This function can account for half the time taken to write a double. 215 | var _parts = { exp: 0, mant: 0 }; 216 | function normalize( v ) { 217 | var exp = 0; 218 | 219 | if (v >= 2) { 220 | exp = countDoublings(1, v); 221 | v *= pow2(-exp); 222 | // if doubled to exactly v/2, adjust up to v 223 | if (v >= 2) { v /= 2; exp += 1 } 224 | } 225 | else if (v < 1) { 226 | exp = countDoublings(v, 2); 227 | // avoid using pow2 exponents > 1023, they overflow to Infinity 228 | if (exp <= 1023) v *= pow2(exp); 229 | else { v *= pow2(exp - 100); v *= pow2(100); } 230 | exp = -exp; 231 | } 232 | 233 | // TODO: pass in num bits, and normalize straight to mantissa / denorm 234 | 235 | _parts.exp = exp; 236 | _parts.mant = v; 237 | return _parts; 238 | } 239 | 240 | // count how many doublings of a are needed for it be close to b. 241 | // Returns a shift count that grows (a) to at least (b/2) but less than (b). 242 | // Doubling 1 toward v ensures that (v >> n) >= 1 < 2, 243 | // and doubling from v toward 2 ensures that (v << n) >= 1 < 2. 244 | var _2e192 = Math.pow(2, 192); 245 | function countDoublings( a, b ) { 246 | var n = 0; 247 | 248 | while (a * _2e192 < b) { a *= _2e192; n += 192 } 249 | while (a * 0x10000000000000000 < b) { a *= 0x10000000000000000; n += 64 } 250 | while (a * 0x10000 < b) { a *= 0x10000; n += 16 } 251 | while (a * 0x40 < b) { a *= 0x40; n += 6 } 252 | while (a * 2 < b) { a *= 2; n += 1 } 253 | 254 | return n; 255 | } 256 | 257 | // round the fraction in v and scale up to scale = 2^n bits 258 | // https://blog.angularindepth.com/how-to-round-binary-fractions-625c8fa3a1af 259 | // Rounding can cause the scaled value to exceed 2^n. 260 | function roundMantissa( v, scale ) { 261 | v *= scale; 262 | // round to nearest, but round a 0.5 tie to even (0.5 to 0.0 and 1.5 to 2.0) 263 | // round all numbers with a fraction other than 1/2, and round up odd numbers with 264 | return ((v - Math.floor(v) !== 0.5) || (v & 1)) ? v + 0.5 : v; 265 | } 266 | 267 | // float32: 1 sign + 8 exponent + (1 implied mantissa 1 bit) + 23 stored mantissa bits 268 | // NaN types: quiet Nan = x.ff.8xxx, signaling NaN = x.ff.0xx1 (msb zero, at least one other bit set) 269 | // JavaScript built-in NaN is the non-signaling 7fc00000, but arithmetic can yield a negative NaN ffc00000. 270 | function writeFloat( buf, v, offset, dirn ) { 271 | var norm, word, sign = 0; 272 | if (v < 0) { sign = 0x80000000; v = -v; } 273 | 274 | if (! (v && v < Infinity)) { 275 | if (v === 0) { // -0, +0 276 | word = (1/v < 0) ? 0x80000000 : 0x00000000; 277 | } 278 | else if (v === Infinity) { // -Infinity, +Infinity 279 | word = sign | 0x7F800000; 280 | } 281 | else { // NaN - positive, non-signaling 282 | word = 0x7FC00000; 283 | } 284 | writeWord(buf, word, offset, dirn); 285 | } 286 | else { 287 | norm = normalize(v); // separate exponent and mantissa 288 | norm.exp += 127; // bias exponent 289 | 290 | if (norm.exp <= 0) { // denormalized number 291 | if (norm.exp <= -25) { // too small, underflow to zero. -24 might round up though. 292 | norm.mant = 0; 293 | norm.exp = 0; 294 | } else { // denormalize 295 | norm.mant = roundMantissa(norm.mant, pow2(22 + norm.exp)); 296 | norm.exp = 0; // rounding can carry out and re-normalize the number 297 | if (norm.mant >= 0x800000) { norm.mant -= 0x800000; norm.exp += 1 } 298 | } 299 | } else { 300 | norm.mant = roundMantissa(norm.mant - 1, 0x800000); 301 | // if rounding overflowed into the hidden 1s place, hide it and adjust the exponent 302 | if (norm.mant >= 0x800000) { norm.mant -= 0x800000; norm.exp += 1 } 303 | if (norm.exp > 254) { // overflow to Infinity 304 | norm.mant = 0; 305 | norm.exp = 255; 306 | } 307 | } 308 | 309 | word = sign | (norm.exp << 23) | norm.mant; 310 | writeWord(buf, word, offset, dirn); 311 | } 312 | } 313 | 314 | // double64: 1 bit sign + 11 bits exponent + (1 implied mantissa 1 bit) + 52 stored mantissa bits 315 | // Writing doubles is simpler than floats, because the internal javascript 64-bit floats 316 | // are identical to the stored representation, and thus will not overflow or underflow. 317 | var doubleArray = [0, 0, 0, 0, 0, 0, 0, 0]; 318 | var doubleBuf = new Uint8Array(8); 319 | var _2e52 = Math.pow(2, 52); 320 | function writeDouble( buf, v, offset, dirn ) { 321 | var norm, highWord, lowWord, sign = 0; 322 | if (v < 0) { sign = 0x80000000; v = -v; } 323 | 324 | if (! (v && v < Infinity)) { 325 | if (v === 0) { // -0, +0 326 | highWord = (1/v < 0) ? 0x80000000 : 0; 327 | lowWord = 0; 328 | } 329 | else if (v === Infinity) { // -Infinity, +Infinity 330 | highWord = (sign + 0x7FF00000); 331 | lowWord = 0; 332 | } 333 | else { // NaN - positive, non-signaling 334 | highWord = 0x7FF80000; 335 | lowWord = 0; 336 | } 337 | writeDoubleWord(buf, highWord, lowWord, offset, dirn); 338 | } 339 | else { 340 | norm = normalize(v); // separate exponent and mantissa 341 | norm.exp += 1023; // bias exponent 342 | 343 | if (norm.exp <= 0) { // denormalized 344 | // JavaScript numbers can not hold values small enough to underflow 345 | // and no need to round, all bits will be written 346 | norm.mant *= pow2(51 + norm.exp); 347 | norm.exp = 0; 348 | } 349 | else { 350 | // no need to round, all bits will be written 351 | norm.mant = (norm.mant - 1) * _2e52; 352 | } 353 | 354 | highWord = sign | (norm.exp << 20) | (norm.mant / 0x100000000); 355 | lowWord = norm.mant >>> 0; 356 | writeDoubleWord(buf, highWord, lowWord, offset, dirn); 357 | } 358 | } 359 | 360 | 361 | ;(function install() { 362 | var exports = typeof module === 'object' && module.exports || this; 363 | 364 | exports.readWord = readWord; 365 | exports.writeWord = writeWord; 366 | exports.writeDoubleWord = writeDoubleWord; 367 | 368 | exports.readFloat = readFloat; 369 | exports.writeFloat = writeFloat; 370 | exports.readDouble = readDouble; 371 | exports.writeDouble = writeDouble; 372 | 373 | // expose the implementation to the tests 374 | exports._useFloatArray = function( yesno ) { 375 | exports._usingFloatArray = yesno; 376 | if (yesno) { 377 | // software conversion is faster for float32 than Float32Array 378 | // Only read via Float32Array if yesno == 'full'. 379 | if (yesno == 'full') exports.readFloatLE = isBigeCpu ? readFloat32ArrayRev : readFloat32Array; 380 | exports.writeFloatLE = isBigeCpu ? writeFloat32ArrayRev : writeFloat32Array; 381 | if (yesno == 'full') exports.readFloatBE = isBigeCpu ? readFloat32Array : readFloat32ArrayRev; 382 | exports.writeFloatBE = isBigeCpu ? writeFloat32Array : writeFloat32ArrayRev; 383 | 384 | exports.readDoubleLE = isBigeCpu ? readFloat64ArrayRev : readFloat64Array; 385 | exports.writeDoubleLE = isBigeCpu ? writeFloat64ArrayRev : writeFloat64Array; 386 | exports.readDoubleBE = isBigeCpu ? readFloat64Array : readFloat64ArrayRev; 387 | exports.writeDoubleBE = isBigeCpu ? writeFloat64Array : writeFloat64ArrayRev; 388 | } 389 | else { 390 | exports._usingFloatArray = ''; 391 | exports.readFloatLE = function readFloatLE( buf, offset ) { return exports.readFloat(buf, offset || 0, 'le'); } 392 | exports.writeFloatLE = function writeFloatLE( buf, v, offset ) { exports.writeFloat(buf, v, offset || 0, 'le'); }; 393 | exports.readFloatBE = function readFloatBE( buf, offset ) { return exports.readFloat(buf, offset || 0, 'bige'); } 394 | exports.writeFloatBE = function writeFloatBE( buf, v, offset ) { exports.writeFloat(buf, v, offset || 0, 'bige'); } 395 | 396 | exports.readDoubleLE = function readDoubleLE( buf, offset ) { return exports.readDouble(buf, offset || 0, 'le'); } 397 | exports.writeDoubleLE = function writeDoubleLE( buf, v, offset ) { exports.writeDouble(buf, v, offset || 0, 'le'); } 398 | exports.readDoubleBE = function readDoubleBE( buf, offset ) { return exports.readDouble(buf, offset || 0, 'bige'); } 399 | exports.writeDoubleBE = function writeDoubleLE( buf, v, offset ) { exports.writeDouble(buf, v, offset || 0, 'bige'); } 400 | } 401 | } 402 | 403 | // expose the cpu endianism to the tests 404 | exports._getBigeCpu = function() { return isBigeCpu }; 405 | exports._setBigeCpu = function(yesno) { isBigeCpu = yesno }; 406 | 407 | // by default export the software conversion functions, then 408 | // if available, convert by casting a FloatArray to a byte array 409 | exports._useFloatArray(false); 410 | exports._useFloatArray(readFloat32Array && readFloat64Array && 'fastest'); 411 | 412 | // accelerate access 413 | install.prototype = exports; 414 | 415 | }).call(this); 416 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atomicassets", 3 | "description": "js module to fetch data from the atomicassets NFT standard", 4 | "keywords": [ 5 | "atomicassets", 6 | "nft", 7 | "blockchain", 8 | "eosio", 9 | "pinknetwork" 10 | ], 11 | "author": "pink.network", 12 | "homepage": "https://atomicassets.io", 13 | "bugs": { 14 | "url": "https://github.com/pinknetworkx/atomicassets-js/issues", 15 | "email": "pinknetworkx@gmail.com" 16 | }, 17 | "license": "MIT", 18 | "version": "1.5.0", 19 | "main": "build/index.js", 20 | "types": "build/index.d.ts", 21 | "scripts": { 22 | "build:npm": "tsc", 23 | "build:web": "webpack --config webpack.prod.js", 24 | "build": "yarn run build:npm && yarn run build:web", 25 | "lint": "tslint -p tsconfig.json", 26 | "test": "mocha -r ts-node/register test/**/*.test.ts", 27 | "publish": "yarn run build && yarn publish" 28 | }, 29 | "dependencies": { 30 | "big-integer": "^1.6.51" 31 | }, 32 | "devDependencies": { 33 | "@types/chai": "^4.2.18", 34 | "@types/mocha": "^8.2.2", 35 | "@types/node": "^14.14.44", 36 | "chai": "^4.3.4", 37 | "mocha": "^8.4.0", 38 | "node-fetch": "^2.6.1", 39 | "ts-loader": "^9.1.2", 40 | "ts-node": "^9.1.1", 41 | "tslint": "^5.20.1", 42 | "tslint-consistent-codestyle": "^1.16.0", 43 | "tslint-eslint-rules": "^5.4.0", 44 | "tslint-origin-ordered-imports-rule": "^1.2.2", 45 | "typescript": "^4.2.4", 46 | "webpack": "^5.37.0", 47 | "webpack-cli": "^4.7.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/API/Explorer/Enums.ts: -------------------------------------------------------------------------------- 1 | export enum OfferState { 2 | Pending, 3 | Invalid, 4 | Unknown, 5 | Accepted, 6 | Declined, 7 | Canceled 8 | } 9 | 10 | export enum OrderParam { 11 | Asc = 'asc', 12 | Desc = 'desc' 13 | } 14 | 15 | export enum AssetsSort { 16 | AssetId = 'asset_id', 17 | Updated = 'updated', 18 | Transferred = 'transferred', 19 | Minted = 'minted', 20 | TemplateMint = 'template_mint', 21 | Name = 'name' 22 | } 23 | 24 | export enum CollectionsSort { 25 | Created = 'created', 26 | CollectionName = 'collection_name' 27 | } 28 | 29 | export enum OffersSort { 30 | Created = 'created', 31 | Updated = 'updated' 32 | } 33 | 34 | export enum SchemasSort { 35 | Created = 'created', 36 | SchemaName = 'schema_name' 37 | } 38 | 39 | export enum TemplatesSort { 40 | Created = 'created', 41 | Name = 'name' 42 | } 43 | 44 | export enum TransfersSort { 45 | Created = 'created' 46 | } 47 | -------------------------------------------------------------------------------- /src/API/Explorer/Objects.ts: -------------------------------------------------------------------------------- 1 | import { SchemaObject } from '../../Schema'; 2 | import { OfferState } from './Enums'; 3 | 4 | export interface ILightCollection { 5 | contract: string; 6 | collection_name: string; 7 | name: string; 8 | img: string; 9 | author: string; 10 | allow_notify: boolean; 11 | authorized_accounts: string[]; 12 | notify_accounts: string[]; 13 | market_fee: number; 14 | data: {[key: string]: any}; 15 | created_at_block: string; 16 | created_at_time: string; 17 | } 18 | 19 | export interface ILightSchema { 20 | schema_name: string; 21 | format: SchemaObject[]; 22 | created_at_block: string; 23 | created_at_time: string; 24 | } 25 | 26 | export interface ILightTemplate { 27 | template_id: string; 28 | max_supply: string; 29 | is_transferable: boolean; 30 | is_burnable: boolean; 31 | issued_supply: string; 32 | immutable_data: {[key: string]: any}; 33 | created_at_block: string; 34 | created_at_time: string; 35 | } 36 | 37 | export interface IAsset { 38 | contract: string; 39 | asset_id: string; 40 | owner: string | null; 41 | name: string; 42 | is_transferable: boolean; 43 | is_burnable: boolean; 44 | template_mint: string; 45 | collection: ILightCollection; 46 | schema: ILightSchema; 47 | template: ILightTemplate | null; 48 | backed_tokens: Array<{token_contract: string, token_symbol: string, token_precision: number, amount: string}>; 49 | immutable_data: {[key: string]: any}; 50 | mutable_data: {[key: string]: any}; 51 | data: {[key: string]: any}; 52 | burned_by_account: string | null; 53 | burned_at_block: string | null; 54 | burned_at_time: string | null; 55 | updated_at_block: string; 56 | updated_at_time: string; 57 | transferred_at_block: string; 58 | transferred_at_time: string; 59 | minted_at_block: string; 60 | minted_at_time: string; 61 | } 62 | 63 | export interface ICollection extends ILightCollection { 64 | contract: string; 65 | } 66 | 67 | export interface ISchema extends ILightSchema { 68 | contract: string; 69 | collection: ILightCollection; 70 | } 71 | 72 | export interface ITemplate extends ILightTemplate { 73 | contract: string; 74 | collection: ILightCollection; 75 | schema: ILightSchema; 76 | } 77 | 78 | export interface IOffer { 79 | contract: string; 80 | offer_id: string; 81 | sender_name: string; 82 | recipient_name: string; 83 | memo: string; 84 | state: OfferState; 85 | sender_assets: IAsset[]; 86 | recipient_assets: IAsset[]; 87 | is_sender_contract: boolean; 88 | is_recipient_contract: boolean; 89 | updated_at_block: string; 90 | updated_at_time: string; 91 | created_at_block: string; 92 | created_at_time: string; 93 | } 94 | 95 | export interface ITransfer { 96 | contract: string; 97 | transfer_id: string; 98 | sender_name: string; 99 | recipient_name: string; 100 | memo: string; 101 | txid: string; 102 | assets: IAsset[]; 103 | created_at_block: string; 104 | created_at_time: string; 105 | } 106 | 107 | export interface ILog { 108 | log_id: string; 109 | name: string; 110 | data: {[key: string]: any}; 111 | txid: string; 112 | created_at_block: string; 113 | created_at_time: string; 114 | } 115 | 116 | export interface IConfig { 117 | contract: string; 118 | version: string; 119 | collection_format: SchemaObject[]; 120 | supported_tokens: Array<{token_contract: string, token_symbol: string, token_precision: number}>; 121 | } 122 | 123 | /* RESPONSES */ 124 | 125 | export interface IAssetStats { 126 | template_mint: number; 127 | } 128 | 129 | export interface ICollectionStats { 130 | assets: string; 131 | burned: string; 132 | templates: string; 133 | schemas: string; 134 | burned_by_template: Array<{template_id: string, burned: number}>; 135 | burned_by_schema: Array<{schema_name: string, burned: number}>; 136 | } 137 | 138 | export interface ISchemaStats { 139 | assets: string; 140 | burned: string; 141 | templates: string; 142 | } 143 | 144 | export interface ITemplateStats { 145 | assets: string; 146 | burned: string; 147 | } 148 | 149 | export interface IAccountStats { 150 | collections: Array<{collection: ICollection, assets: string}>; 151 | templates: Array<{template_id: string, assets: string}>; 152 | assets: string; 153 | } 154 | 155 | export interface IAccountCollectionStats { 156 | schemas: Array<{schema_name: ICollection, assets: string}>; 157 | templates: Array<{template_id: string, assets: string}>; 158 | } 159 | -------------------------------------------------------------------------------- /src/API/Explorer/Params.ts: -------------------------------------------------------------------------------- 1 | import { AssetsSort, CollectionsSort, OffersSort, OfferState, OrderParam, SchemasSort, TemplatesSort, TransfersSort } from './Enums'; 2 | 3 | export interface GreylistParams { 4 | collection_blacklist?: string; 5 | collection_whitelist?: string; 6 | } 7 | 8 | export interface HideOffersParams { 9 | hide_offers?: boolean; 10 | } 11 | 12 | export interface PrimaryBoundaryParams { 13 | ids?: string; 14 | lower_bound?: string; 15 | upper_bound?: string; 16 | } 17 | 18 | export interface DateBoundaryParams { 19 | before?: number; 20 | after?: number; 21 | } 22 | 23 | export interface AssetFilterParams { 24 | asset_id?: string; 25 | owner?: string; 26 | burned?: boolean; 27 | collection_name?: string; 28 | schema_name?: string; 29 | template_id?: number; 30 | match?: string; 31 | is_transferable?: boolean; 32 | is_burnable?: boolean; 33 | [key: string]: any; 34 | } 35 | 36 | export interface AssetsApiParams extends AssetFilterParams, GreylistParams, HideOffersParams, PrimaryBoundaryParams, DateBoundaryParams { 37 | authorized_account?: string; 38 | only_duplicate_templates?: boolean; 39 | 40 | min_template_mint?: number; 41 | max_template_mint?: number; 42 | template_blacklist?: string; 43 | template_whitelist?: string; 44 | 45 | order?: OrderParam; 46 | sort?: AssetsSort; 47 | } 48 | 49 | export interface CollectionApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 50 | author?: string; 51 | match?: string; 52 | authorized_account?: string; 53 | notify_account?: string; 54 | 55 | order?: OrderParam; 56 | sort?: CollectionsSort; 57 | } 58 | 59 | export interface SchemaApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 60 | collection_name?: string; 61 | schema_name?: string; 62 | match?: string; 63 | authorized_account?: string; 64 | 65 | order?: OrderParam; 66 | sort?: SchemasSort; 67 | } 68 | 69 | export interface TemplateApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 70 | collection_name?: string; 71 | schema_name?: string; 72 | authorized_account?: string; 73 | template_id?: string; 74 | max_supply?: number; 75 | has_assets?: boolean; 76 | 77 | issued_supply?: number; 78 | min_issued_supply?: number; 79 | max_issued_supply?: number; 80 | 81 | is_transferable?: boolean; 82 | is_burnable?: boolean; 83 | 84 | order?: OrderParam; 85 | sort?: TemplatesSort; 86 | [key: string]: any; 87 | } 88 | 89 | export interface TransferApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 90 | account?: string; 91 | sender?: string; 92 | recipient?: string; 93 | asset_id?: string; 94 | 95 | order?: OrderParam; 96 | sort?: TransfersSort; 97 | } 98 | 99 | export interface OfferApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 100 | account?: string; 101 | sender?: string; 102 | recipient?: string; 103 | asset_id?: string; 104 | state?: OfferState; 105 | 106 | is_recipient_contract?: boolean; 107 | hide_contracts?: boolean; 108 | 109 | recipient_asset_blacklist?: string; 110 | recipient_asset_whitelist?: string; 111 | sender_asset_blacklist?: string; 112 | sender_asset_whitelist?: string; 113 | account_whitelist?: string; 114 | account_blacklist?: string; 115 | 116 | order?: OrderParam; 117 | sort?: OffersSort; 118 | } 119 | 120 | export interface AccountApiParams extends GreylistParams, PrimaryBoundaryParams, DateBoundaryParams { 121 | match?: string; 122 | collection_name?: string; 123 | schema_name?: string; 124 | template_id?: string; 125 | } 126 | -------------------------------------------------------------------------------- /src/API/Explorer/index.ts: -------------------------------------------------------------------------------- 1 | import ExplorerActionGenerator from '../../Actions/Explorer'; 2 | import ApiError from '../../Errors/ApiError'; 3 | import { 4 | IAccountCollectionStats, 5 | IAccountStats, 6 | IAsset, 7 | IAssetStats, 8 | ICollection, 9 | ICollectionStats, 10 | IConfig, 11 | ILog, IOffer, 12 | ISchema, 13 | ISchemaStats, 14 | ITemplate, 15 | ITemplateStats, ITransfer 16 | } from './Objects'; 17 | import { 18 | AccountApiParams, 19 | AssetsApiParams, 20 | CollectionApiParams, GreylistParams, HideOffersParams, 21 | OfferApiParams, 22 | SchemaApiParams, 23 | TemplateApiParams, 24 | TransferApiParams 25 | } from './Params'; 26 | 27 | type Fetch = (input?: Request | string, init?: RequestInit) => Promise; 28 | type ApiArgs = { fetch?: Fetch }; 29 | 30 | export type DataOptions = Array<{key: string, value: any, type?: string}>; 31 | 32 | function buildDataOptions(options: {[key: string]: any}, data: DataOptions): {[key: string]: any} { 33 | const dataFields: {[key: string]: string} = {}; 34 | 35 | for (const row of data) { 36 | const dataType = row.type ?? 'data'; 37 | 38 | if (typeof row.value === 'number') { 39 | dataFields[dataType + ':number.' + row.key] = String(row.value); 40 | } else if (typeof row.value === 'boolean') { 41 | dataFields[dataType + ':bool.' + row.key] = row.value ? 'true' : 'false'; 42 | } else { 43 | dataFields[dataType + '.' + row.key] = row.value; 44 | } 45 | } 46 | 47 | return Object.assign({}, options, dataFields); 48 | } 49 | 50 | export default class ExplorerApi { 51 | readonly action: Promise; 52 | 53 | private readonly endpoint: string; 54 | private readonly namespace: string; 55 | 56 | private readonly fetchBuiltin: Fetch; 57 | 58 | constructor(endpoint: string, namespace: string, args: ApiArgs) { 59 | this.endpoint = endpoint; 60 | this.namespace = namespace; 61 | 62 | if (args.fetch) { 63 | this.fetchBuiltin = args.fetch; 64 | } else { 65 | this.fetchBuiltin = window.fetch; 66 | } 67 | 68 | this.action = (async () => { 69 | return new ExplorerActionGenerator((await this.getConfig()).contract, this); 70 | })(); 71 | } 72 | 73 | async getConfig(): Promise { 74 | return await this.fetchEndpoint('/v1/config', {}); 75 | } 76 | 77 | async getAssets(options: AssetsApiParams = {}, page: number = 1, limit: number = 100, data: DataOptions = []): Promise { 78 | return await this.fetchEndpoint('/v1/assets', {page, limit, ...buildDataOptions(options, data)}); 79 | } 80 | 81 | async countAssets(options: AssetsApiParams, data: DataOptions = []): Promise { 82 | return await this.countEndpoint('/v1/assets', buildDataOptions(options, data)); 83 | } 84 | 85 | async getAsset(id: string): Promise { 86 | return await this.fetchEndpoint('/v1/assets/' + id, {}); 87 | } 88 | 89 | async getAssetStats(id: string): Promise { 90 | return await this.fetchEndpoint('/v1/assets/' + id + '/stats', {}); 91 | } 92 | 93 | async getAssetLogs(id: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise { 94 | return await this.fetchEndpoint('/v1/assets/' + id + '/logs', {page, limit, order}); 95 | } 96 | 97 | async getCollections(options: CollectionApiParams = {}, page: number = 1, limit: number = 100): Promise { 98 | return await this.fetchEndpoint('/v1/collections', {page, limit, ...options}); 99 | } 100 | 101 | async countCollections(options: CollectionApiParams = {}): Promise { 102 | return await this.countEndpoint('/v1/collections', options); 103 | } 104 | 105 | async getCollection(name: string): Promise { 106 | return await this.fetchEndpoint('/v1/collections/' + name, {}); 107 | } 108 | 109 | async getCollectionStats(name: string): Promise { 110 | return await this.fetchEndpoint('/v1/collections/' + name + '/stats', {}); 111 | } 112 | 113 | async getCollectionLogs(name: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise { 114 | return await this.fetchEndpoint('/v1/collections/' + name + '/logs', {page, limit, order}); 115 | } 116 | 117 | async getSchemas(options: SchemaApiParams = {}, page: number = 1, limit: number = 100): Promise { 118 | return await this.fetchEndpoint('/v1/schemas', {page, limit, ...options}); 119 | } 120 | 121 | async countSchemas(options: SchemaApiParams = {}): Promise { 122 | return await this.countEndpoint('/v1/schemas', options); 123 | } 124 | 125 | async getSchema(collection: string, name: string): Promise { 126 | return await this.fetchEndpoint('/v1/schemas/' + collection + '/' + name, {}); 127 | } 128 | 129 | async getSchemaStats(collection: string, name: string): Promise { 130 | return await this.fetchEndpoint('/v1/schemas/' + collection + '/' + name + '/stats', {}); 131 | } 132 | 133 | async getSchemaLogs(collection: string, name: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise { 134 | return await this.fetchEndpoint('/v1/schemas/' + collection + '/' + name + '/logs', {page, limit, order}); 135 | } 136 | 137 | async getTemplates(options: TemplateApiParams = {}, page: number = 1, limit: number = 100, data: DataOptions = []): Promise { 138 | return await this.fetchEndpoint('/v1/templates', {page, limit, ...buildDataOptions(options, data)}); 139 | } 140 | 141 | async countTemplates(options: TemplateApiParams = {}, data: DataOptions = []): Promise { 142 | return await this.countEndpoint('/v1/templates', buildDataOptions(options, data)); 143 | } 144 | 145 | async getTemplate(collection: string, id: string): Promise { 146 | return await this.fetchEndpoint('/v1/templates/' + collection + '/' + id, {}); 147 | } 148 | 149 | async getTemplateStats(collection: string, name: string): Promise { 150 | return await this.fetchEndpoint('/v1/templates/' + collection + '/' + name + '/stats', {}); 151 | } 152 | 153 | async getTemplateLogs(collection: string, id: string, page: number = 1, limit: number = 100, order: string = 'desc'): Promise { 154 | return await this.fetchEndpoint('/v1/templates/' + collection + '/' + id + '/logs', {page, limit, order}); 155 | } 156 | 157 | async getTransfers(options: TransferApiParams = {}, page: number = 1, limit: number = 100): Promise { 158 | return await this.fetchEndpoint('/v1/transfers', {page, limit, ...options}); 159 | } 160 | 161 | async countTransfers(options: TransferApiParams = {}): Promise { 162 | return await this.countEndpoint('/v1/transfers', options); 163 | } 164 | 165 | async getOffers(options: OfferApiParams = {}, page: number = 1, limit: number = 100): Promise { 166 | return await this.fetchEndpoint('/v1/offers', {page, limit, ...options}); 167 | } 168 | 169 | async countOffers(options: OfferApiParams = {}): Promise { 170 | return await this.countEndpoint('/v1/offers', options); 171 | } 172 | 173 | async getOffer(id: string): Promise { 174 | return await this.fetchEndpoint('/v1/offers/' + id, {}); 175 | } 176 | 177 | async getAccounts(options: AccountApiParams = {}, page: number = 1, limit: number = 100): Promise> { 178 | return await this.fetchEndpoint('/v1/accounts', {page, limit, ...options}); 179 | } 180 | 181 | async getBurns(options: AccountApiParams = {}, page: number = 1, limit: number = 100): Promise> { 182 | return await this.fetchEndpoint('/v1/burns', {page, limit, ...options}); 183 | } 184 | 185 | async countAccounts(options: AccountApiParams = {}): Promise { 186 | return await this.countEndpoint('/v1/accounts', options); 187 | } 188 | 189 | async getAccount(account: string, options: GreylistParams & HideOffersParams = {}): Promise { 190 | return await this.fetchEndpoint('/v1/accounts/' + account, options); 191 | } 192 | 193 | async getAccountCollection(account: string, collection: string): Promise { 194 | return await this.fetchEndpoint('/v1/accounts/' + account + '/' + collection, {}); 195 | } 196 | 197 | async getAccountBurns(account: string, options: GreylistParams & HideOffersParams = {}): Promise { 198 | return await this.fetchEndpoint('/v1/burns/' + account, options); 199 | } 200 | 201 | async fetchEndpoint(path: string, args: any): Promise { 202 | let response, json; 203 | 204 | const f = this.fetchBuiltin; 205 | const queryString = Object.keys(args).map((key) => { 206 | let value = args[key]; 207 | 208 | if (value === true) { 209 | value = 'true'; 210 | } 211 | 212 | if (value === false) { 213 | value = 'false'; 214 | } 215 | 216 | return key + '=' + encodeURIComponent(value); 217 | }).join('&'); 218 | 219 | try { 220 | if ( queryString.length < 1000 ) { 221 | response = await f(this.endpoint + '/' + this.namespace + path + (queryString.length > 0 ? '?' + queryString : '')); 222 | } 223 | else { 224 | response = await f( 225 | this.endpoint + '/' + this.namespace + path, 226 | { 227 | headers: { 228 | 'accept': '*.*', 229 | 'content-type': 'application/json' 230 | }, 231 | method: 'POST', 232 | body: JSON.stringify(args) 233 | } 234 | ); 235 | } 236 | 237 | json = await response.json(); 238 | } catch (e: any) { 239 | throw new ApiError(e.message, 500); 240 | } 241 | 242 | if (response.status !== 200) { 243 | throw new ApiError(json.message, response.status); 244 | } 245 | 246 | if (!json.success) { 247 | throw new ApiError(json.message, response.status); 248 | } 249 | 250 | return json.data; 251 | } 252 | 253 | async countEndpoint(path: string, args: any): Promise { 254 | const res = await this.fetchEndpoint(path + '/_count', args); 255 | 256 | return parseInt(res, 10); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/API/Rpc/Asset.ts: -------------------------------------------------------------------------------- 1 | import { deserialize } from '../../Serialization'; 2 | import { IAssetRow } from './RpcCache'; 3 | import RpcCollection from './Collection'; 4 | import RpcApi from './index'; 5 | import RpcSchema from './Schema'; 6 | import RpcTemplate from './Template'; 7 | 8 | export default class RpcAsset { 9 | readonly owner: string; 10 | readonly id: string; 11 | 12 | // tslint:disable-next-line:variable-name 13 | private readonly _data: Promise; 14 | // tslint:disable-next-line:variable-name 15 | private readonly _template: Promise; 16 | // tslint:disable-next-line:variable-name 17 | private readonly _collection: Promise; 18 | // tslint:disable-next-line:variable-name 19 | private readonly _schema: Promise; 20 | 21 | constructor( 22 | private readonly api: RpcApi, 23 | owner: string, 24 | id: string, 25 | data?: IAssetRow, 26 | collection?: RpcCollection, 27 | schema?: RpcSchema, 28 | template?: RpcTemplate, 29 | cache: boolean = true 30 | ) { 31 | this.owner = owner; 32 | this.id = id; 33 | 34 | this._data = new Promise(async (resolve, reject) => { 35 | if (data) { 36 | resolve(data); 37 | } else { 38 | try { 39 | resolve(await api.queue.fetchAsset(owner, id, cache)); 40 | } catch (e) { 41 | reject(e); 42 | } 43 | } 44 | }); 45 | 46 | this._template = new Promise(async (resolve, reject) => { 47 | if (template) { 48 | resolve(template); 49 | } else { 50 | try { 51 | const row = await this._data; 52 | 53 | if (Number(row.template_id) < 0) { 54 | return resolve(null); 55 | } 56 | 57 | resolve(new RpcTemplate(api, row.collection_name, row.template_id, undefined, undefined, cache)); 58 | } catch (e) { 59 | reject(e); 60 | } 61 | } 62 | }); 63 | 64 | this._collection = new Promise(async (resolve, reject) => { 65 | if (collection) { 66 | resolve(collection); 67 | } else { 68 | try { 69 | const row = await this._data; 70 | 71 | resolve(new RpcCollection(api, row.collection_name, undefined, cache)); 72 | } catch (e) { 73 | reject(e); 74 | } 75 | } 76 | }); 77 | 78 | this._schema = new Promise(async (resolve, reject) => { 79 | if (schema) { 80 | resolve(schema); 81 | } else { 82 | try { 83 | const row = await this._data; 84 | 85 | resolve(new RpcSchema(api, row.collection_name, row.schema_name, undefined, cache)); 86 | } catch (e) { 87 | reject(e); 88 | } 89 | } 90 | }); 91 | } 92 | 93 | async template(): Promise { 94 | return await this._template; 95 | } 96 | 97 | async collection(): Promise { 98 | return await this._collection; 99 | } 100 | 101 | async schema(): Promise { 102 | return await this._schema; 103 | } 104 | 105 | async backedTokens(): Promise { 106 | return (await this._data).backed_tokens; 107 | } 108 | 109 | async immutableData(): Promise { 110 | const schema = await this.schema(); 111 | const row = await this._data; 112 | 113 | return deserialize(row.immutable_serialized_data, await schema.format()); 114 | } 115 | 116 | async mutableData(): Promise { 117 | const schema = await this.schema(); 118 | const row = await this._data; 119 | 120 | return deserialize(row.mutable_serialized_data, await schema.format()); 121 | } 122 | 123 | async data(): Promise { 124 | const mutableData = await this.mutableData(); 125 | const immutableData = await this.immutableData(); 126 | 127 | const template = await this.template(); 128 | const templateData = template ? await template.immutableData() : {}; 129 | 130 | return Object.assign({}, mutableData, immutableData, templateData); 131 | } 132 | 133 | async toObject(): Promise { 134 | const template = await this.template(); 135 | const collection = await this.collection(); 136 | const schema = await this.schema(); 137 | 138 | return { 139 | asset_id: this.id, 140 | 141 | collection: await collection.toObject(), 142 | schema: await schema.toObject(), 143 | template: template ? await template.toObject() : null, 144 | 145 | backedTokens: await this.backedTokens(), 146 | immutableData: await this.immutableData(), 147 | mutableData: await this.mutableData(), 148 | data: await this.data() 149 | }; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/API/Rpc/BaseCache.ts: -------------------------------------------------------------------------------- 1 | export interface ICache { 2 | get(key: string): T | null; 3 | put(key: string, value: any, timeout: number): void; 4 | remove(key: string): void; 5 | } 6 | 7 | export class MemoryCache implements ICache { 8 | private readonly store: any; 9 | 10 | constructor() { 11 | this.store = {}; 12 | } 13 | 14 | get(key: string): T | null { 15 | const res = this.store[key]; 16 | 17 | if (typeof res === 'undefined') { 18 | return null; 19 | } 20 | 21 | const [value, expires] = res; 22 | 23 | if (new Date() > expires) { 24 | delete this.store[key]; 25 | return null; 26 | } 27 | 28 | return value; 29 | } 30 | 31 | put(key: string, value: any, timeout: number): void { 32 | const expires = new Date(Date.now() + timeout); 33 | 34 | this.store[key] = [value, expires]; 35 | } 36 | 37 | remove(key: string): void { 38 | delete this.store[key]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/API/Rpc/Collection.ts: -------------------------------------------------------------------------------- 1 | import { ObjectSchema } from '../../Schema'; 2 | import { deserialize } from '../../Serialization'; 3 | import { ICollectionRow } from './RpcCache'; 4 | import RpcApi from './index'; 5 | 6 | export default class RpcCollection { 7 | readonly name: string; 8 | 9 | // tslint:disable-next-line:variable-name 10 | private readonly _data: Promise; 11 | 12 | constructor(private readonly api: RpcApi, name: string, data?: ICollectionRow, cache: boolean = true) { 13 | this.name = name; 14 | 15 | this._data = new Promise(async (resolve, reject) => { 16 | if (data) { 17 | resolve(data); 18 | } else { 19 | try { 20 | resolve(await api.queue.fetchCollection(name, cache)); 21 | } catch (e) { 22 | reject(e); 23 | } 24 | } 25 | }); 26 | } 27 | 28 | async author(): Promise { 29 | return (await this._data).author; 30 | } 31 | 32 | async allowNotify(): Promise { 33 | return (await this._data).allow_notify; 34 | } 35 | 36 | async authorizedAccounts(): Promise { 37 | return (await this._data).authorized_accounts; 38 | } 39 | 40 | async notifyAccounts(): Promise { 41 | return (await this._data).notify_accounts; 42 | } 43 | 44 | async marketFee(): Promise { 45 | return Number((await this._data).market_fee); 46 | } 47 | 48 | async data(): Promise { 49 | return deserialize((await this._data).serialized_data, ObjectSchema((await this.api.config()).collection_format)); 50 | } 51 | 52 | async toObject(): Promise { 53 | return { 54 | collection_name: this.name, 55 | 56 | author: await this.author(), 57 | allowNotify: await this.allowNotify(), 58 | authorizedAccounts: await this.authorizedAccounts(), 59 | notifyAccounts: await this.notifyAccounts(), 60 | marketFee: await this.marketFee(), 61 | 62 | data: await this.data() 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/API/Rpc/Offer.ts: -------------------------------------------------------------------------------- 1 | import RpcAsset from './Asset'; 2 | import { IAssetRow, IOfferRow } from './RpcCache'; 3 | import RpcApi from './index'; 4 | 5 | export default class RpcOffer { 6 | readonly id: string; 7 | 8 | // tslint:disable-next-line:variable-name 9 | private readonly _data: Promise; 10 | // tslint:disable-next-line:variable-name 11 | private readonly _senderAssets: Promise>; 12 | // tslint:disable-next-line:variable-name 13 | private readonly _recipientAssets: Promise>; 14 | 15 | constructor(private readonly api: RpcApi, id: string, data?: IOfferRow, senderAssets?: RpcAsset[], receiverAssets?: RpcAsset[], cache: boolean = true) { 16 | this.id = id; 17 | 18 | this._data = new Promise(async (resolve, reject) => { 19 | if (data) { 20 | resolve(data); 21 | } else { 22 | try { 23 | resolve(await this.api.queue.fetchOffer(id, cache)); 24 | } catch (e) { 25 | reject(e); 26 | } 27 | } 28 | }); 29 | 30 | this._senderAssets = new Promise(async (resolve, reject) => { 31 | if (senderAssets) { 32 | resolve(senderAssets); 33 | } else { 34 | try { 35 | const row: IOfferRow = await this._data; 36 | const inventory: IAssetRow[] = await this.api.queue.fetchAccountAssets(row.sender); 37 | 38 | return resolve(row.sender_asset_ids.map((assetID) => { 39 | const asset = inventory.find((assetRow) => assetRow.asset_id === assetID); 40 | 41 | return asset ? new RpcAsset(this.api, row.sender, assetID, asset, undefined, undefined, undefined, cache) : assetID; 42 | })); 43 | } catch (e) { 44 | return reject(e); 45 | } 46 | } 47 | }); 48 | 49 | this._recipientAssets = new Promise(async (resolve, reject) => { 50 | if (receiverAssets) { 51 | resolve(receiverAssets); 52 | } else { 53 | try { 54 | const row: IOfferRow = await this._data; 55 | const inventory: IAssetRow[] = await this.api.queue.fetchAccountAssets(row.recipient); 56 | 57 | return resolve(row.recipient_asset_ids.map((assetID) => { 58 | const asset = inventory.find((assetRow) => assetRow.asset_id === assetID); 59 | 60 | return asset ? new RpcAsset(this.api, row.recipient, assetID, asset, undefined, undefined, undefined, cache) : assetID; 61 | })); 62 | } catch (e) { 63 | return reject(e); 64 | } 65 | } 66 | }); 67 | } 68 | 69 | async sender(): Promise { 70 | return (await this._data).sender; 71 | } 72 | 73 | async recipient(): Promise { 74 | return (await this._data).recipient; 75 | } 76 | 77 | async senderAssets(): Promise> { 78 | return await this._senderAssets; 79 | } 80 | 81 | async recipientAssets(): Promise> { 82 | return await this._recipientAssets; 83 | } 84 | 85 | async memo(): Promise { 86 | return (await this._data).memo; 87 | } 88 | 89 | async toObject(): Promise { 90 | return { 91 | offer_id: this.id, 92 | sender: { 93 | account: await this.sender(), 94 | assets: await Promise.all((await this.senderAssets()).map(async (asset) => typeof asset === 'string' ? asset : await asset.toObject())) 95 | }, 96 | recipient: { 97 | account: await this.recipient(), 98 | assets: await Promise.all((await this.recipientAssets()).map(async (asset) => typeof asset === 'string' ? asset : await asset.toObject())) 99 | }, 100 | memo: await this.memo() 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/API/Rpc/Queue.ts: -------------------------------------------------------------------------------- 1 | import { IAssetRow, ICollectionRow, IOfferRow, ISchemaRow, ITemplateRow } from './RpcCache'; 2 | import RpcApi from './index'; 3 | 4 | export default class RpcQueue { 5 | private elements: any[] = []; 6 | private interval: any = null; 7 | 8 | private preloadedCollections: {[key: string]: number} = {}; 9 | 10 | constructor(private readonly api: RpcApi, private readonly requestLimit: number = 4) { } 11 | 12 | async fetchAsset(owner: string, assetID: string, useCache: boolean = true): Promise { 13 | return await this.fetch_single_row('assets', owner, assetID, (data?: IAssetRow) => { 14 | return (useCache || typeof data !== 'undefined') ? this.api.cache.getAsset(assetID, data) : null; 15 | }); 16 | } 17 | 18 | async fetchAccountAssets(account: string): Promise { 19 | const rows = await this.fetch_all_rows('assets', account, 'asset_id'); 20 | 21 | return rows.map((asset) => { 22 | return this.api.cache.getAsset(asset.asset_id, asset); 23 | }); 24 | } 25 | 26 | async fetchTemplate(collectionName: string, templateID: string, useCache: boolean = true): Promise { 27 | return await this.fetch_single_row('templates', collectionName, templateID, (data?: ITemplateRow) => { 28 | return (useCache || typeof data !== 'undefined') ? this.api.cache.getTemplate(collectionName, templateID, data) : null; 29 | }); 30 | } 31 | 32 | async fetchSchema(collectionName: string, schemaName: string, useCache: boolean = true): Promise { 33 | return await this.fetch_single_row('schemas', collectionName, schemaName, (data?: ISchemaRow) => { 34 | return (useCache || typeof data !== 'undefined') ? this.api.cache.getSchema(collectionName, schemaName, data) : null; 35 | }); 36 | } 37 | 38 | async fetchCollection(collectionName: string, useCache: boolean = true): Promise { 39 | return await this.fetch_single_row('collections', this.api.contract, collectionName, (data?: ICollectionRow) => { 40 | return (useCache || typeof data !== 'undefined') ? this.api.cache.getCollection(collectionName, data) : null; 41 | }); 42 | } 43 | 44 | async fetchCollectionSchemas(collectionName: string): Promise { 45 | const rows = await this.fetch_all_rows('schemas', collectionName, 'schema_name'); 46 | 47 | return rows.map((schema) => { 48 | return this.api.cache.getSchema(collectionName, schema.schema_name, schema); 49 | }); 50 | } 51 | 52 | async fetchCollectionTemplates(collectionName: string): Promise { 53 | const rows = await this.fetch_all_rows('templates', collectionName, 'template_id'); 54 | 55 | return rows.map((template) => { 56 | return this.api.cache.getTemplate(collectionName, String(template.template_id), template); 57 | }); 58 | } 59 | 60 | async preloadCollection(collectionName: string, useCache: boolean = true): Promise { 61 | if (!useCache || !this.preloadedCollections[collectionName] || this.preloadedCollections[collectionName] + 15 * 60 * 1000 < Date.now()) { 62 | await this.fetchCollectionSchemas(collectionName); 63 | await this.fetchCollectionTemplates(collectionName); 64 | } 65 | } 66 | 67 | async fetchOffer(offerID: string, useCache: boolean = true): Promise { 68 | return await this.fetch_single_row('offers', this.api.contract, offerID, (data?: IOfferRow) => { 69 | return (useCache || typeof data !== 'undefined') ? this.api.cache.getOffer(offerID, data) : null; 70 | }); 71 | } 72 | 73 | async fetchAccountOffers(account: string): Promise { 74 | const rows: any[][] = await Promise.all([ 75 | this.fetch_all_rows( 76 | 'offers', this.api.contract, 'offer_sender', account, account, 2, 'name' 77 | ), 78 | this.fetch_all_rows( 79 | 'offers', this.api.contract, 'offer_recipient', account, account, 3, 'name' 80 | ) 81 | ]); 82 | 83 | const offers: IOfferRow[] = rows[0].concat(rows[1]); 84 | 85 | return offers.map((offer) => { 86 | return this.api.cache.getOffer(offer.offer_id, offer); 87 | }); 88 | } 89 | 90 | private dequeue(): void { 91 | if (this.interval) { 92 | return; 93 | } 94 | 95 | this.interval = setInterval(async () => { 96 | if (this.elements.length > 0) { 97 | this.elements.shift()(); 98 | } else { 99 | clearInterval(this.interval); 100 | this.interval = null; 101 | } 102 | }, Math.ceil(1000 / this.requestLimit)); 103 | } 104 | 105 | private async fetch_single_row( 106 | table: string, scope: string, match: any, 107 | cacheFn: (data?: T) => T | null, 108 | indexPosition: number = 1, keyType: string = '' 109 | ): Promise { 110 | return new Promise((resolve, reject) => { 111 | let data = cacheFn(); 112 | 113 | if (data !== null) { 114 | return resolve(data); 115 | } 116 | 117 | this.elements.push(async () => { 118 | data = cacheFn(); 119 | 120 | if (data !== null) { 121 | return resolve(data); 122 | } 123 | 124 | try { 125 | const options = { 126 | code: this.api.contract, table, scope, 127 | limit: 1, lower_bound: match, upper_bound: match, 128 | index_position: indexPosition, key_type: keyType 129 | }; 130 | 131 | const resp = await this.api.getTableRows(options); 132 | 133 | if (resp.rows.length === 0) { 134 | return reject(new Error('Row not found for ' + JSON.stringify(options))); 135 | } 136 | 137 | return resolve(cacheFn(resp.rows[0])); 138 | } catch (e) { 139 | return reject(e); 140 | } 141 | }); 142 | 143 | this.dequeue(); 144 | }); 145 | } 146 | 147 | private async fetch_all_rows( 148 | table: string, scope: string, tableKey: string, 149 | lowerBound: string = '', upperBound: string = '', 150 | indexPosition: number = 1, keyType: string = '' 151 | ): Promise { 152 | return new Promise(async (resolve, reject) => { 153 | this.elements.push(async () => { 154 | const resp: { more: boolean, rows: any[] } = await this.api.getTableRows({ 155 | code: this.api.contract, scope, table, 156 | lower_bound: lowerBound, upper_bound: upperBound, limit: 1000, 157 | index_position: indexPosition, key_type: keyType 158 | }); 159 | 160 | if (resp.more && indexPosition === 1) { 161 | this.elements.unshift(async () => { 162 | try { 163 | const next = await this.fetch_all_rows( 164 | table, scope, tableKey, 165 | resp.rows[resp.rows.length - 1][tableKey], 166 | upperBound, indexPosition, keyType 167 | ); 168 | 169 | if (next.length > 0) { 170 | next.shift(); 171 | } 172 | 173 | resolve(resp.rows.concat(next)); 174 | } catch (e) { 175 | reject(e); 176 | } 177 | }); 178 | 179 | this.dequeue(); 180 | } else { 181 | resolve(resp.rows); 182 | } 183 | }); 184 | 185 | this.dequeue(); 186 | }); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/API/Rpc/RpcCache.ts: -------------------------------------------------------------------------------- 1 | import { ICache, MemoryCache } from './BaseCache'; 2 | 3 | export type SchemaFormat = Array<{ name: string, type: string }>; 4 | 5 | export interface ICollectionRow { 6 | collection_name: string; 7 | author: string; 8 | allow_notify: boolean; 9 | authorized_accounts: string[]; 10 | notify_accounts: string[]; 11 | market_fee: number; 12 | serialized_data: Uint8Array; 13 | } 14 | 15 | export interface ISchemaRow { 16 | schema_name: string; 17 | format: SchemaFormat; 18 | } 19 | 20 | export interface ITemplateRow { 21 | template_id: number; 22 | schema_name: string; 23 | transferable: boolean; 24 | burnable: boolean; 25 | max_supply: number; 26 | issued_supply: number; 27 | immutable_serialized_data: Uint8Array; 28 | } 29 | 30 | export interface IAssetRow { 31 | asset_id: string; 32 | collection_name: string; 33 | schema_name: string; 34 | template_id: string; 35 | ram_payer: string; 36 | backed_tokens: string[]; 37 | immutable_serialized_data: Uint8Array; 38 | mutable_serialized_data: Uint8Array; 39 | } 40 | 41 | export interface IOfferRow { 42 | offer_id: string; 43 | sender: string; 44 | recipient: string; 45 | sender_asset_ids: string[]; 46 | recipient_asset_ids: string[]; 47 | memo: string; 48 | } 49 | 50 | export interface IConfigRow { 51 | asset_counter: string; 52 | offer_counter: string; 53 | collection_format: SchemaFormat; 54 | } 55 | 56 | export default class RpcCache { 57 | private readonly cache: ICache; 58 | 59 | constructor() { 60 | this.cache = new MemoryCache(); 61 | } 62 | 63 | getAsset(assetID: string, data?: IAssetRow): IAssetRow | null { 64 | if (data) { 65 | data.mutable_serialized_data = new Uint8Array(data.mutable_serialized_data); 66 | data.immutable_serialized_data = new Uint8Array(data.immutable_serialized_data); 67 | } 68 | 69 | return this.access('assets', assetID, data); 70 | } 71 | 72 | deleteAsset(assetID: string): void { 73 | this.delete('assets', assetID); 74 | } 75 | 76 | getTemplate(collectionName: string, templateID: string, data?: ITemplateRow): ITemplateRow | null { 77 | if (data) { 78 | data.immutable_serialized_data = new Uint8Array(data.immutable_serialized_data); 79 | } 80 | 81 | return this.access('templates', collectionName + ':' + templateID, data); 82 | } 83 | 84 | deleteTemplate(collectionName: string, templateID: string): void { 85 | this.delete('templates', collectionName + ':' + templateID); 86 | } 87 | 88 | getSchema(collectionName: string, schemaName: string, data?: ISchemaRow): ISchemaRow | null { 89 | return this.access('schemas', collectionName + ':' + schemaName, data); 90 | } 91 | 92 | deleteSchema(collectionName: string, schemaName: string): void { 93 | this.delete('schemas', collectionName + ':' + schemaName); 94 | } 95 | 96 | getCollection(collectionName: string, data?: ICollectionRow): ICollectionRow | null { 97 | return this.access('collections', collectionName, data); 98 | } 99 | 100 | deleteCollection(collectionName: string): void { 101 | this.delete('collections', collectionName); 102 | } 103 | 104 | getOffer(offerID: string, data?: IOfferRow): IOfferRow | null { 105 | return this.access('offers', offerID, data); 106 | } 107 | 108 | deleteOffer(offerID: string): void { 109 | this.delete('offers', offerID); 110 | } 111 | 112 | private access(namespace: any, identifier: any, data?: T): T | null { 113 | if (typeof data === 'undefined') { 114 | const cache = this.cache.get(namespace + ':' + identifier); 115 | 116 | return cache === null ? null : cache; 117 | } 118 | 119 | this.cache.put(namespace + ':' + identifier, data, 15 * 60 * 1000); 120 | 121 | return data; 122 | } 123 | 124 | private delete(namespace: any, identifier: any): void { 125 | this.cache.remove(namespace + ':' + identifier); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/API/Rpc/Schema.ts: -------------------------------------------------------------------------------- 1 | import { ISchema, ObjectSchema, SchemaObject } from '../../Schema'; 2 | import { ISchemaRow } from './RpcCache'; 3 | import RpcCollection from './Collection'; 4 | import RpcApi from './index'; 5 | 6 | export default class RpcSchema { 7 | readonly collection: string; 8 | readonly name: string; 9 | 10 | // tslint:disable-next-line:variable-name 11 | private readonly _data: Promise; 12 | // tslint:disable-next-line:variable-name 13 | private readonly _collection: Promise; 14 | 15 | constructor(private readonly api: RpcApi, collection: string, name: string, data?: ISchemaRow, cache: boolean = true) { 16 | this.collection = collection; 17 | this.name = name; 18 | 19 | this._data = new Promise(async (resolve, reject) => { 20 | if (data) { 21 | resolve(data); 22 | } else { 23 | try { 24 | resolve(await api.queue.fetchSchema(collection, name, cache)); 25 | } catch (e) { 26 | reject(e); 27 | } 28 | } 29 | }); 30 | 31 | this._collection = new Promise(async (resolve, reject) => { 32 | try { 33 | resolve(new RpcCollection(api, collection, undefined, cache)); 34 | } catch (e) { 35 | reject(e); 36 | } 37 | }); 38 | } 39 | 40 | async format(): Promise { 41 | return ObjectSchema((await this._data).format); 42 | } 43 | 44 | async rawFormat(): Promise { 45 | return (await this._data).format; 46 | } 47 | 48 | async toObject(): Promise { 49 | return { 50 | collection_name: this.collection, 51 | schema_name: this.name, 52 | format: await this.rawFormat() 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/API/Rpc/Template.ts: -------------------------------------------------------------------------------- 1 | import { deserialize } from '../../Serialization'; 2 | import { ITemplateRow } from './RpcCache'; 3 | import RpcApi from './index'; 4 | import RpcSchema from './Schema'; 5 | 6 | export default class RpcTemplate { 7 | readonly collection: string; 8 | readonly id: string; 9 | 10 | // tslint:disable-next-line:variable-name 11 | private readonly _data: Promise; 12 | 13 | // tslint:disable-next-line:variable-name 14 | private readonly _schema: Promise; 15 | 16 | constructor(private readonly api: RpcApi, collection: string, id: string, data?: ITemplateRow, schema?: RpcSchema, cache: boolean = true) { 17 | this.collection = collection; 18 | this.id = id; 19 | 20 | this._data = new Promise(async (resolve, reject) => { 21 | if (data) { 22 | resolve(data); 23 | } else { 24 | try { 25 | resolve(await api.queue.fetchTemplate(collection, id, cache)); 26 | } catch (e) { 27 | reject(e); 28 | } 29 | } 30 | }); 31 | 32 | this._schema = new Promise(async (resolve, reject) => { 33 | if (schema) { 34 | resolve(schema); 35 | } else { 36 | try { 37 | const row = await this._data; 38 | 39 | resolve(new RpcSchema(this.api, collection, row.schema_name, undefined, cache)); 40 | } catch (e) { 41 | reject(e); 42 | } 43 | } 44 | }); 45 | } 46 | 47 | async schema(): Promise { 48 | return await this._schema; 49 | } 50 | 51 | async immutableData(): Promise { 52 | const schema = await this._schema; 53 | 54 | return deserialize((await this._data).immutable_serialized_data, await schema.format()); 55 | } 56 | 57 | async isTransferable(): Promise { 58 | return (await this._data).transferable; 59 | } 60 | 61 | async isBurnable(): Promise { 62 | return (await this._data).burnable; 63 | } 64 | 65 | async maxSupply(): Promise { 66 | return (await this._data).max_supply; 67 | } 68 | 69 | async circulation(): Promise { 70 | return (await this._data).issued_supply; 71 | } 72 | 73 | async toObject(): Promise { 74 | return { 75 | collection_name: this.collection, 76 | template_id: this.id, 77 | 78 | schema: await (await this.schema()).toObject(), 79 | immutableData: await this.immutableData(), 80 | transferable: await this.isTransferable(), 81 | burnable: await this.isBurnable(), 82 | maxSupply: await this.maxSupply(), 83 | circulation: await this.circulation() 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/API/Rpc/index.ts: -------------------------------------------------------------------------------- 1 | import RpcActionGenerator from '../../Actions/Rpc'; 2 | import RpcError from '../../Errors/RpcError'; 3 | import RpcAsset from './Asset'; 4 | import RpcCache, { IConfigRow } from './RpcCache'; 5 | import RpcCollection from './Collection'; 6 | import RpcOffer from './Offer'; 7 | import RpcQueue from './Queue'; 8 | import RpcSchema from './Schema'; 9 | import RpcTemplate from './Template'; 10 | 11 | type Fetch = (input?: Request | string, init?: RequestInit) => Promise; 12 | type ApiArgs = { fetch?: Fetch, rateLimit?: number }; 13 | 14 | export default class RpcApi { 15 | readonly queue: RpcQueue; 16 | readonly cache: RpcCache; 17 | 18 | readonly action: RpcActionGenerator; 19 | 20 | readonly endpoint: string; 21 | readonly contract: string; 22 | 23 | private readonly fetchBuiltin: Fetch; 24 | 25 | // tslint:disable-next-line:variable-name 26 | private readonly _config: Promise; 27 | 28 | constructor(endpoint: string, contract: string, args: ApiArgs = {rateLimit: 4}) { 29 | this.endpoint = endpoint; 30 | this.contract = contract; 31 | 32 | if (args.fetch) { 33 | this.fetchBuiltin = args.fetch; 34 | } else { 35 | this.fetchBuiltin = (global).fetch; 36 | } 37 | 38 | this.queue = new RpcQueue(this, args.rateLimit); 39 | this.cache = new RpcCache(); 40 | this.action = new RpcActionGenerator(this); 41 | 42 | this._config = new Promise((async (resolve, reject) => { 43 | try { 44 | const resp = await this.getTableRows({ 45 | code: this.contract, scope: this.contract, table: 'config' 46 | }); 47 | 48 | if (resp.rows.length !== 1) { 49 | return reject('invalid config'); 50 | } 51 | 52 | return resolve(resp.rows[0]); 53 | } catch (e) { 54 | reject(e); 55 | } 56 | })); 57 | } 58 | 59 | async config(): Promise { 60 | return await this._config; 61 | } 62 | 63 | async getAsset(owner: string, id: string, cache: boolean = true): Promise { 64 | if (!cache) { 65 | this.cache.deleteAsset(id); 66 | } 67 | 68 | const data = await this.queue.fetchAsset(owner, id, cache); 69 | 70 | return new RpcAsset(this, owner, id, data, undefined, undefined, undefined, cache); 71 | } 72 | 73 | async getTemplate(collectionName: string, templateID: string, cache: boolean = true): Promise { 74 | if (!cache) { 75 | this.cache.deleteTemplate(collectionName, templateID); 76 | } 77 | 78 | const data = await this.queue.fetchTemplate(collectionName, templateID, cache); 79 | 80 | return new RpcTemplate(this, collectionName, templateID, data, undefined, cache); 81 | } 82 | 83 | async getCollection(collectionName: string, cache: boolean = true): Promise { 84 | if (!cache) { 85 | this.cache.deleteCollection(collectionName); 86 | } 87 | 88 | const data = await this.queue.fetchCollection(collectionName, cache); 89 | 90 | return new RpcCollection(this, collectionName, data, cache); 91 | } 92 | 93 | async getCollectionTemplates(collectionName: string): Promise { 94 | return (await this.queue.fetchCollectionTemplates(collectionName)).map((templateRow) => { 95 | return new RpcTemplate(this, collectionName, String(templateRow.template_id), templateRow, undefined); 96 | }); 97 | } 98 | 99 | async getCollectionsSchemas(collectionName: string): Promise { 100 | return (await this.queue.fetchCollectionSchemas(collectionName)).map((schemaRow) => { 101 | return new RpcSchema(this, collectionName, schemaRow.schema_name, undefined); 102 | }); 103 | } 104 | 105 | async getSchema(collectionName: string, schemaName: string, cache: boolean = true): Promise { 106 | if (!cache) { 107 | this.cache.deleteSchema(collectionName, schemaName); 108 | } 109 | 110 | const data = await this.queue.fetchSchema(collectionName, schemaName, cache); 111 | 112 | return new RpcSchema(this, collectionName, schemaName, data, cache); 113 | } 114 | 115 | async getOffer(offerID: string, cache: boolean = true): Promise { 116 | if (!cache) { 117 | this.cache.deleteOffer(offerID); 118 | } 119 | 120 | const data = await this.queue.fetchOffer(offerID, cache); 121 | 122 | return new RpcOffer(this, offerID, data, undefined, undefined, cache); 123 | } 124 | 125 | async getAccountOffers(account: string): Promise { 126 | return (await this.queue.fetchAccountOffers(account)).map((offerRow) => { 127 | return new RpcOffer(this, offerRow.offer_id, offerRow, undefined, undefined); 128 | }); 129 | } 130 | 131 | async getAccountAssets(account: string): Promise { 132 | return (await this.queue.fetchAccountAssets(account)).map((assetRow) => { 133 | return new RpcAsset(this, account, assetRow.asset_id, assetRow, undefined, undefined, undefined); 134 | }); 135 | } 136 | 137 | async getCollectionInventory(collectionName: string, account: string): Promise { 138 | await this.queue.preloadCollection(collectionName, true); 139 | 140 | return (await this.queue.fetchAccountAssets(account)) 141 | .filter(assetRow => assetRow.collection_name === collectionName) 142 | .map((assetRow) => { 143 | return new RpcAsset(this, account, assetRow.asset_id, assetRow, undefined, undefined, undefined); 144 | }); 145 | } 146 | 147 | async preloadCollection(collectionName: string, cache: boolean = true): Promise { 148 | await this.queue.preloadCollection(collectionName, cache); 149 | } 150 | 151 | async getTableRows({ 152 | code, scope, table, table_key = '', lower_bound = '', upper_bound = '', 153 | index_position = 1, key_type = '' 154 | }: any): Promise { 155 | return await this.fetchRpc('/v1/chain/get_table_rows', { 156 | code, scope, table, table_key, 157 | lower_bound, upper_bound, index_position, 158 | key_type, limit: 101, reverse: false, show_payer: false, json: true 159 | }); 160 | } 161 | 162 | async fetchRpc(path: string, body: any): Promise { 163 | let response; 164 | let json; 165 | 166 | try { 167 | const f = this.fetchBuiltin; 168 | 169 | response = await f(this.endpoint + path, { 170 | body: JSON.stringify(body), 171 | method: 'POST' 172 | }); 173 | 174 | json = await response.json(); 175 | } catch (e: any) { 176 | e.isFetchError = true; 177 | 178 | throw e; 179 | } 180 | 181 | if ((json.processed && json.processed.except) || !response.ok) { 182 | throw new RpcError(json); 183 | } 184 | 185 | return json; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/Actions/Explorer.ts: -------------------------------------------------------------------------------- 1 | import ExplorerApi from '../API/Explorer'; 2 | import { IConfig } from '../API/Explorer/Objects'; 3 | import { ActionGenerator, EosioActionObject, EosioAuthorizationObject, toAttributeMap } from './Generator'; 4 | 5 | /* tslint:disable:variable-name */ 6 | 7 | export default class ExplorerActionGenerator extends ActionGenerator { 8 | private config: Promise; 9 | 10 | constructor(contract: string, readonly api: ExplorerApi) { 11 | super(contract); 12 | 13 | this.config = api.getConfig(); 14 | } 15 | 16 | async createcol( 17 | authorization: EosioAuthorizationObject[], author: string, collection_name: string, 18 | allow_notify: boolean, authorized_accounts: string[], notify_accounts: string[], market_fee: number, data: object 19 | ): Promise { 20 | return super.createcol( 21 | authorization, author, collection_name, allow_notify, authorized_accounts, notify_accounts, market_fee, 22 | toAttributeMap(data, (await this.config).collection_format) 23 | ); 24 | } 25 | 26 | async createtempl( 27 | authorization: EosioAuthorizationObject[], authorized_creator: string, 28 | collection_name: string, schema_name: string, 29 | transferable: boolean, burnable: boolean, max_supply: string, immutable_data: object 30 | ): Promise { 31 | const schema = await this.api.getSchema(collection_name, schema_name); 32 | 33 | const immutable_attribute_map = toAttributeMap(immutable_data, schema.format); 34 | 35 | return super.createtempl( 36 | authorization, authorized_creator, collection_name, schema_name, 37 | transferable, burnable, max_supply, immutable_attribute_map 38 | ); 39 | } 40 | 41 | async mintasset( 42 | authorization: EosioAuthorizationObject[], authorized_minter: string, 43 | collection_name: string, schema_name: string, template_id: string, 44 | new_owner: string, immutable_data: object, mutable_data: object, tokens_to_back: string[] 45 | ): Promise { 46 | const schema = await this.api.getSchema(collection_name, schema_name); 47 | 48 | const immutable_attribute_map = toAttributeMap(immutable_data, schema.format); 49 | const mutable_attribute_map = toAttributeMap(mutable_data, schema.format); 50 | 51 | return super.mintasset( 52 | authorization, authorized_minter, collection_name, schema_name, template_id, 53 | new_owner, immutable_attribute_map, mutable_attribute_map, tokens_to_back 54 | ); 55 | } 56 | 57 | async setassetdata( 58 | authorization: EosioAuthorizationObject[], authorized_editor: string, owner: string, asset_id: string, mutable_data: object 59 | ): Promise { 60 | const asset = await this.api.getAsset(asset_id); 61 | 62 | const mutable_attribute_map = toAttributeMap(mutable_data, asset.schema.format); 63 | 64 | return super.setassetdata(authorization, authorized_editor, owner, asset_id, mutable_attribute_map); 65 | } 66 | 67 | async setcoldata(authorization: EosioAuthorizationObject[], collection_name: string, data: object): Promise { 68 | const mdata = toAttributeMap(data, (await this.config).collection_format); 69 | 70 | return super.setcoldata(authorization, collection_name, mdata); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Actions/Generator.ts: -------------------------------------------------------------------------------- 1 | import { SchemaFormat } from '../API/Rpc/RpcCache'; 2 | import SerializationError from '../Errors/SerializationError'; 3 | 4 | export type EosioAuthorizationObject = { actor: string, permission: string }; 5 | export type EosioActionObject = { 6 | account: string, 7 | name: string, 8 | authorization: EosioAuthorizationObject[], 9 | data: any 10 | }; 11 | export type AttributeMap = Array<{ key: string, value: [string, any] }>; 12 | export type Format = { name: string, type: string }; 13 | 14 | /* tslint:disable:variable-name */ 15 | 16 | export class ActionGenerator { 17 | constructor(readonly contract: string) { 18 | } 19 | 20 | async acceptoffer(authorization: EosioAuthorizationObject[], offer_id: string): Promise { 21 | return this._pack(authorization, 'acceptoffer', {offer_id}); 22 | } 23 | 24 | async addcolauth(authorization: EosioAuthorizationObject[], collection_name: string, account_to_add: string): Promise { 25 | return this._pack(authorization, 'addcolauth', {collection_name, account_to_add}); 26 | } 27 | 28 | async addconftoken(authorization: EosioAuthorizationObject[], token_contract: string, token_symbol: string): Promise { 29 | return this._pack(authorization, 'addconftoken', {token_contract, token_symbol}); 30 | } 31 | 32 | async addnotifyacc(authorization: EosioAuthorizationObject[], collection_name: string, account_to_add: string): Promise { 33 | return this._pack(authorization, 'addnotifyacc', {collection_name, account_to_add}); 34 | } 35 | 36 | async announcedepo(authorization: EosioAuthorizationObject[], owner: string, symbol_to_announce: string): Promise { 37 | return this._pack(authorization, 'announcedepo', {owner, symbol_to_announce}); 38 | } 39 | 40 | async backasset( 41 | authorization: EosioAuthorizationObject[], payer: string, asset_owner: string, asset_id: string, token_to_back: string 42 | ): Promise { 43 | return this._pack(authorization, 'backasset', {payer, asset_owner, asset_id, token_to_back}); 44 | } 45 | 46 | async burnasset(authorization: EosioAuthorizationObject[], asset_owner: string, asset_id: string): Promise { 47 | return this._pack(authorization, 'burnasset', {asset_owner, asset_id}); 48 | } 49 | 50 | async canceloffer(authorization: EosioAuthorizationObject[], offer_id: string): Promise { 51 | return this._pack(authorization, 'canceloffer', {offer_id}); 52 | } 53 | 54 | async createcol( 55 | authorization: EosioAuthorizationObject[], author: string, collection_name: string, allow_notify: boolean, 56 | authorized_accounts: string[], notify_accounts: string[], market_fee: number, data: AttributeMap 57 | ): Promise { 58 | return this._pack(authorization, 'createcol', { 59 | author, 60 | collection_name, 61 | allow_notify, 62 | authorized_accounts, 63 | notify_accounts, 64 | market_fee, 65 | data 66 | }); 67 | } 68 | 69 | async createoffer( 70 | authorization: EosioAuthorizationObject[], sender: string, recipient: string, 71 | sender_asset_ids: string[], recipient_asset_ids: string[], memo: string 72 | ): Promise { 73 | return this._pack(authorization, 'createoffer', {sender, recipient, sender_asset_ids, recipient_asset_ids, memo}); 74 | } 75 | 76 | async createtempl( 77 | authorization: EosioAuthorizationObject[], authorized_creator: string, collection_name: string, schema_name: string, 78 | transferable: boolean, burnable: boolean, max_supply: string, immutable_data: AttributeMap 79 | ): Promise { 80 | return this._pack(authorization, 'createtempl', { 81 | authorized_creator, collection_name, schema_name, transferable, burnable, max_supply, immutable_data 82 | }); 83 | } 84 | 85 | async createschema( 86 | authorization: EosioAuthorizationObject[], authorized_creator: string, 87 | collection_name: string, schema_name: string, schema_format: Format[] 88 | ): Promise { 89 | return this._pack(authorization, 'createschema', {authorized_creator, collection_name, schema_name, schema_format}); 90 | } 91 | 92 | async declineoffer(authorization: EosioAuthorizationObject[], offer_id: string): Promise { 93 | return this._pack(authorization, 'declineoffer', {offer_id}); 94 | } 95 | 96 | async extendschema( 97 | authorization: EosioAuthorizationObject[], authorized_editor: string, 98 | collection_name: string, schema_name: string, schema_format_extension: Format[] 99 | ): Promise { 100 | return this._pack(authorization, 'extendschema', {authorized_editor, collection_name, schema_name, schema_format_extension}); 101 | } 102 | 103 | async forbidnotify(authorization: EosioAuthorizationObject[], collection_name: string): Promise { 104 | return this._pack(authorization, 'forbidnotify', {collection_name}); 105 | } 106 | 107 | async locktemplate( 108 | authorization: EosioAuthorizationObject[], authorized_editor: string, collection_name: string, template_id: number 109 | ): Promise { 110 | return this._pack(authorization, 'locktemplate', {authorized_editor, collection_name, template_id}); 111 | } 112 | 113 | async mintasset( 114 | authorization: EosioAuthorizationObject[], authorized_minter: string, collection_name: string, schema_name: string, template_id: string, 115 | new_asset_owner: string, immutable_data: AttributeMap, mutable_data: AttributeMap, tokens_to_back: string[] 116 | ): Promise { 117 | return this._pack(authorization, 'mintasset', { 118 | authorized_minter, collection_name, schema_name, template_id, new_asset_owner, immutable_data, mutable_data, tokens_to_back 119 | }); 120 | } 121 | 122 | async payofferram(authorization: EosioAuthorizationObject[], payer: string, offer_id: string): Promise { 123 | return this._pack(authorization, 'payofferram', {payer, offer_id}); 124 | } 125 | 126 | async remcolauth(authorization: EosioAuthorizationObject[], collection_name: string, account_to_remove: string): Promise { 127 | return this._pack(authorization, 'remcolauth', {collection_name, account_to_remove}); 128 | } 129 | 130 | async remnotifyacc(authorization: EosioAuthorizationObject[], collection_name: string, account_to_remove: string): Promise { 131 | return this._pack(authorization, 'remnotifyacc', {collection_name, account_to_remove}); 132 | } 133 | 134 | async setassetdata( 135 | authorization: EosioAuthorizationObject[], authorized_editor: string, 136 | asset_owner: string, asset_id: string, new_mutable_data: AttributeMap 137 | ): Promise { 138 | return this._pack(authorization, 'setassetdata', {authorized_editor, asset_owner, asset_id, new_mutable_data}); 139 | } 140 | 141 | async setcoldata(authorization: EosioAuthorizationObject[], collection_name: string, data: AttributeMap): Promise { 142 | return this._pack(authorization, 'setcoldata', {collection_name, data}); 143 | } 144 | 145 | async setmarketfee(authorization: EosioAuthorizationObject[], collection_name: string, market_fee: number): Promise { 146 | return this._pack(authorization, 'setmarketfee', {collection_name, market_fee}); 147 | } 148 | 149 | async transfer( 150 | authorization: EosioAuthorizationObject[], account_from: string, account_to: string, asset_ids: string[], memo: string 151 | ): Promise { 152 | return this._pack(authorization, 'transfer', {from: account_from, to: account_to, asset_ids, memo}); 153 | } 154 | 155 | async withdraw(authorization: EosioAuthorizationObject[], owner: string, token_to_withdraw: string): Promise { 156 | return this._pack(authorization, 'withdraw', {owner, token_to_withdraw}); 157 | } 158 | 159 | protected _pack(authorization: EosioAuthorizationObject[], name: string, data: any): EosioActionObject[] { 160 | return [{account: this.contract, name, authorization, data}]; 161 | } 162 | } 163 | 164 | export function toAttributeMap(obj: any, schema: SchemaFormat): AttributeMap { 165 | const types: { [id: string]: string } = {}; 166 | const result: AttributeMap = []; 167 | 168 | for (const row of schema) { 169 | types[row.name] = row.type; 170 | } 171 | 172 | const keys = Object.keys(obj); 173 | for (const key of keys) { 174 | if (typeof types[key] === 'undefined') { 175 | throw new SerializationError('field not defined in schema'); 176 | } 177 | 178 | result.push({key, value: [types[key], obj[key]]}); 179 | } 180 | 181 | return result; 182 | } 183 | -------------------------------------------------------------------------------- /src/Actions/Rpc.ts: -------------------------------------------------------------------------------- 1 | import RpcApi from '../API/Rpc'; 2 | import { ActionGenerator, EosioActionObject, EosioAuthorizationObject, toAttributeMap } from './Generator'; 3 | 4 | /* tslint:disable:variable-name */ 5 | 6 | export default class RpcActionGenerator extends ActionGenerator { 7 | constructor(readonly api: RpcApi) { 8 | super(api.contract); 9 | } 10 | 11 | async createcol( 12 | authorization: EosioAuthorizationObject[], author: string, collection_name: string, 13 | allow_notify: boolean, authorized_accounts: string[], notify_accounts: string[], market_fee: number, data: object 14 | ): Promise { 15 | const config = await this.api.config(); 16 | 17 | return super.createcol( 18 | authorization, author, collection_name, allow_notify, authorized_accounts, notify_accounts, market_fee, 19 | toAttributeMap(data, config.collection_format) 20 | ); 21 | } 22 | 23 | async createtempl( 24 | authorization: EosioAuthorizationObject[], authorized_creator: string, 25 | collection_name: string, schema_name: string, 26 | transferable: boolean, burnable: boolean, max_supply: string, immutable_data: object 27 | ): Promise { 28 | const schema = await this.api.getSchema(collection_name, schema_name); 29 | 30 | const immutable_attribute_map = toAttributeMap(immutable_data, await schema.rawFormat()); 31 | 32 | return super.createtempl( 33 | authorization, authorized_creator, collection_name, schema_name, 34 | transferable, burnable, max_supply, immutable_attribute_map 35 | ); 36 | } 37 | 38 | async mintasset( 39 | authorization: EosioAuthorizationObject[], authorized_minter: string, 40 | collection_name: string, schema_name: string, template_id: string, 41 | new_owner: string, immutable_data: object, mutable_data: object, tokens_to_back: string[] 42 | ): Promise { 43 | const template = await this.api.getTemplate(collection_name, template_id); 44 | 45 | const immutable_attribute_map = toAttributeMap(immutable_data, await (await template.schema()).rawFormat()); 46 | const mutable_attribute_map = toAttributeMap(mutable_data, await (await template.schema()).rawFormat()); 47 | 48 | return super.mintasset( 49 | authorization, authorized_minter, collection_name, schema_name, template_id, 50 | new_owner, immutable_attribute_map, mutable_attribute_map, tokens_to_back 51 | ); 52 | } 53 | 54 | async setassetdata( 55 | authorization: EosioAuthorizationObject[], authorized_editor: string, owner: string, asset_id: string, mutable_data: object 56 | ): Promise { 57 | const asset = await this.api.getAsset(owner, asset_id); 58 | const schema = await asset.schema(); 59 | 60 | const mutable_attribute_map = toAttributeMap(mutable_data, await schema.rawFormat()); 61 | 62 | return super.setassetdata(authorization, authorized_editor, owner, asset_id, mutable_attribute_map); 63 | } 64 | 65 | async setcoldata(authorization: EosioAuthorizationObject[], collection_name: string, data: object): Promise { 66 | const mdata = toAttributeMap(data, (await this.api.config()).collection_format); 67 | 68 | return super.setcoldata(authorization, collection_name, mdata); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Errors/ApiError.ts: -------------------------------------------------------------------------------- 1 | export default class ApiError extends Error { 2 | isApiError = true; 3 | 4 | constructor(readonly message: any, readonly status: number) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Errors/DeserializationError.ts: -------------------------------------------------------------------------------- 1 | export default class DeserializationError extends Error { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/Errors/ExplorerError.ts: -------------------------------------------------------------------------------- 1 | export default class RpcError extends Error { } 2 | -------------------------------------------------------------------------------- /src/Errors/RpcError.ts: -------------------------------------------------------------------------------- 1 | export default class RpcError extends Error { 2 | json: any; 3 | 4 | constructor(json: any) { 5 | if (json.error && json.error.details && json.error.details.length && json.error.details[0].message) { 6 | super(json.error.details[0].message); 7 | } else if (json.processed && json.processed.except && json.processed.except.message) { 8 | super(json.processed.except.message); 9 | } else { 10 | super(json.message); 11 | } 12 | 13 | this.json = json; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Errors/SchemaError.ts: -------------------------------------------------------------------------------- 1 | export default class SchemaError extends Error { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/Errors/SerializationError.ts: -------------------------------------------------------------------------------- 1 | export default class SerializationError extends Error { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/Schema/MappingSchema.ts: -------------------------------------------------------------------------------- 1 | import SchemaError from '../Errors/SchemaError'; 2 | import { concat_byte_arrays, varint_decode, varint_encode } from '../Serialization/Binary'; 3 | import SerializationState from '../Serialization/State'; 4 | import { ISchema, MappingAttribute } from './index'; 5 | 6 | export default class MappingSchema implements ISchema { 7 | private readonly reserved = 4; 8 | 9 | constructor(private readonly attributes: MappingAttribute[]) { 10 | } 11 | 12 | deserialize(state: SerializationState, upwardsCompatible: boolean = false): any { 13 | const object: any = {}; 14 | 15 | while (state.position < state.data.length) { 16 | const identifier = varint_decode(state); 17 | 18 | if (identifier.equals(0)) { 19 | break; 20 | } 21 | 22 | const attribute = this.getAttribute(identifier.toJSNumber(), !upwardsCompatible); 23 | 24 | if (attribute) { 25 | object[attribute.name] = attribute.value.deserialize(state); 26 | } 27 | } 28 | 29 | return object; 30 | } 31 | 32 | serialize(object: any): Uint8Array { 33 | const data: Uint8Array[] = []; 34 | 35 | for (let i = 0; i < this.attributes.length; i++) { 36 | const attribute = this.attributes[i]; 37 | 38 | if (typeof object[attribute.name] === 'undefined') { 39 | continue; 40 | } 41 | 42 | data.push(varint_encode(i + this.reserved)); 43 | data.push(attribute.value.serialize(object[attribute.name])); 44 | } 45 | 46 | data.push(varint_encode(0)); 47 | 48 | return concat_byte_arrays(data); 49 | } 50 | 51 | private getAttribute(identifier: number, throwError: boolean = true): MappingAttribute | undefined { 52 | const attributeID = identifier - this.reserved; 53 | 54 | if (attributeID >= this.attributes.length) { 55 | if (throwError) { 56 | throw new SchemaError('attribute does not exists'); 57 | } 58 | 59 | return; 60 | } 61 | 62 | return this.attributes[Number(attributeID)]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Schema/ValueSchema.ts: -------------------------------------------------------------------------------- 1 | import SchemaError from '../Errors/SchemaError'; 2 | import SerializationState from '../Serialization/State'; 3 | import { ITypeParser } from '../Serialization/TypeParser'; 4 | import { ParserTypes } from '..'; 5 | import { ISchema } from './index'; 6 | 7 | export default class ValueSchema implements ISchema { 8 | readonly parser: ITypeParser; 9 | 10 | constructor(type: string) { 11 | if (typeof ParserTypes[type] === 'undefined') { 12 | throw new SchemaError(`attribute type '${type}' not defined`); 13 | } 14 | 15 | this.parser = ParserTypes[type]; 16 | } 17 | 18 | deserialize(state: SerializationState): any { 19 | return this.parser.deserialize(state); 20 | } 21 | 22 | serialize(value: any): Uint8Array { 23 | return this.parser.serialize(value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Schema/VectorSchema.ts: -------------------------------------------------------------------------------- 1 | import { concat_byte_arrays, varint_decode, varint_encode } from '../Serialization/Binary'; 2 | import SerializationState from '../Serialization/State'; 3 | import { ISchema } from './index'; 4 | 5 | export default class VectorSchema implements ISchema { 6 | constructor(private readonly element: ISchema) { 7 | } 8 | 9 | deserialize(state: SerializationState): any { 10 | const length = varint_decode(state).toJSNumber(); 11 | const array: any[] = []; 12 | 13 | for (let i = 0; i < length; i++) { 14 | array.push(this.element.deserialize(state)); 15 | } 16 | 17 | return array; 18 | } 19 | 20 | serialize(array: any[]): Uint8Array { 21 | const data: Uint8Array[] = [varint_encode(array.length)]; 22 | 23 | for (const element of array) { 24 | data.push(this.element.serialize(element)); 25 | } 26 | 27 | return concat_byte_arrays(data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Schema/index.ts: -------------------------------------------------------------------------------- 1 | import SchemaError from '../Errors/SchemaError'; 2 | import SerializationState from '../Serialization/State'; 3 | import MappingSchema from './MappingSchema'; 4 | import ValueSchema from './ValueSchema'; 5 | import VectorSchema from './VectorSchema'; 6 | 7 | export interface ISchema { 8 | serialize(data: any): Uint8Array; 9 | 10 | deserialize(state: SerializationState): Uint8Array; 11 | } 12 | 13 | export type SchemaObject = { name: string, type: string, parent?: number }; 14 | export type MappingAttribute = { name: string, value: ISchema }; 15 | 16 | type ObjectLookup = { [id: number]: SchemaObject[] }; 17 | 18 | function buildObjectSchema(objectID: number, lookup: ObjectLookup): ISchema { 19 | const attributes: MappingAttribute[] = []; 20 | let fields = lookup[objectID]; 21 | 22 | if (typeof fields === 'undefined') { 23 | fields = []; 24 | } 25 | 26 | delete lookup[objectID]; 27 | 28 | for (const field of fields) { 29 | attributes.push({name: field.name, value: buildValueSchema(field.type, lookup)}); 30 | } 31 | 32 | return new MappingSchema(attributes); 33 | } 34 | 35 | function buildValueSchema(type: string, lookup: ObjectLookup): ISchema { 36 | if (type.endsWith('[]')) { 37 | return new VectorSchema(buildValueSchema(type.substring(0, type.length - 2), lookup)); 38 | } 39 | 40 | // not supported by the contract currently 41 | if (type.startsWith('object{') && type.endsWith('}')) { 42 | const objectID = parseInt(type.substring(7, type.length - 1), 10); 43 | 44 | if (isNaN(objectID)) { 45 | throw new SchemaError(`invalid type '${type}'`); 46 | } 47 | 48 | return buildObjectSchema(objectID, lookup); 49 | } 50 | 51 | return new ValueSchema(type); 52 | } 53 | 54 | export function ObjectSchema(schema: SchemaObject[]): ISchema { 55 | const objectLookup: ObjectLookup = {}; 56 | 57 | for (const schemaObject of schema) { 58 | const objectID = typeof schemaObject.parent === 'undefined' ? 0 : schemaObject.parent; 59 | 60 | if (typeof objectLookup[objectID] === 'undefined') { 61 | objectLookup[objectID] = []; 62 | } 63 | 64 | objectLookup[objectID].push(schemaObject); 65 | } 66 | 67 | return buildObjectSchema(0, objectLookup); 68 | } 69 | -------------------------------------------------------------------------------- /src/Serialization/Binary.ts: -------------------------------------------------------------------------------- 1 | import bigInt, { BigInteger } from 'big-integer'; 2 | 3 | import DeserializationError from '../Errors/DeserializationError'; 4 | import SerializationError from '../Errors/SerializationError'; 5 | import BaseCoder from './Coders/Base'; 6 | import SerializationState from './State'; 7 | 8 | export function varint_encode(input: any): Uint8Array { 9 | const bytes: number[] = []; 10 | let n = bigInt(input); 11 | 12 | if (n.lesser(0)) { 13 | throw new SerializationError('cant pack negative integer'); 14 | } 15 | 16 | while (true) { 17 | const byte = n.and(0x7F); 18 | 19 | n = n.shiftRight(7); 20 | 21 | if (n.equals(0)) { 22 | bytes.push(byte.toJSNumber()); 23 | 24 | break; 25 | } 26 | 27 | bytes.push(byte.toJSNumber() + 128); 28 | } 29 | 30 | return new Uint8Array(bytes); 31 | } 32 | 33 | export function varint_decode(state: SerializationState): BigInteger { 34 | let result: BigInteger = bigInt(0); 35 | 36 | for (let i = 0; true; i++) { 37 | if (state.position >= state.data.length) { 38 | throw new DeserializationError('failed to unpack integer'); 39 | } 40 | 41 | const byte = bigInt(state.data[state.position]); 42 | state.position += 1; 43 | 44 | if (byte.lesser(128)) { 45 | result = result.plus(byte.shiftLeft(7 * i)); 46 | 47 | break; 48 | } 49 | 50 | result = result.plus(byte.and(0x7F).shiftLeft(7 * i)); 51 | } 52 | 53 | return result; 54 | } 55 | 56 | export function integer_sign(input: any, size: number): BigInteger { 57 | const n = bigInt(input); 58 | 59 | if (n.greaterOrEquals(bigInt(2).pow(8 * size - 1))) { 60 | throw new Error('cannot sign integer: too big'); 61 | } 62 | 63 | if (n.greaterOrEquals(0)) { 64 | return n; 65 | } 66 | 67 | return n.negate().xor(bigInt(2).pow(8 * size).minus(1)).plus(1); 68 | } 69 | 70 | export function integer_unsign(input: any, size: number): BigInteger { 71 | const n = bigInt(input); 72 | 73 | if (n.greater(bigInt(2).pow(8 * size))) { 74 | throw new Error('cannot unsign integer: too big'); 75 | } 76 | 77 | if (n.greater(bigInt(2).pow(8 * size - 1))) { 78 | return n.minus(1).xor(bigInt(2).pow(8 * size).minus(1)).negate(); 79 | } 80 | 81 | return n; 82 | } 83 | 84 | export function zigzag_encode(input: any): BigInteger { 85 | const n = bigInt(input); 86 | 87 | if (n.lesser(0)) { 88 | return n.plus(1).multiply(-2).plus(1); 89 | } 90 | 91 | return n.multiply(2); 92 | } 93 | 94 | export function zigzag_decode(input: any): BigInteger { 95 | const n = bigInt(input); 96 | 97 | if (n.mod(2).equals(0)) { 98 | return n.divmod(2).quotient; 99 | } 100 | 101 | return n.divmod(2).quotient.multiply(-1).minus(1); 102 | } 103 | 104 | const bs58 = new BaseCoder('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); 105 | 106 | export function base58_decode(data: string): Uint8Array { 107 | return bs58.decode(data); 108 | } 109 | 110 | export function base58_encode(data: Uint8Array): string { 111 | return bs58.encode(data); 112 | } 113 | 114 | export function hex_decode(hex: string): Uint8Array { 115 | const bytes = hex.match(/.{1,2}/g); 116 | 117 | if (!bytes) { 118 | return new Uint8Array(0); 119 | } 120 | 121 | return new Uint8Array(bytes.map((byte) => parseInt(byte, 16))); 122 | } 123 | 124 | export function hex_encode(bytes: Uint8Array): string { 125 | return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); 126 | } 127 | 128 | export function concat_byte_arrays(arr: Uint8Array[]): Uint8Array { 129 | // concat all bytearrays into one array 130 | const data = new Uint8Array(arr.reduce((acc, val) => acc + val.length, 0)); 131 | 132 | let offset = 0; 133 | for (const bytes of arr) { 134 | data.set(bytes, offset); 135 | offset += bytes.length; 136 | } 137 | 138 | return data; 139 | } 140 | 141 | export function int_to_byte_vector(n: any): Uint8Array { 142 | const bytes: number[] = []; 143 | let num = bigInt(n); 144 | 145 | while (num.notEquals(0)) { 146 | bytes.push(num.and(0xFF).toJSNumber()); 147 | num = num.shiftRight(8); 148 | } 149 | 150 | return new Uint8Array(bytes); 151 | } 152 | 153 | export function byte_vector_to_int(bytes: Uint8Array): number { 154 | let num = bigInt(0); 155 | 156 | for (let i = 0; i < bytes.length; i++) { 157 | num = num.plus(bigInt(bytes[i]).shiftLeft(8 * i)); 158 | } 159 | 160 | return num.toJSNumber(); 161 | } 162 | -------------------------------------------------------------------------------- /src/Serialization/Coders/Base.ts: -------------------------------------------------------------------------------- 1 | /* based on npm base-x module (removed buffer, added class structure) */ 2 | export default class BaseCoder { 3 | private readonly BASE: number; 4 | private readonly BASE_MAP: Uint8Array; 5 | private readonly LEADER: string; 6 | private readonly FACTOR: number; 7 | private readonly iFACTOR: number; 8 | 9 | constructor(private readonly ALPHABET: string) { 10 | if (ALPHABET.length >= 255) { 11 | throw new TypeError('Alphabet too long'); 12 | } 13 | this.BASE_MAP = new Uint8Array(256); 14 | 15 | for (let j = 0; j < this.BASE_MAP.length; j++) { 16 | this.BASE_MAP[j] = 255; 17 | } 18 | 19 | for (let i = 0; i < ALPHABET.length; i++) { 20 | const x = ALPHABET.charAt(i); 21 | const xc = x.charCodeAt(0); 22 | if (this.BASE_MAP[xc] !== 255) { 23 | throw new TypeError(x + ' is ambiguous'); 24 | } 25 | this.BASE_MAP[xc] = i; 26 | } 27 | 28 | this.BASE = ALPHABET.length; 29 | this.LEADER = ALPHABET.charAt(0); 30 | this.FACTOR = Math.log(this.BASE) / Math.log(256); // log(BASE) / log(256), rounded up 31 | this.iFACTOR = Math.log(256) / Math.log(this.BASE); // log(256) / log(BASE), rounded up 32 | } 33 | 34 | encode(source: Uint8Array): string { 35 | if (source.length === 0) { 36 | return ''; 37 | } 38 | // Skip & count leading zeroes. 39 | let zeroes = 0; 40 | let length = 0; 41 | let pbegin = 0; 42 | const pend = source.length; 43 | 44 | while (pbegin !== pend && source[pbegin] === 0) { 45 | pbegin++; 46 | zeroes++; 47 | } 48 | 49 | // Allocate enough space in big-endian base58 representation. 50 | const size = ((pend - pbegin) * this.iFACTOR + 1) >>> 0; 51 | const b58 = new Uint8Array(size); 52 | // Process the bytes. 53 | while (pbegin !== pend) { 54 | let carry = source[pbegin]; 55 | // Apply "b58 = b58 * 256 + ch". 56 | let i = 0; 57 | for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { 58 | carry += (256 * b58[it1]) >>> 0; 59 | b58[it1] = (carry % this.BASE) >>> 0; 60 | carry = (carry / this.BASE) >>> 0; 61 | } 62 | if (carry !== 0) { 63 | throw new Error('Non-zero carry'); 64 | } 65 | length = i; 66 | pbegin++; 67 | } 68 | 69 | // Skip leading zeroes in base58 result. 70 | let it2 = size - length; 71 | while (it2 !== size && b58[it2] === 0) { 72 | it2++; 73 | } 74 | 75 | // Translate the result into a string. 76 | let str = this.LEADER.repeat(zeroes); 77 | for (; it2 < size; ++it2) { 78 | str += this.ALPHABET.charAt(b58[it2]); 79 | } 80 | 81 | return str; 82 | } 83 | 84 | decode(source: string): Uint8Array { 85 | const buffer = this.decodeUnsafe(source); 86 | if (buffer) { 87 | return buffer; 88 | } 89 | 90 | throw new Error('Non-base' + this.BASE + ' character'); 91 | } 92 | 93 | private decodeUnsafe(source: string): Uint8Array { 94 | if (source.length === 0) { 95 | return new Uint8Array(0); 96 | } 97 | let psz = 0; 98 | // Skip leading spaces. 99 | if (source[psz] === ' ') { 100 | return new Uint8Array(0); 101 | } 102 | 103 | // Skip and count leading '1's. 104 | let zeroes = 0; 105 | let length = 0; 106 | while (source[psz] === this.LEADER) { 107 | zeroes++; 108 | psz++; 109 | } 110 | 111 | // Allocate enough space in big-endian base256 representation. 112 | const size = (((source.length - psz) * this.FACTOR) + 1) >>> 0; // log(58) / log(256), rounded up. 113 | const b256 = new Uint8Array(size); 114 | 115 | // Process the characters. 116 | while (source[psz]) { 117 | // Decode character 118 | let carry = this.BASE_MAP[source.charCodeAt(psz)]; 119 | // Invalid character 120 | if (carry === 255) { 121 | return new Uint8Array(0); 122 | } 123 | let i = 0; 124 | for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { 125 | carry += (this.BASE * b256[it3]) >>> 0; 126 | b256[it3] = (carry % 256) >>> 0; 127 | carry = (carry / 256) >>> 0; 128 | } 129 | if (carry !== 0) { 130 | throw new Error('Non-zero carry'); 131 | } 132 | length = i; 133 | psz++; 134 | } 135 | 136 | // Skip trailing spaces. 137 | if (source[psz] === ' ') { 138 | return new Uint8Array(0); 139 | } 140 | // Skip leading zeroes in b256. 141 | let it4 = size - length; 142 | while (it4 !== size && b256[it4] === 0) { 143 | it4++; 144 | } 145 | 146 | const vch = new Uint8Array(zeroes + (size - it4)); 147 | vch.fill(0x00, 0, zeroes); 148 | 149 | let j = zeroes; 150 | while (it4 !== size) { 151 | vch[j++] = b256[it4++]; 152 | } 153 | 154 | return vch; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Serialization/State.ts: -------------------------------------------------------------------------------- 1 | export default class SerializationState { 2 | constructor(readonly data: Uint8Array, public position: number = 0) { 3 | } 4 | } 5 | 6 | export function prepare(data: Uint8Array): SerializationState { 7 | return new SerializationState(data, 0); 8 | } 9 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/BooleanParser.ts: -------------------------------------------------------------------------------- 1 | import SerializationState from '../State'; 2 | import FixedParser from './FixedParser'; 3 | 4 | export default class BooleanParser extends FixedParser { 5 | constructor() { 6 | super(1); 7 | } 8 | 9 | deserialize(state: SerializationState): number { 10 | const data: Uint8Array = super.deserialize(state); 11 | 12 | return data[0] === 1 ? 1 : 0; 13 | } 14 | 15 | serialize(data: boolean): Uint8Array { 16 | return super.serialize(new Uint8Array([data ? 1 : 0])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/ByteParser.ts: -------------------------------------------------------------------------------- 1 | import SerializationState from '../State'; 2 | import VariableParser from './VariableParser'; 3 | 4 | export class ByteParser extends VariableParser { 5 | deserialize(state: SerializationState): Uint8Array { 6 | return super.deserialize(state); 7 | } 8 | 9 | serialize(data: Uint8Array): Uint8Array { 10 | return super.serialize(data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/FixedIntegerParser.ts: -------------------------------------------------------------------------------- 1 | import bigInt, { BigInteger } from 'big-integer'; 2 | 3 | import SerializationState from '../State'; 4 | import FixedParser from './FixedParser'; 5 | 6 | export default class FixedIntegerParser extends FixedParser { 7 | deserialize(state: SerializationState): number | string { 8 | const data: Uint8Array = super.deserialize(state).reverse(); 9 | let n = bigInt(0); 10 | 11 | for (const byte of data) { 12 | n = n.shiftLeft(8); 13 | n = n.plus(byte); 14 | } 15 | 16 | if (this.size <= 6) { 17 | return n.toJSNumber(); 18 | } 19 | 20 | return n.toString(); 21 | } 22 | 23 | serialize(data: any): Uint8Array { 24 | let n: BigInteger = bigInt(data); 25 | const buffer: number[] = []; 26 | 27 | for (let i = 0; i < this.size; i++) { 28 | buffer.push(n.and(0xFF).toJSNumber()); 29 | n = n.shiftRight(8); 30 | } 31 | 32 | return super.serialize(new Uint8Array(buffer)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/FixedParser.ts: -------------------------------------------------------------------------------- 1 | import DeserializationError from '../../Errors/DeserializationError'; 2 | import SerializationError from '../../Errors/SerializationError'; 3 | import SerializationState from '../State'; 4 | import { ITypeParser } from './index'; 5 | 6 | export default class FixedParser implements ITypeParser { 7 | constructor(readonly size: number) { 8 | } 9 | 10 | deserialize(state: SerializationState): any { 11 | state.position += this.size; 12 | 13 | const data = state.data.slice(state.position - this.size, state.position); 14 | 15 | if (data.length !== this.size) { 16 | throw new DeserializationError('FixedParser: read past end'); 17 | } 18 | 19 | return data; 20 | } 21 | 22 | serialize(data: any): Uint8Array { 23 | if (data.length !== this.size) { 24 | throw new SerializationError(`input data does not conform fixed size`); 25 | } 26 | 27 | return data; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/FloatingParser.ts: -------------------------------------------------------------------------------- 1 | import SerializationState from '../State'; 2 | import FixedParser from './FixedParser'; 3 | 4 | // tslint:disable-next-line:no-var-requires 5 | const fp: any = require('../../../lib/float'); 6 | 7 | export default class FloatingParser extends FixedParser { 8 | constructor(private readonly isDouble: boolean) { 9 | super(isDouble ? 8 : 4); 10 | } 11 | 12 | deserialize(state: SerializationState): number { 13 | if (this.isDouble) { 14 | return fp.readDoubleLE(super.deserialize(state)); 15 | } 16 | 17 | return fp.readFloatLE(super.deserialize(state)); 18 | } 19 | 20 | serialize(data: number): Uint8Array { 21 | // tslint:disable-next-line:prefer-const 22 | let bytes: number[] = []; 23 | 24 | if (this.isDouble) { 25 | fp.writeDoubleLE(bytes, data); 26 | 27 | return super.serialize(new Uint8Array(bytes)); 28 | } 29 | 30 | fp.writeFloatLE(bytes, data); 31 | 32 | return super.serialize(new Uint8Array(bytes)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/IPFSParser.ts: -------------------------------------------------------------------------------- 1 | import { base58_decode, base58_encode } from '../Binary'; 2 | import SerializationState from '../State'; 3 | import VariableParser from './VariableParser'; 4 | 5 | export default class IPFSParser extends VariableParser { 6 | deserialize(state: SerializationState): any { 7 | return base58_encode(super.deserialize(state)); 8 | } 9 | 10 | serialize(data: string): Uint8Array { 11 | return super.serialize(base58_decode(data)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/StringParser.ts: -------------------------------------------------------------------------------- 1 | import SerializationState from '../State'; 2 | import VariableParser from './VariableParser'; 3 | 4 | export default class StringParser extends VariableParser { 5 | deserialize(state: SerializationState): any { 6 | return new TextDecoder().decode(super.deserialize(state)); 7 | } 8 | 9 | serialize(data: string): Uint8Array { 10 | return super.serialize(new TextEncoder().encode(data)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/VariableIntegerParser.ts: -------------------------------------------------------------------------------- 1 | import bigInt from 'big-integer'; 2 | 3 | import DeserializationError from '../../Errors/DeserializationError'; 4 | import SerializationError from '../../Errors/SerializationError'; 5 | import { varint_decode, varint_encode, zigzag_decode, zigzag_encode } from '../Binary'; 6 | import SerializationState from '../State'; 7 | import { ITypeParser } from './index'; 8 | 9 | export default class VariableIntegerParser implements ITypeParser { 10 | constructor(readonly size: number, private readonly unsigned: boolean) { 11 | } 12 | 13 | deserialize(state: SerializationState): number | string { 14 | let n = varint_decode(state); 15 | 16 | if (!this.unsigned) { 17 | n = zigzag_decode(n); 18 | } 19 | 20 | if (n.greaterOrEquals(bigInt(2).pow(this.size * 8 - (this.unsigned ? 0 : 1)))) { 21 | throw new DeserializationError('number \'' + n.toString() + '\' too large for given type'); 22 | } 23 | 24 | if (this.size <= 6) { 25 | return n.toJSNumber(); 26 | } 27 | 28 | return n.toString(); 29 | } 30 | 31 | serialize(data: any): Uint8Array { 32 | let n = bigInt(data); 33 | 34 | if (n.greaterOrEquals(bigInt(2).pow(this.size * 8 - (this.unsigned ? 0 : 1)))) { 35 | throw new SerializationError('number \'' + n.toString() + '\' too large for given type'); 36 | } 37 | 38 | if (!this.unsigned) { 39 | n = zigzag_encode(n); 40 | } 41 | 42 | return varint_encode(n); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/VariableParser.ts: -------------------------------------------------------------------------------- 1 | import DeserializationError from '../../Errors/DeserializationError'; 2 | import { concat_byte_arrays, varint_decode, varint_encode } from '../Binary'; 3 | import SerializationState from '../State'; 4 | import { ITypeParser } from './index'; 5 | 6 | export default class VariableParser implements ITypeParser { 7 | deserialize(state: SerializationState): any { 8 | const length = varint_decode(state).toJSNumber(); 9 | state.position += length; 10 | 11 | const data = state.data.slice(state.position - length, state.position); 12 | 13 | if (data.length !== length) { 14 | throw new DeserializationError(`VariableParser: read past end`); 15 | } 16 | 17 | return data; 18 | } 19 | 20 | serialize(data: any): Uint8Array { 21 | return concat_byte_arrays([varint_encode(data.length), data]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Serialization/TypeParser/index.ts: -------------------------------------------------------------------------------- 1 | import SerializationState from '../State'; 2 | 3 | export interface ITypeParser { 4 | serialize(object: any): Uint8Array; 5 | 6 | deserialize(data: SerializationState): any; 7 | } 8 | -------------------------------------------------------------------------------- /src/Serialization/Types.ts: -------------------------------------------------------------------------------- 1 | import { ITypeParser } from './TypeParser'; 2 | import BooleanParser from './TypeParser/BooleanParser'; 3 | import { ByteParser } from './TypeParser/ByteParser'; 4 | import FixedIntegerParser from './TypeParser/FixedIntegerParser'; 5 | import FloatingParser from './TypeParser/FloatingParser'; 6 | import IPFSParser from './TypeParser/IPFSParser'; 7 | import StringParser from './TypeParser/StringParser'; 8 | import VariableIntegerParser from './TypeParser/VariableIntegerParser'; 9 | 10 | // tslint:disable:object-literal-sort-keys 11 | export const ParserTypes: { [id: string]: ITypeParser } = { 12 | int8: new VariableIntegerParser(1, false), 13 | int16: new VariableIntegerParser(2, false), 14 | int32: new VariableIntegerParser(4, false), 15 | int64: new VariableIntegerParser(8, false), 16 | 17 | uint8: new VariableIntegerParser(1, true), 18 | uint16: new VariableIntegerParser(2, true), 19 | uint32: new VariableIntegerParser(4, true), 20 | uint64: new VariableIntegerParser(8, true), 21 | 22 | fixed8: new FixedIntegerParser(1), 23 | fixed16: new FixedIntegerParser(2), 24 | fixed32: new FixedIntegerParser(4), 25 | fixed64: new FixedIntegerParser(8), 26 | 27 | bool: new BooleanParser(), 28 | 29 | bytes: new ByteParser(), 30 | string: new StringParser(), 31 | image: new StringParser(), 32 | 33 | ipfs: new IPFSParser(), 34 | float: new FloatingParser(false), 35 | double: new FloatingParser(true) 36 | }; 37 | -------------------------------------------------------------------------------- /src/Serialization/index.ts: -------------------------------------------------------------------------------- 1 | import { ISchema } from '../Schema'; 2 | import MappingSchema from '../Schema/MappingSchema'; 3 | import { concat_byte_arrays, varint_encode } from './Binary'; 4 | import SerializationState from './State'; 5 | 6 | export function serialize(object: any, schema: ISchema): Uint8Array { 7 | const data = schema.serialize(object); 8 | 9 | // remove terminating 0 byte because it is unnecessary 10 | if (schema instanceof MappingSchema) { 11 | return data.slice(0, data.length - 1); 12 | } 13 | 14 | return data; 15 | } 16 | 17 | export function deserialize(data: Uint8Array, schema: ISchema): any { 18 | if (schema instanceof MappingSchema) { 19 | data = concat_byte_arrays([data, varint_encode(0)]); 20 | } 21 | 22 | const state = new SerializationState(data, 0); 23 | 24 | return schema.deserialize(state); 25 | } 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import ExplorerActionGenerator from './Actions/Explorer'; 2 | import { ActionGenerator } from './Actions/Generator'; 3 | import RpcActionGenerator from './Actions/Rpc'; 4 | import ExplorerApi from './API/Explorer'; 5 | import RpcApi from './API/Rpc'; 6 | import { ObjectSchema } from './Schema'; 7 | import { deserialize, serialize } from './Serialization'; 8 | import { ParserTypes } from './Serialization/Types'; 9 | 10 | export { 11 | RpcApi, ExplorerApi, ObjectSchema, deserialize, serialize, ParserTypes, 12 | RpcActionGenerator, ExplorerActionGenerator, ActionGenerator 13 | }; 14 | -------------------------------------------------------------------------------- /test/binary.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { 4 | base58_decode, 5 | base58_encode, 6 | byte_vector_to_int, 7 | hex_decode, 8 | hex_encode, 9 | int_to_byte_vector, 10 | integer_sign, integer_unsign, 11 | varint_decode, 12 | varint_encode, 13 | zigzag_decode, 14 | zigzag_encode 15 | } from '../src/Serialization/Binary'; 16 | import { prepare } from '../src/Serialization/State'; 17 | 18 | describe('Binary', () => { 19 | it('sign integer', () => { 20 | expect(Number(integer_sign(BigInt(-534), 2))).to.equal(65002); 21 | expect(Number(integer_sign(BigInt(534), 2))).to.equal(534); 22 | expect(Number(integer_sign(BigInt(0), 2))).to.equal(0); 23 | }); 24 | 25 | it('unsign integer', () => { 26 | expect(Number(integer_unsign(BigInt(65002), 2))).to.equal(-534); 27 | expect(Number(integer_unsign(BigInt(534), 2))).to.equal(534); 28 | expect(Number(integer_unsign(BigInt(0), 2))).to.equal(0); 29 | }); 30 | 31 | it('pack integer below 127', () => { 32 | expect(varint_encode(BigInt(5))).to.deep.equal(new Uint8Array([0b00000101])); 33 | }); 34 | 35 | it('unpack integer below 127', () => { 36 | const data = new Uint8Array([0b00000101]); 37 | 38 | expect(varint_decode(prepare(data)).toJSNumber()).to.equal(5); 39 | }); 40 | 41 | it('pack 2 bytes', () => { 42 | expect(varint_encode(BigInt(230))).to.deep.equal(new Uint8Array([0b11100110, 0b00000001])); 43 | }); 44 | 45 | it('unpack 2 bytes', () => { 46 | const data = new Uint8Array([0b11100110, 0b00000001]); 47 | 48 | expect(varint_decode(prepare(data)).toJSNumber()).to.equal(230); 49 | }); 50 | 51 | it('pack max integer', () => { 52 | expect(varint_encode(BigInt(4294867286))).to.deep.equal(new Uint8Array([0b11010110, 0b11110010, 0b11111001, 0b11111111, 0b00001111])); 53 | }); 54 | 55 | it('unpack max integer', () => { 56 | const data = new Uint8Array([0b11010110, 0b11110010, 0b11111001, 0b11111111, 0b00001111]); 57 | 58 | expect(varint_decode(prepare(data)).toJSNumber()).to.equal(4294867286); 59 | }); 60 | 61 | it('base58 encode', () => { 62 | expect(base58_encode(hex_decode('122037b8d00f5a5b37181c8fa8a05f7446b2ea06a0bbaaba3f1e95e4e97726a5e67c'))).to.equal('QmS6AaitSdut3Te4fagW6jgfyKL73A1NBSSt3K38vQP9xf'); 63 | }); 64 | 65 | it('base58 decode', () => { 66 | expect(hex_encode(base58_decode('QmS6AaitSdut3Te4fagW6jgfyKL73A1NBSSt3K38vQP9xf'))).to.equal('122037b8d00f5a5b37181c8fa8a05f7446b2ea06a0bbaaba3f1e95e4e97726a5e67c'); 67 | }); 68 | 69 | it('vector <-> int', () => { 70 | expect(byte_vector_to_int(int_to_byte_vector(1000))).to.equal(1000); 71 | }); 72 | 73 | it('zigzag encode', () => { 74 | expect(zigzag_encode(6).toJSNumber()).to.equal(12); 75 | expect(zigzag_encode(1).toJSNumber()).to.equal(2); 76 | expect(zigzag_encode(-1).toJSNumber()).to.equal(1); 77 | expect(zigzag_encode(0).toJSNumber()).to.equal(0); 78 | }); 79 | 80 | it('zigzag decode', () => { 81 | expect(zigzag_decode(12).toJSNumber()).to.equal(6); 82 | expect(zigzag_decode(3).toJSNumber()).to.equal(-2); 83 | expect(zigzag_decode(2).toJSNumber()).to.equal(1); 84 | expect(zigzag_decode(1).toJSNumber()).to.equal(-1); 85 | expect(zigzag_decode(0).toJSNumber()).to.equal(0); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/explorerapi.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import ExplorerApi from '../src/API/Explorer'; 3 | 4 | // tslint:disable-next-line:no-var-requires 5 | const fetch = require('node-fetch'); 6 | 7 | describe('Explorer API', () => { 8 | const api = new ExplorerApi('https://test.wax.api.atomicassets.io', 'atomicassets', {fetch}); 9 | 10 | const exampleAsset = { 11 | owner: 'testuser2222', 12 | id: '1099511627784' 13 | }; 14 | 15 | it('fetch asset ' + exampleAsset.id, async () => { 16 | const assets = await api.getAssets({ 17 | owner: exampleAsset.owner 18 | }); 19 | 20 | expect(assets).to.deep.equal(assets); 21 | }).timeout(10000); 22 | }); 23 | -------------------------------------------------------------------------------- /test/rpcapi.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import RpcApi from '../src/API/Rpc'; 4 | 5 | // tslint:disable-next-line:no-var-requires 6 | const fetch = require('node-fetch'); 7 | 8 | describe('RPC API', () => { 9 | const api = new RpcApi('https://wax.pink.gg', 'atomicassets', { 10 | fetch, rateLimit: 4 11 | }); 12 | 13 | const exampleAsset = { 14 | owner: 'pink.gg', 15 | id: '1099511628276' 16 | }; 17 | 18 | it('fetch asset ' + exampleAsset.id, async () => { 19 | const asset = await api.getAsset(exampleAsset.owner, exampleAsset.id); 20 | 21 | const result = await asset.toObject(); 22 | expect(result).to.deep.equal(result); 23 | }).timeout(10000); 24 | 25 | it('test caching', async () => { 26 | const asset = await api.getAsset(exampleAsset.owner, exampleAsset.id); 27 | 28 | const result = await asset.toObject(); 29 | 30 | expect(result).to.deep.equal(result); 31 | }).timeout(10000); 32 | 33 | it('fetch offers ', async () => { 34 | const offers = await api.getAccountOffers(exampleAsset.owner); 35 | 36 | const result = await Promise.all(offers.map(async (offer) => await offer.toObject())); 37 | 38 | expect(result).to.deep.equal(result); 39 | }).timeout(20000); 40 | 41 | it('fetch assets ', async () => { 42 | const assets = await api.getAccountAssets(exampleAsset.owner); 43 | 44 | const result = await Promise.all(assets.map(async (asset) => await asset.toObject())); 45 | 46 | expect(result).to.deep.equal(result); 47 | }).timeout(120000); 48 | 49 | it('fetch collection inventory ', async () => { 50 | const assets = await api.getCollectionInventory('gpk.topps', 'lc4l.wam'); 51 | 52 | const result = await Promise.all(assets.map(asset => asset.toObject())); 53 | expect(result).to.deep.equal([]); 54 | }).timeout(120000); 55 | }); 56 | -------------------------------------------------------------------------------- /test/serialization_advanced.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { ObjectSchema } from '../src/Schema'; 4 | import { deserialize, serialize } from '../src/Serialization'; 5 | 6 | describe('Advanced Serialization', () => { 7 | const schema = ObjectSchema([ 8 | { 9 | name: 'name', 10 | type: 'string', 11 | parent: 0 12 | }, 13 | { 14 | name: 'id', 15 | type: 'uint64', 16 | parent: 0 17 | }, 18 | { 19 | name: 'ipfs', 20 | type: 'ipfs', 21 | parent: 0 22 | }, 23 | { 24 | name: 'chance', 25 | type: 'float', 26 | parent: 0 27 | }, 28 | { 29 | name: 'attributes', 30 | type: 'object{1}[]', 31 | parent: 0 32 | }, 33 | { 34 | name: 'strength', 35 | type: 'string', 36 | parent: 1 37 | }, 38 | { 39 | name: 'height', 40 | type: 'int16', 41 | parent: 1 42 | }, 43 | { 44 | name: 'matrix', 45 | type: 'fixed32[][]', 46 | parent: 0 47 | }, 48 | { 49 | name: 'isCool', 50 | type: 'bool', 51 | parent: 0 52 | }, 53 | { 54 | name: 'deeper', 55 | type: 'object{78910}', 56 | parent: 1 57 | }, 58 | { 59 | name: 'precise', 60 | type: 'double', 61 | parent: 78910 62 | } 63 | ]); 64 | 65 | // tslint:disable-next-line:max-line-length 66 | const serializedObject = new Uint8Array([4, 4, 72, 97, 110, 115, 5, 173, 226, 229, 13, 6, 34, 18, 32, 104, 242, 255, 105, 42, 38, 111, 205, 242, 95, 137, 204, 243, 62, 87, 157, 39, 114, 105, 180, 68, 42, 33, 48, 192, 51, 224, 46, 18, 103, 14, 68, 7, 0, 0, 64, 63, 8, 2, 4, 6, 98, 114, 117, 116, 97, 108, 5, 136, 142, 1, 6, 4, 247, 53, 154, 117, 233, 8, 87, 64, 0, 0, 4, 10, 97, 117, 99, 104, 32, 107, 114, 97, 115, 115, 5, 247, 47, 0, 9, 2, 3, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 168, 65, 1, 0, 9, 0, 0, 0, 56, 115, 156, 73, 10, 1]); 67 | const rawObject = { 68 | name: 'Hans', 69 | id: '28930349', 70 | attributes: [ 71 | { 72 | height: 9092, 73 | strength: 'brutal', 74 | deeper: { 75 | precise: 92.13924923 76 | } 77 | }, 78 | { 79 | height: -3068, 80 | strength: 'auch krass' 81 | } 82 | ], 83 | chance: 0.75, 84 | ipfs: 'QmVQL22VUsxV26aKrJL8ZbQgU2o6veUatE9jZzPCdHV7pf', 85 | matrix: [ 86 | [ 87 | 1, 88 | 2, 89 | 3 90 | ], 91 | [ 92 | 82344, 93 | 9, 94 | 1234989880 95 | ] 96 | ], 97 | isCool: 1 98 | }; 99 | 100 | it('serialize object', () => { 101 | expect(serialize(rawObject, schema)).to.deep.equal(serializedObject); 102 | }); 103 | 104 | it('deserialize object', () => { 105 | expect(deserialize(serializedObject, schema)).to.deep.equal(rawObject); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/serialization_basic.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { ObjectSchema } from '../src/Schema'; 4 | import { deserialize, serialize } from '../src/Serialization'; 5 | import { base58_decode, concat_byte_arrays, hex_decode, varint_encode, zigzag_encode } from '../src/Serialization/Binary'; 6 | 7 | describe('Basic Serialization', () => { 8 | const schema = ObjectSchema([ 9 | /* 04 */ {name: 'name', type: 'string'}, 10 | /* 05 */ {name: 'img', type: 'ipfs'}, 11 | /* 06 */ {name: 'rarity1', type: 'int16'}, 12 | /* 07 */ {name: 'rarity2', type: 'uint16'}, 13 | /* 08 */ {name: 'rarity3', type: 'int64'}, 14 | /* 09 */ {name: 'rarity4', type: 'fixed16'}, 15 | /* 10 */ {name: 'depth1', type: 'int32'}, 16 | /* 11 */ {name: 'depth2', type: 'uint32'}, 17 | /* 12 */ {name: 'depth3', type: 'int64'}, 18 | /* 13 */ {name: 'depth4', type: 'fixed32'}, 19 | /* 14 */ {name: 'wear', type: 'float'}, 20 | /* 15 */ {name: 'tradeable', type: 'bool'}, 21 | /* 16 */ {name: 'share', type: 'double'} 22 | ]); 23 | 24 | const encoder = new TextEncoder(); 25 | 26 | const rawObject = { 27 | name: 'M4A4 Skin', 28 | img: 'QmS6AaitSdut3Te4fagW6jgfyKL73A1NBSSt3K38vQP9xf', 29 | rarity1: 534, 30 | rarity2: 534, 31 | rarity3: '534', 32 | rarity4: 534, 33 | depth1: -1000000, 34 | depth2: 1000000, 35 | depth3: '-1000000', 36 | depth4: 4293967296, 37 | wear: 0.75, 38 | tradeable: 1, 39 | share: 1024.25 40 | }; 41 | 42 | const serializedName = concat_byte_arrays([ 43 | varint_encode(4), 44 | varint_encode(rawObject.name.length), 45 | new Uint8Array(encoder.encode(rawObject.name)) 46 | ]); 47 | 48 | const serializedImage = concat_byte_arrays([ 49 | varint_encode(5), 50 | varint_encode(base58_decode(rawObject.img).length), 51 | base58_decode(rawObject.img) 52 | ]); 53 | 54 | const serializedRarity = concat_byte_arrays([ 55 | varint_encode(6), 56 | varint_encode(zigzag_encode(534)), 57 | varint_encode(7), 58 | varint_encode(534), 59 | varint_encode(8), 60 | varint_encode(zigzag_encode(534)), 61 | varint_encode(9), 62 | new Uint8Array([0x2, 0x16].reverse()) 63 | ]); 64 | 65 | const serializedDepth = concat_byte_arrays([ 66 | varint_encode(10), 67 | varint_encode(zigzag_encode(-1000000)), 68 | varint_encode(11), 69 | varint_encode(1000000), 70 | varint_encode(12), 71 | varint_encode(zigzag_encode(-1000000)), 72 | varint_encode(13), 73 | new Uint8Array([0b11111111, 0b11110000, 0b10111101, 0b11000000].reverse()) 74 | ]); 75 | 76 | const serializedWear = concat_byte_arrays([ 77 | varint_encode(14), 78 | new Uint8Array([0x3f, 0x40, 0, 0].reverse()) 79 | ]); 80 | 81 | const serializedTradeable = concat_byte_arrays([ 82 | varint_encode(15), 83 | new Uint8Array([1]) 84 | ]); 85 | 86 | const serializedShare = concat_byte_arrays([ 87 | varint_encode(16), 88 | hex_decode('4090010000000000').reverse() 89 | ]); 90 | 91 | it('serialize string', () => { 92 | expect(serialize({ 93 | name: rawObject.name 94 | }, schema)).to.deep.equal(serializedName); 95 | }); 96 | 97 | it('deserialize string', () => { 98 | expect(deserialize(serializedName, schema)).to.deep.equal({ 99 | name: rawObject.name 100 | }); 101 | }); 102 | 103 | it('serialize ipfs', () => { 104 | expect(serialize({ 105 | img: rawObject.img 106 | }, schema)).to.deep.equal(serializedImage); 107 | }); 108 | 109 | it('deserialize ipfs', () => { 110 | expect(deserialize(serializedImage, schema)).to.deep.equal({ 111 | img: rawObject.img 112 | }); 113 | }); 114 | 115 | it('serialize int', () => { 116 | expect(serialize({ 117 | rarity1: rawObject.rarity1, 118 | rarity2: rawObject.rarity2, 119 | rarity3: rawObject.rarity3, 120 | rarity4: rawObject.rarity4 121 | }, schema)).to.deep.equal(serializedRarity); 122 | }); 123 | 124 | it('deserialize int', () => { 125 | expect(deserialize(serializedRarity, schema)).to.deep.equal({ 126 | rarity1: rawObject.rarity1, 127 | rarity2: rawObject.rarity2, 128 | rarity3: rawObject.rarity3, 129 | rarity4: rawObject.rarity4 130 | }); 131 | }); 132 | 133 | it('serialize negative int', () => { 134 | expect(serialize({ 135 | depth1: rawObject.depth1, 136 | depth2: rawObject.depth2, 137 | depth3: rawObject.depth3, 138 | depth4: rawObject.depth4 139 | }, schema)).to.deep.equal(serializedDepth); 140 | }); 141 | 142 | it('deserialize negative int', () => { 143 | expect(deserialize(serializedDepth, schema)).to.deep.equal({ 144 | depth1: rawObject.depth1, 145 | depth2: rawObject.depth2, 146 | depth3: rawObject.depth3, 147 | depth4: rawObject.depth4 148 | }); 149 | }); 150 | 151 | it('serialize float', () => { 152 | expect(serialize({ 153 | wear: rawObject.wear 154 | }, schema)).to.deep.equal(serializedWear); 155 | }); 156 | 157 | it('deserialize float', () => { 158 | expect(deserialize(serializedWear, schema)).to.deep.equal({ 159 | wear: rawObject.wear 160 | }); 161 | }); 162 | 163 | it('serialize double', () => { 164 | expect(serialize({ 165 | share: rawObject.share 166 | }, schema)).to.deep.equal(serializedShare); 167 | }); 168 | 169 | it('deserialize double', () => { 170 | expect(deserialize(serializedShare, schema)).to.deep.equal({ 171 | share: rawObject.share 172 | }); 173 | }); 174 | 175 | it('serialize bool', () => { 176 | expect(serialize({ 177 | tradeable: rawObject.tradeable 178 | }, schema)).to.deep.equal(serializedTradeable); 179 | }); 180 | 181 | it('deserialize bool', () => { 182 | expect(deserialize(serializedTradeable, schema)).to.deep.equal({ 183 | tradeable: rawObject.tradeable 184 | }); 185 | }); 186 | 187 | it('serialize object', () => { 188 | expect(serialize(rawObject, schema)).to.deep.equal(concat_byte_arrays([ 189 | serializedName, 190 | serializedImage, 191 | serializedRarity, 192 | serializedDepth, 193 | serializedWear, 194 | serializedTradeable, 195 | serializedShare 196 | ])); 197 | }); 198 | 199 | it('deserialize object', () => { 200 | expect(deserialize(concat_byte_arrays([ 201 | serializedName, 202 | serializedImage, 203 | serializedRarity, 204 | serializedDepth, 205 | serializedWear, 206 | serializedTradeable, 207 | serializedShare 208 | ]), schema)).to.deep.equal(rawObject); 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /test/serialization_contract.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { ObjectSchema } from '../src/Schema'; 4 | import { deserialize, serialize } from '../src/Serialization'; 5 | 6 | describe('Serialization Contract', () => { 7 | const schema = ObjectSchema([ 8 | { 9 | name: 'id', 10 | type: 'uint64', 11 | parent: 0 12 | }, 13 | { 14 | name: 'name', 15 | type: 'string', 16 | parent: 0 17 | }, 18 | { 19 | name: 'test2', 20 | type: 'fixed16[]', 21 | parent: 0 22 | }, 23 | { 24 | name: 'test3', 25 | type: 'float', 26 | parent: 0 27 | }, 28 | { 29 | name: 'unused', 30 | type: 'int32', 31 | parent: 0 32 | }, 33 | { 34 | name: 'isTrueTrue', 35 | type: 'bool', 36 | parent: 0 37 | }, 38 | { 39 | name: 'image', 40 | type: 'ipfs', 41 | parent: 0 42 | }, 43 | { 44 | name: 'gibnumber', 45 | type: 'int16', 46 | parent: 0 47 | }, 48 | { 49 | name: 'onemore', 50 | type: 'int16[]', 51 | parent: 0 52 | } 53 | ]); 54 | 55 | const serializedObject = new Uint8Array('04 12 05 06 4d 75 6e 69 63 68 06 04 12 00 7b 00 21 00 90 03 07 00 00 40 bf 09 01 0a 22 12 20 b7 41 a3 b1 cf 5b fe ae 20 8c 86 ef bf ac 8e 0b bc 0c 92 ee a7 ef 9a 2d 96 40 12 e6 c2 21 0f 4c 0b f6 01 0c 08 fe ff 03 ff ff 03 10 18 10 be 3a a9 03 f2 c0 01'.split(' ').map((val) => parseInt(val, 16))); 56 | const rawObject = { 57 | id: '18', 58 | name: 'Munich', 59 | test2: [18, 123, 33, 912], 60 | test3: -0.75, 61 | isTrueTrue: 1, 62 | image: 'Qmag1NRBcpYyz27Kq2demHavXoi7nwbcCfkUq5vh6nuNN7', 63 | gibnumber: 123, 64 | onemore: [32767, -32768, 8, 12, 8, 3743, -213, 12345] 65 | }; 66 | 67 | it('serialize object', () => { 68 | expect(serialize(rawObject, schema)).to.deep.equal(serializedObject); 69 | }); 70 | 71 | it('deserialize object', () => { 72 | expect(deserialize(serializedObject, schema)).to.deep.equal(rawObject); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/serialization_utf8.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { ObjectSchema } from '../src/Schema'; 4 | import { deserialize, serialize } from '../src/Serialization'; 5 | 6 | describe('Serialization Contract', () => { 7 | const schema = ObjectSchema([ 8 | { 9 | name: 'name', 10 | type: 'string' 11 | } 12 | ]); 13 | 14 | const serializedObject = new Uint8Array([4, 14, 116, 101, 115, 116, 32, 240, 159, 165, 182, 32, 116, 101, 115, 116]); 15 | const rawObject = { 16 | name: 'test 🥶 test' 17 | }; 18 | 19 | it('serialize object', () => { 20 | expect(serialize(rawObject, schema)).to.deep.equal(serializedObject); 21 | }); 22 | 23 | it('deserialize object', () => { 24 | expect(deserialize(serializedObject, schema)).to.deep.equal(rawObject); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": true, 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "./build", 8 | "strict": true, 9 | "esModuleInterop": true 10 | }, 11 | "include": ["src/**/*.ts"], 12 | "exclude": ["node_modules", "**/*.test.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.web.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "alwaysStrict": true, 7 | "sourceMap": false, 8 | "noImplicitAny": true, 9 | "moduleResolution": "node", 10 | "declaration": false, 11 | "allowJs": true, 12 | "downlevelIteration": true, 13 | "esModuleInterop": true, 14 | "lib": ["es2017", "dom"] 15 | }, 16 | "include": ["src/**/*.ts"], 17 | "exclude": ["**/*.test.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "project": "./tsconfig.json", 4 | "extends": [ 5 | "tslint-eslint-rules" 6 | ], 7 | "rulesDirectory": [ 8 | "node_modules/tslint-origin-ordered-imports-rule/dist", 9 | "node_modules/tslint-consistent-codestyle/rules" 10 | ], 11 | "rules": { 12 | "trailing-comma": [ 13 | true, 14 | { 15 | "multiline": "never", 16 | "singleline": "never" 17 | } 18 | ], 19 | "no-unused": true, 20 | "no-return-undefined": true, 21 | "no-unnecessary-else": true, 22 | "no-collapsible-if": true, 23 | "no-as-type-assertion": true, 24 | "arrow-return-shorthand": true, 25 | "callable-types": true, 26 | "class-name": true, 27 | "no-multi-spaces": true, 28 | "prefer-for-of": true, 29 | "typedef": [ 30 | true, 31 | "call-signature", 32 | "parameter", 33 | "property-declaration" 34 | ], 35 | "comment-format": [ 36 | true, 37 | "check-space" 38 | ], 39 | "curly": true, 40 | "eofline": true, 41 | "forin": true, 42 | "import-blacklist": [ 43 | true, 44 | "rxjs" 45 | ], 46 | "import-spacing": true, 47 | "indent": [ 48 | true, 49 | "spaces", 50 | 4 51 | ], 52 | "ter-indent": [ 53 | true, 54 | 4, 55 | { 56 | "SwitchCase": true 57 | } 58 | ], 59 | "interface-over-type-literal": false, 60 | "interface-name": false, 61 | "label-position": true, 62 | "origin-ordered-imports": [ 63 | true, 64 | "one-blank-line" 65 | ], 66 | "max-line-length": [ 67 | true, 68 | 160 69 | ], 70 | "member-access": [ 71 | true, 72 | "no-public" 73 | ], 74 | "member-ordering": [ 75 | true, 76 | { 77 | "order": [ 78 | "public-instance-field", 79 | "protected-instance-field", 80 | "private-instance-field", 81 | "public-constructor", 82 | "public-instance-method", 83 | "protected-instance-method", 84 | "private-instance-method" 85 | ] 86 | } 87 | ], 88 | "no-arg": true, 89 | "no-bitwise": false, 90 | "no-consecutive-blank-lines": true, 91 | "no-console": [ 92 | true, 93 | "log", 94 | "debug", 95 | "time", 96 | "timeEnd", 97 | "trace" 98 | ], 99 | "no-construct": true, 100 | "no-debugger": true, 101 | "no-duplicate-super": true, 102 | "no-empty": false, 103 | "no-empty-interface": true, 104 | "no-eval": true, 105 | "no-inferrable-types": [ 106 | true, 107 | "ignore-params" 108 | ], 109 | "no-misused-new": true, 110 | "no-non-null-assertion": true, 111 | "no-shadowed-variable": true, 112 | "no-string-literal": false, 113 | "no-string-throw": true, 114 | "no-switch-case-fall-through": true, 115 | "switch-default": true, 116 | "no-trailing-whitespace": false, 117 | "no-unnecessary-initializer": true, 118 | "no-unused-expression": true, 119 | "no-var-keyword": true, 120 | "object-literal-sort-keys": false, 121 | "one-line": [ 122 | true, 123 | "check-open-brace", 124 | "check-catch", 125 | "check-else", 126 | "check-whitespace" 127 | ], 128 | "prefer-const": true, 129 | "quotemark": [ 130 | true, 131 | "single" 132 | ], 133 | "radix": true, 134 | "semicolon": [ 135 | true, 136 | "always" 137 | ], 138 | "triple-equals": [ 139 | true 140 | ], 141 | "typedef-whitespace": [ 142 | true, 143 | { 144 | "call-signature": "nospace", 145 | "index-signature": "nospace", 146 | "parameter": "nospace", 147 | "property-declaration": "nospace", 148 | "variable-declaration": "nospace" 149 | } 150 | ], 151 | "unified-signatures": true, 152 | "variable-name": [ 153 | true, 154 | "ban-keywords", 155 | "check-format", 156 | "allow-trailing-underscore", 157 | "allow-pascal-case", 158 | "allow-leading-underscore" 159 | ], 160 | "whitespace": [ 161 | true, 162 | "check-branch", 163 | "check-decl", 164 | "check-operator", 165 | "check-separator", 166 | "check-type", 167 | "check-module" 168 | ] 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: { 5 | atomicassets: './src/index.ts', 6 | }, 7 | mode: 'production', 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.ts$/, 12 | use: { 13 | loader: 'ts-loader', 14 | options: { configFile: 'tsconfig.web.json' } 15 | }, 16 | exclude: /node_modules/, 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | extensions: ['.ts', '.js'] 22 | }, 23 | output: { 24 | filename: x => x.chunk.name.replace('_', '-') + '.js', 25 | library: '[name]', 26 | path: path.resolve(__dirname, 'dist'), 27 | } 28 | }; --------------------------------------------------------------------------------