├── CNAME
├── .prettierignore
├── CODEOWNERS
├── borsh-ts
├── test
│ ├── .eslintrc.yml
│ ├── fuzz
│ │ ├── corpus
│ │ │ ├── 004b705c22403d1c22ceab37181f6340b5b269b7faee05d97fe65b1ab91cd171
│ │ │ ├── 25b2da453a8cb0d574d31a06f0a57fc7b5f2cb5c08ba00ff900a79ebd0637da7
│ │ │ ├── 2f6f8117189cb82e111a13b3ffb19fcaf35deef41f8c084726c5aac5a4551696
│ │ │ ├── 4eb3f263ac0d9bac83d67e28d3381c64c61654579dc455a9e5ccaa54bc093721
│ │ │ ├── 5a0ac0f8936af102c2ea974ba237896a8ee4dffebd27b65c8101a1d8897d6409
│ │ │ ├── 67fc403d488908e0a27d74a2de056c34c40850bbe61b9ebc82cf48365484f4fc
│ │ │ ├── 7fa19d2ffc291390ebe8a238baae9bddbd620d018f803540e09ac1bc82ec3a6c
│ │ │ ├── 824d229c23c200a937bcd558a5c55d25830a8efffb9e8661a81e2db3584ca512
│ │ │ ├── 9d6a787b7d95c13bb67d02137cf3e506f550fcfa19363050d1e4917250d83d32
│ │ │ ├── a330e2fd8d25278e3d28205107e67373646fe7c5370030819ccc13990375ca73
│ │ │ ├── a53d5be4493c7cccab1d70bdaf44b17f2cd52501f1b5567ed05406163cf96fef
│ │ │ ├── b7361e727623b1cdafda1411e27c119009d0fcccbdddd5fe669c8c02bf04f7dd
│ │ │ ├── b98f80e8a618038000215158136c1f2dbd9ed3cad450556faf7b18db4184b42e
│ │ │ ├── bec080831059d96afd82176d6e8ee36819bb22b7304f4e23f61d189a6c221f92
│ │ │ ├── cf94b13d93b7accf950d3d2a1be308f7d25d21f81622a7ffc7da3733c03eb572
│ │ │ ├── d2897d4dc13f861fb5e7caf8e5b7e1c9962a36c8489ada9cb0f798c1ec967eb7
│ │ │ ├── dfbc571f6a049f8f66f2fcb510fffc51f5c78078db2a2fb5cf19b0bd840291ee
│ │ │ ├── e08ccfae0c2fd6432c8e1bf841050f0b2d78fa519ac76f178db0757d1bd3e96e
│ │ │ └── ecd71b60d1820dbbcb06d1f981ab86d16876888bb7ba665637c5717c0a8916b2
│ │ ├── transaction-example
│ │ │ ├── enums.d.ts
│ │ │ ├── serialize.js
│ │ │ ├── enums.js
│ │ │ ├── serialize.d.ts
│ │ │ ├── key_pair.d.ts
│ │ │ ├── signer.d.ts
│ │ │ ├── signer.js
│ │ │ ├── transaction.d.ts
│ │ │ ├── key_pair.js
│ │ │ └── transaction.js
│ │ └── borsh-roundtrip.js
│ └── serialize.test.js
├── .eslintrc.yml
└── index.ts
├── .gitignore
├── .travis.yml
├── .eslintrc.yml
├── .github
└── workflows
│ └── borsh.yml
├── tsconfig.json
├── LICENSE-MIT.txt
├── package.json
├── lib
├── index.d.ts
└── index.js
├── README.md
└── LICENSE-APACHE
/CNAME:
--------------------------------------------------------------------------------
1 | borsh.io
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.d.ts
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @volovyk-s @ailisp
2 |
--------------------------------------------------------------------------------
/borsh-ts/test/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: '../../.eslintrc.yml'
2 | env:
3 | jest: true
4 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/004b705c22403d1c22ceab37181f6340b5b269b7faee05d97fe65b1ab91cd171:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/004b705c22403d1c22ceab37181f6340b5b269b7faee05d97fe65b1ab91cd171
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/25b2da453a8cb0d574d31a06f0a57fc7b5f2cb5c08ba00ff900a79ebd0637da7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/25b2da453a8cb0d574d31a06f0a57fc7b5f2cb5c08ba00ff900a79ebd0637da7
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/2f6f8117189cb82e111a13b3ffb19fcaf35deef41f8c084726c5aac5a4551696:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/2f6f8117189cb82e111a13b3ffb19fcaf35deef41f8c084726c5aac5a4551696
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/4eb3f263ac0d9bac83d67e28d3381c64c61654579dc455a9e5ccaa54bc093721:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/4eb3f263ac0d9bac83d67e28d3381c64c61654579dc455a9e5ccaa54bc093721
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/5a0ac0f8936af102c2ea974ba237896a8ee4dffebd27b65c8101a1d8897d6409:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/5a0ac0f8936af102c2ea974ba237896a8ee4dffebd27b65c8101a1d8897d6409
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/67fc403d488908e0a27d74a2de056c34c40850bbe61b9ebc82cf48365484f4fc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/67fc403d488908e0a27d74a2de056c34c40850bbe61b9ebc82cf48365484f4fc
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/7fa19d2ffc291390ebe8a238baae9bddbd620d018f803540e09ac1bc82ec3a6c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/7fa19d2ffc291390ebe8a238baae9bddbd620d018f803540e09ac1bc82ec3a6c
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/824d229c23c200a937bcd558a5c55d25830a8efffb9e8661a81e2db3584ca512:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/824d229c23c200a937bcd558a5c55d25830a8efffb9e8661a81e2db3584ca512
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/9d6a787b7d95c13bb67d02137cf3e506f550fcfa19363050d1e4917250d83d32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/9d6a787b7d95c13bb67d02137cf3e506f550fcfa19363050d1e4917250d83d32
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/a330e2fd8d25278e3d28205107e67373646fe7c5370030819ccc13990375ca73:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/a330e2fd8d25278e3d28205107e67373646fe7c5370030819ccc13990375ca73
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/a53d5be4493c7cccab1d70bdaf44b17f2cd52501f1b5567ed05406163cf96fef:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/a53d5be4493c7cccab1d70bdaf44b17f2cd52501f1b5567ed05406163cf96fef
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/b7361e727623b1cdafda1411e27c119009d0fcccbdddd5fe669c8c02bf04f7dd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/b7361e727623b1cdafda1411e27c119009d0fcccbdddd5fe669c8c02bf04f7dd
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/b98f80e8a618038000215158136c1f2dbd9ed3cad450556faf7b18db4184b42e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/b98f80e8a618038000215158136c1f2dbd9ed3cad450556faf7b18db4184b42e
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/bec080831059d96afd82176d6e8ee36819bb22b7304f4e23f61d189a6c221f92:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/bec080831059d96afd82176d6e8ee36819bb22b7304f4e23f61d189a6c221f92
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/cf94b13d93b7accf950d3d2a1be308f7d25d21f81622a7ffc7da3733c03eb572:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/cf94b13d93b7accf950d3d2a1be308f7d25d21f81622a7ffc7da3733c03eb572
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/d2897d4dc13f861fb5e7caf8e5b7e1c9962a36c8489ada9cb0f798c1ec967eb7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/d2897d4dc13f861fb5e7caf8e5b7e1c9962a36c8489ada9cb0f798c1ec967eb7
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/dfbc571f6a049f8f66f2fcb510fffc51f5c78078db2a2fb5cf19b0bd840291ee:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/dfbc571f6a049f8f66f2fcb510fffc51f5c78078db2a2fb5cf19b0bd840291ee
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/e08ccfae0c2fd6432c8e1bf841050f0b2d78fa519ac76f178db0757d1bd3e96e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/e08ccfae0c2fd6432c8e1bf841050f0b2d78fa519ac76f178db0757d1bd3e96e
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/corpus/ecd71b60d1820dbbcb06d1f981ab86d16876888bb7ba665637c5717c0a8916b2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ailisp/borsh-js/master/borsh-ts/test/fuzz/corpus/ecd71b60d1820dbbcb06d1f981ab86d16876888bb7ba665637c5717c0a8916b2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Idea IDE files.
2 | **/.idea/
3 |
4 | # VSCode IDE files.
5 | **/.vscode/
6 |
7 | # OS X
8 | **/.DS_Store/
9 |
10 | # JS modules
11 | **/node_modules/
12 | package-lock.json
13 | yarn.lock
14 | .nyc_output
15 |
16 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/enums.d.ts:
--------------------------------------------------------------------------------
1 | export declare abstract class Enum {
2 | enum: string;
3 | constructor(properties: any);
4 | }
5 | export declare abstract class Assignable {
6 | constructor(properties: any);
7 | }
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12
4 |
5 | before_script:
6 | - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash"
7 |
8 | script:
9 | - fossa init
10 | - fossa analyze --server-scan
11 | - fossa test
12 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | env:
2 | es6: true
3 | node: true
4 | extends:
5 | - 'eslint:recommended'
6 | parserOptions:
7 | ecmaVersion: 2018
8 | rules:
9 | indent:
10 | - error
11 | - 4
12 | linebreak-style:
13 | - error
14 | - unix
15 | quotes:
16 | - error
17 | - single
18 | semi:
19 | - error
20 | - always
21 | no-console: 0
22 |
--------------------------------------------------------------------------------
/borsh-ts/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | env:
2 | es6: true
3 | node: true
4 | extends:
5 | - 'eslint:recommended'
6 | - 'plugin:@typescript-eslint/eslint-recommended'
7 | - 'plugin:@typescript-eslint/recommended'
8 | parser: '@typescript-eslint/parser'
9 | rules:
10 | no-inner-declarations: 1
11 | '@typescript-eslint/no-explicit-any': 1
12 | '@typescript-eslint/camelcase': 1
13 | '@typescript-eslint/explicit-function-return-type': 1
14 | '@typescript-eslint/no-use-before-define': 1
15 |
16 | parserOptions:
17 | ecmaVersion: 2018
18 | sourceType: module
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/serialize.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | const bs58_1 = __importDefault(require("bs58"));
6 | function base_encode(value) {
7 | if (typeof (value) === 'string') {
8 | value = Buffer.from(value, 'utf8');
9 | }
10 | return bs58_1.default.encode(Buffer.from(value));
11 | }
12 | exports.base_encode = base_encode;
13 | function base_decode(value) {
14 | return Buffer.from(bs58_1.default.decode(value));
15 | }
16 | exports.base_decode = base_decode;
17 |
--------------------------------------------------------------------------------
/.github/workflows/borsh.yml:
--------------------------------------------------------------------------------
1 | name: Borsh Tests
2 | on: [push]
3 | jobs:
4 | unit-tests:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - uses: actions/setup-node@v2
9 | with:
10 | node-version: '14'
11 | - run: yarn install
12 | - run: yarn test
13 | prettier:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v2
18 | with:
19 | node-version: '14'
20 | - run: yarn install
21 | - run: yarn pretty:check
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "lib": [
5 | "es2015",
6 | "esnext",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "target": "esnext",
11 | "moduleResolution": "node",
12 | "outDir": "./lib",
13 | "declaration": true,
14 | "preserveSymlinks": true,
15 | "preserveWatchOutput": true,
16 | "pretty": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "noFallthroughCasesInSwitch": true,
19 | "noImplicitAny": false,
20 | "noImplicitReturns": true,
21 | "experimentalDecorators": true,
22 | "noUnusedLocals": true
23 | },
24 | "files": ["./borsh-ts/index.ts"]
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/enums.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | class Enum {
4 | constructor(properties) {
5 | if (Object.keys(properties).length !== 1) {
6 | throw new Error('Enum can only take single value');
7 | }
8 | Object.keys(properties).map((key) => {
9 | this[key] = properties[key];
10 | this.enum = key;
11 | });
12 | }
13 | }
14 | exports.Enum = Enum;
15 | class Assignable {
16 | constructor(properties) {
17 | Object.keys(properties).map((key) => {
18 | this[key] = properties[key];
19 | });
20 | }
21 | }
22 | exports.Assignable = Assignable;
23 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/borsh-roundtrip.js:
--------------------------------------------------------------------------------
1 | const borsh = require('../../../lib/index.js');
2 | const transaction = require('./transaction-example/transaction');
3 |
4 | exports.fuzz = input => {
5 | try {
6 | const deserialized = borsh.deserialize(transaction.SCHEMA, transaction.Transaction, input);
7 | const serialized = borsh.serialize(transaction.SCHEMA, deserialized);
8 | if (!serialized.equals(input)) {
9 | console.log(`Mismatching output:\n${serialized.toString('hex')}\nand input:\n${input.toString('hex')}`);
10 | throw new Error('Mismatching input and output');
11 | }
12 | } catch (e) {
13 | if (e instanceof borsh.BorshError) {
14 | // Do nothing
15 | } else {
16 | throw e;
17 | }
18 | }
19 | };
--------------------------------------------------------------------------------
/LICENSE-MIT.txt:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any
2 | person obtaining a copy of this software and associated
3 | documentation files (the "Software"), to deal in the
4 | Software without restriction, including without
5 | limitation the rights to use, copy, modify, merge,
6 | publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software
8 | is furnished to do so, subject to the following
9 | conditions:
10 |
11 | The above copyright notice and this permission notice
12 | shall be included in all copies or substantial portions
13 | of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/serialize.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import BN from 'bn.js';
3 | export declare function base_encode(value: Uint8Array | string): string;
4 | export declare function base_decode(value: string): Uint8Array;
5 | export declare type Schema = Map;
6 | export declare class BorshError extends Error {
7 | originalMessage: string;
8 | fieldPath: string[];
9 | constructor(message: string);
10 | addToFieldPath(fieldName: string): void;
11 | }
12 | export declare class BinaryWriter {
13 | buf: Buffer;
14 | length: number;
15 | constructor();
16 | maybe_resize(): void;
17 | write_u8(value: number): void;
18 | write_u32(value: number): void;
19 | write_u64(value: BN): void;
20 | write_u128(value: BN): void;
21 | private write_buffer;
22 | write_string(str: string): void;
23 | write_fixed_array(array: Uint8Array): void;
24 | write_array(array: any[], fn: any): void;
25 | toArray(): Uint8Array;
26 | }
27 | export declare class BinaryReader {
28 | buf: Buffer;
29 | offset: number;
30 | constructor(buf: Buffer);
31 | read_u8(): number;
32 | read_u32(): number;
33 | read_u64(): BN;
34 | read_u128(): BN;
35 | private read_buffer;
36 | read_string(): string;
37 | read_fixed_array(len: number): Uint8Array;
38 | read_array(fn: any): any[];
39 | }
40 | export declare function serialize(schema: Schema, obj: any): Uint8Array;
41 | export declare function deserialize(schema: Schema, classType: any, buffer: Buffer): any;
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "borsh",
3 | "version": "0.7.0",
4 | "description": "Binary Object Representation Serializer for Hashing",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "files": [
8 | "lib",
9 | "LICENSE-APACHE",
10 | "LICENSE-MIT.txt"
11 | ],
12 | "scripts": {
13 | "build": "tsc -p ./tsconfig.json",
14 | "test": "jest test --runInBand",
15 | "fuzz": "jsfuzz borsh-ts/test/fuzz/borsh-roundtrip.js borsh-ts/test/fuzz/corpus/",
16 | "dev": "yarn build -w",
17 | "pretest": "yarn build",
18 | "lint": "eslint borsh-ts/**/*.ts",
19 | "pretty": "prettier --write borsh-ts/**/*.ts package.json",
20 | "pretty:check": "yarn prettier --loglevel error --check borsh-ts/**/*.ts package.json",
21 | "fix": "eslint borsh-ts/**/*.ts --fix"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/near/borsh-js.git"
26 | },
27 | "keywords": [
28 | "serializer",
29 | "binary",
30 | "serializer",
31 | "deserializer",
32 | "consistency",
33 | "deterministic"
34 | ],
35 | "author": "Near Inc",
36 | "license": "Apache-2.0",
37 | "bugs": {
38 | "url": "https://github.com/near/borsh-js/issues"
39 | },
40 | "homepage": "https://github.com/near/borsh-js#readme",
41 | "devDependencies": {
42 | "@types/babel__core": "^7.1.2",
43 | "@types/babel__template": "^7.0.2",
44 | "@types/bn.js": "^5.1.0",
45 | "@types/node": "^12.7.3",
46 | "@typescript-eslint/eslint-plugin": "^2.18.0",
47 | "@typescript-eslint/parser": "^2.18.0",
48 | "bs58": "^4.0.0",
49 | "eslint": "^6.5.1",
50 | "jest": "^26.0.1",
51 | "js-sha256": "^0.9.0",
52 | "jsfuzz": "^1.0.14",
53 | "prettier": "^2.4.1",
54 | "typescript": "^3.6.2"
55 | },
56 | "dependencies": {
57 | "bn.js": "^5.2.0",
58 | "bs58": "^4.0.0",
59 | "buffer": "^6.0.3",
60 | "text-encoding-utf-8": "^1.0.2"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import BN from "bn.js";
3 | export declare function baseEncode(value: Uint8Array | string): string;
4 | export declare function baseDecode(value: string): Buffer;
5 | export declare type Schema = Map;
6 | export declare class BorshError extends Error {
7 | originalMessage: string;
8 | fieldPath: string[];
9 | constructor(message: string);
10 | addToFieldPath(fieldName: string): void;
11 | }
12 | export declare class BinaryWriter {
13 | buf: Buffer;
14 | length: number;
15 | constructor();
16 | maybeResize(): void;
17 | writeU8(value: number): void;
18 | writeU16(value: number): void;
19 | writeU32(value: number): void;
20 | writeU64(value: number | BN): void;
21 | writeU128(value: number | BN): void;
22 | writeU256(value: number | BN): void;
23 | writeU512(value: number | BN): void;
24 | private writeBuffer;
25 | writeString(str: string): void;
26 | writeFixedArray(array: Uint8Array): void;
27 | writeArray(array: any[], fn: any): void;
28 | toArray(): Uint8Array;
29 | }
30 | export declare class BinaryReader {
31 | buf: Buffer;
32 | offset: number;
33 | constructor(buf: Buffer);
34 | readU8(): number;
35 | readU16(): number;
36 | readU32(): number;
37 | readU64(): BN;
38 | readU128(): BN;
39 | readU256(): BN;
40 | readU512(): BN;
41 | private readBuffer;
42 | readString(): string;
43 | readFixedArray(len: number): Uint8Array;
44 | readArray(fn: any): any[];
45 | }
46 | export declare function serialize(schema: Schema, obj: any, Writer?: typeof BinaryWriter): Uint8Array;
47 | export declare function deserialize(schema: Schema, classType: {
48 | new (args: any): T;
49 | }, buffer: Buffer, Reader?: typeof BinaryReader): T;
50 | export declare function deserializeUnchecked(schema: Schema, classType: {
51 | new (args: any): T;
52 | }, buffer: Buffer, Reader?: typeof BinaryReader): T;
53 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/key_pair.d.ts:
--------------------------------------------------------------------------------
1 | import { Assignable } from './enums';
2 | export declare type Arrayish = string | ArrayLike;
3 | export interface Signature {
4 | signature: Uint8Array;
5 | publicKey: PublicKey;
6 | }
7 | /** All supported key types */
8 | export declare enum KeyType {
9 | ED25519 = 0
10 | }
11 | /**
12 | * PublicKey representation that has type and bytes of the key.
13 | */
14 | export declare class PublicKey extends Assignable {
15 | keyType: KeyType;
16 | data: Uint8Array;
17 | static from(value: string | PublicKey): PublicKey;
18 | static fromString(encodedKey: string): PublicKey;
19 | toString(): string;
20 | }
21 | export declare abstract class KeyPair {
22 | abstract sign(message: Uint8Array): Signature;
23 | abstract verify(message: Uint8Array, signature: Uint8Array): boolean;
24 | abstract toString(): string;
25 | abstract getPublicKey(): PublicKey;
26 | /**
27 | * @param curve Name of elliptical curve, case-insensitive
28 | * @returns Random KeyPair based on the curve
29 | */
30 | static fromRandom(curve: string): KeyPair;
31 | static fromString(encodedKey: string): KeyPair;
32 | }
33 | /**
34 | * This class provides key pair functionality for Ed25519 curve:
35 | * generating key pairs, encoding key pairs, signing and verifying.
36 | */
37 | export declare class KeyPairEd25519 extends KeyPair {
38 | readonly publicKey: PublicKey;
39 | readonly secretKey: string;
40 | /**
41 | * Construct an instance of key pair given a secret key.
42 | * It's generally assumed that these are encoded in base58.
43 | * @param {string} secretKey
44 | */
45 | constructor(secretKey: string);
46 | /**
47 | * Generate a new random keypair.
48 | * @example
49 | * const keyRandom = KeyPair.fromRandom();
50 | * keyRandom.publicKey
51 | * // returns [PUBLIC_KEY]
52 | *
53 | * keyRandom.secretKey
54 | * // returns [SECRET_KEY]
55 | */
56 | static fromRandom(): KeyPairEd25519;
57 | sign(message: Uint8Array): Signature;
58 | verify(message: Uint8Array, signature: Uint8Array): boolean;
59 | toString(): string;
60 | getPublicKey(): PublicKey;
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Borsh JS
2 |
3 | [](https://opensource.org/licenses/Apache-2.0)
4 | [](https://opensource.org/licenses/MIT)
5 | [](https://discord.gg/Vyp7ETM)
6 | [](https://travis-ci.com/near/borsh-js)
7 | [](https://npmjs.com/borsh)
8 | [](https://npmjs.com/borsh)
9 |
10 | **Borsh JS** is an implementation of the [Borsh] binary serialization format for
11 | JavaScript and TypeScript projects.
12 |
13 | Borsh stands for _Binary Object Representation Serializer for Hashing_. It is meant to be used in security-critical projects as it prioritizes consistency,
14 | safety, speed, and comes with a strict specification.
15 |
16 | ## Examples
17 | ### Serializing an object
18 | ```javascript
19 | const value = new Test({ x: 255, y: 20, z: '123', q: [1, 2, 3] });
20 | const schema = new Map([[Test, { kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] }]]);
21 | const buffer = borsh.serialize(schema, value);
22 | ```
23 |
24 | ### Deserializing an object
25 | ```javascript
26 | const newValue = borsh.deserialize(schema, Test, buffer);
27 | ```
28 |
29 | ## Type Mappings
30 |
31 | | Borsh | TypeScript |
32 | |-----------------------|----------------|
33 | | `u8` integer | `number` |
34 | | `u16` integer | `number` |
35 | | `u32` integer | `number` |
36 | | `u64` integer | `BN` |
37 | | `u128` integer | `BN` |
38 | | `u256` integer | `BN` |
39 | | `u512` integer | `BN` |
40 | | `f32` float | N/A |
41 | | `f64` float | N/A |
42 | | fixed-size byte array | `Uint8Array` |
43 | | UTF-8 string | `string` |
44 | | option | `null` or type |
45 | | map | N/A |
46 | | set | N/A |
47 | | structs | `any` |
48 |
49 | ## Contributing
50 |
51 | Install dependencies:
52 | ```bash
53 | yarn install
54 | ```
55 |
56 | Continuously build with:
57 | ```bash
58 | yarn dev
59 | ```
60 |
61 | Run tests:
62 | ```bash
63 | yarn test
64 | ```
65 |
66 | Run linter
67 | ```bash
68 | yarn lint
69 | ```
70 | ## Publish
71 |
72 | Prepare `dist` version by running:
73 | ```bash
74 | yarn build
75 | ```
76 |
77 | When publishing to npm use [np](https://github.com/sindresorhus/np).
78 |
79 | # License
80 | This repository is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
81 | See [LICENSE-MIT](LICENSE-MIT.txt) and [LICENSE-APACHE](LICENSE-APACHE) for details.
82 |
83 | [Borsh]: https://borsh.io
84 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/signer.d.ts:
--------------------------------------------------------------------------------
1 | import { Signature, KeyPair, PublicKey } from './key_pair';
2 | import { KeyStore } from './key_stores';
3 | /**
4 | * General signing interface, can be used for in memory signing, RPC singing, external wallet, HSM, etc.
5 | */
6 | export declare abstract class Signer {
7 | /**
8 | * Creates new key and returns public key.
9 | */
10 | abstract createKey(accountId: string, networkId?: string): Promise;
11 | /**
12 | * Returns public key for given account / network.
13 | * @param accountId accountId to retrieve from.
14 | * @param networkId The targeted network. (ex. default, betanet, etc…)
15 | */
16 | abstract getPublicKey(accountId?: string, networkId?: string): Promise;
17 | /**
18 | * Signs given message, by first hashing with sha256.
19 | * @param message message to sign.
20 | * @param accountId accountId to use for signing.
21 | * @param networkId The targeted network. (ex. default, betanet, etc…)
22 | */
23 | abstract signMessage(message: Uint8Array, accountId?: string, networkId?: string): Promise;
24 | }
25 | /**
26 | * Signs using in memory key store.
27 | */
28 | export declare class InMemorySigner extends Signer {
29 | readonly keyStore: KeyStore;
30 | constructor(keyStore: KeyStore);
31 | /**
32 | * Creates a single account Signer instance with account, network and keyPair provided.
33 | *
34 | * Intended to be useful for temporary keys (e.g. claiming a Linkdrop).
35 | *
36 | * @param networkId The targeted network. (ex. default, betanet, etc…)
37 | * @param accountId The NEAR account to assign the key pair to
38 | * @param keyPair The keyPair to use for signing
39 | */
40 | static fromKeyPair(networkId: string, accountId: string, keyPair: KeyPair): Promise;
41 | /**
42 | * Creates a public key for the account given
43 | * @param accountId The NEAR account to assign a public key to
44 | * @param networkId The targeted network. (ex. default, betanet, etc…)
45 | * @returns {Promise}
46 | */
47 | createKey(accountId: string, networkId: string): Promise;
48 | /**
49 | * Gets the existing public key for a given account
50 | * @param accountId The NEAR account to assign a public key to
51 | * @param networkId The targeted network. (ex. default, betanet, etc…)
52 | * @returns {Promise} Returns the public key or null if not found
53 | */
54 | getPublicKey(accountId?: string, networkId?: string): Promise;
55 | /**
56 | * @param message A message to be signed, typically a serialized transaction
57 | * @param accountId the NEAR account signing the message
58 | * @param networkId The targeted network. (ex. default, betanet, etc…)
59 | * @returns {Promise}
60 | */
61 | signMessage(message: Uint8Array, accountId?: string, networkId?: string): Promise;
62 | toString(): string;
63 | }
64 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/signer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const js_sha256_1 = __importDefault(require("js-sha256"));
7 | const key_pair_1 = require("./key_pair");
8 | const key_stores_1 = require("./key_stores");
9 | /**
10 | * General signing interface, can be used for in memory signing, RPC singing, external wallet, HSM, etc.
11 | */
12 | class Signer {
13 | }
14 | exports.Signer = Signer;
15 | /**
16 | * Signs using in memory key store.
17 | */
18 | class InMemorySigner extends Signer {
19 | constructor(keyStore) {
20 | super();
21 | this.keyStore = keyStore;
22 | }
23 | /**
24 | * Creates a single account Signer instance with account, network and keyPair provided.
25 | *
26 | * Intended to be useful for temporary keys (e.g. claiming a Linkdrop).
27 | *
28 | * @param networkId The targeted network. (ex. default, betanet, etc…)
29 | * @param accountId The NEAR account to assign the key pair to
30 | * @param keyPair The keyPair to use for signing
31 | */
32 | static async fromKeyPair(networkId, accountId, keyPair) {
33 | const keyStore = new key_stores_1.InMemoryKeyStore();
34 | await keyStore.setKey(networkId, accountId, keyPair);
35 | return new InMemorySigner(keyStore);
36 | }
37 | /**
38 | * Creates a public key for the account given
39 | * @param accountId The NEAR account to assign a public key to
40 | * @param networkId The targeted network. (ex. default, betanet, etc…)
41 | * @returns {Promise}
42 | */
43 | async createKey(accountId, networkId) {
44 | const keyPair = key_pair_1.KeyPair.fromRandom('ed25519');
45 | await this.keyStore.setKey(networkId, accountId, keyPair);
46 | return keyPair.getPublicKey();
47 | }
48 | /**
49 | * Gets the existing public key for a given account
50 | * @param accountId The NEAR account to assign a public key to
51 | * @param networkId The targeted network. (ex. default, betanet, etc…)
52 | * @returns {Promise} Returns the public key or null if not found
53 | */
54 | async getPublicKey(accountId, networkId) {
55 | const keyPair = await this.keyStore.getKey(networkId, accountId);
56 | if (keyPair === null) {
57 | return null;
58 | }
59 | return keyPair.getPublicKey();
60 | }
61 | /**
62 | * @param message A message to be signed, typically a serialized transaction
63 | * @param accountId the NEAR account signing the message
64 | * @param networkId The targeted network. (ex. default, betanet, etc…)
65 | * @returns {Promise}
66 | */
67 | async signMessage(message, accountId, networkId) {
68 | const hash = new Uint8Array(js_sha256_1.default.sha256.array(message));
69 | if (!accountId) {
70 | throw new Error('InMemorySigner requires provided account id');
71 | }
72 | const keyPair = await this.keyStore.getKey(networkId, accountId);
73 | if (keyPair === null) {
74 | throw new Error(`Key for ${accountId} not found in ${networkId}`);
75 | }
76 | return keyPair.sign(hash);
77 | }
78 | toString() {
79 | return `InMemorySigner(${this.keyStore})`;
80 | }
81 | }
82 | exports.InMemorySigner = InMemorySigner;
83 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/transaction.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import BN from 'bn.js';
3 | import { Enum, Assignable } from './enums';
4 | import { KeyType, PublicKey } from './key_pair';
5 | import { Signer } from './signer';
6 | export declare class FunctionCallPermission extends Assignable {
7 | allowance?: BN;
8 | receiverId: string;
9 | methodNames: string[];
10 | }
11 | export declare class FullAccessPermission extends Assignable {
12 | }
13 | export declare class AccessKeyPermission extends Enum {
14 | functionCall: FunctionCallPermission;
15 | fullAccess: FullAccessPermission;
16 | }
17 | export declare class AccessKey extends Assignable {
18 | nonce: number;
19 | permission: AccessKeyPermission;
20 | }
21 | export declare function fullAccessKey(): AccessKey;
22 | export declare function functionCallAccessKey(receiverId: string, methodNames: string[], allowance?: BN): AccessKey;
23 | export declare class IAction extends Assignable {
24 | }
25 | export declare class CreateAccount extends IAction {
26 | }
27 | export declare class DeployContract extends IAction {
28 | code: Uint8Array;
29 | }
30 | export declare class FunctionCall extends IAction {
31 | methodName: string;
32 | args: Uint8Array;
33 | gas: BN;
34 | deposit: BN;
35 | }
36 | export declare class Transfer extends IAction {
37 | deposit: BN;
38 | }
39 | export declare class Stake extends IAction {
40 | stake: BN;
41 | publicKey: PublicKey;
42 | }
43 | export declare class AddKey extends IAction {
44 | publicKey: PublicKey;
45 | accessKey: AccessKey;
46 | }
47 | export declare class DeleteKey extends IAction {
48 | publicKey: PublicKey;
49 | }
50 | export declare class DeleteAccount extends IAction {
51 | beneficiaryId: string;
52 | }
53 | export declare function createAccount(): Action;
54 | export declare function deployContract(code: Uint8Array): Action;
55 | /**
56 | * Constructs {@link Action} instance representing contract method call.
57 | *
58 | * @param methodName the name of the method to call
59 | * @param args arguments to pass to method. Can be either plain JS object which gets serialized as JSON automatically
60 | * or `Uint8Array` instance which represents bytes passed as is.
61 | * @param gas max amount of gas that method call can use
62 | * @param deposit amount of NEAR (in yoctoNEAR) to send together with the call
63 | */
64 | export declare function functionCall(methodName: string, args: Uint8Array | object, gas: BN, deposit: BN): Action;
65 | export declare function transfer(deposit: BN): Action;
66 | export declare function stake(stake: BN, publicKey: PublicKey): Action;
67 | export declare function addKey(publicKey: PublicKey, accessKey: AccessKey): Action;
68 | export declare function deleteKey(publicKey: PublicKey): Action;
69 | export declare function deleteAccount(beneficiaryId: string): Action;
70 | export declare class Signature extends Assignable {
71 | keyType: KeyType;
72 | data: Uint8Array;
73 | }
74 | export declare class Transaction extends Assignable {
75 | signerId: string;
76 | publicKey: PublicKey;
77 | nonce: number;
78 | receiverId: string;
79 | actions: Action[];
80 | blockHash: Uint8Array;
81 | encode(): Uint8Array;
82 | static decode(bytes: Buffer): Transaction;
83 | }
84 | export declare class SignedTransaction extends Assignable {
85 | transaction: Transaction;
86 | signature: Signature;
87 | encode(): Uint8Array;
88 | static decode(bytes: Buffer): SignedTransaction;
89 | }
90 | /**
91 | * Contains a list of the valid transaction Actions available with this API
92 | */
93 | export declare class Action extends Enum {
94 | createAccount: CreateAccount;
95 | deployContract: DeployContract;
96 | functionCall: FunctionCall;
97 | transfer: Transfer;
98 | stake: Stake;
99 | addKey: AddKey;
100 | deleteKey: DeleteKey;
101 | deleteAccount: DeleteAccount;
102 | }
103 | export declare const SCHEMA: Map;
104 | export declare function createTransaction(signerId: string, publicKey: PublicKey, receiverId: string, nonce: number, actions: Action[], blockHash: Uint8Array): Transaction;
105 | export declare function signTransaction(transaction: Transaction, signer: Signer, accountId?: string, networkId?: string): Promise<[Uint8Array, SignedTransaction]>;
106 | export declare function signTransaction(receiverId: string, nonce: number, actions: Action[], blockHash: Uint8Array, signer: Signer, accountId?: string, networkId?: string): Promise<[Uint8Array, SignedTransaction]>;
107 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/key_pair.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const tweetnacl_1 = __importDefault(require("tweetnacl"));
7 | const serialize_1 = require("./serialize");
8 | const enums_1 = require("./enums");
9 | /** All supported key types */
10 | var KeyType;
11 | (function (KeyType) {
12 | KeyType[KeyType["ED25519"] = 0] = "ED25519";
13 | })(KeyType = exports.KeyType || (exports.KeyType = {}));
14 | function key_type_to_str(keyType) {
15 | switch (keyType) {
16 | case KeyType.ED25519: return 'ed25519';
17 | default: throw new Error(`Unknown key type ${keyType}`);
18 | }
19 | }
20 | function str_to_key_type(keyType) {
21 | switch (keyType.toLowerCase()) {
22 | case 'ed25519': return KeyType.ED25519;
23 | default: throw new Error(`Unknown key type ${keyType}`);
24 | }
25 | }
26 | /**
27 | * PublicKey representation that has type and bytes of the key.
28 | */
29 | class PublicKey extends enums_1.Assignable {
30 | static from(value) {
31 | if (typeof value === 'string') {
32 | return PublicKey.fromString(value);
33 | }
34 | return value;
35 | }
36 | static fromString(encodedKey) {
37 | const parts = encodedKey.split(':');
38 | if (parts.length === 1) {
39 | return new PublicKey({ keyType: KeyType.ED25519, data: serialize_1.base_decode(parts[0]) });
40 | }
41 | else if (parts.length === 2) {
42 | return new PublicKey({ keyType: str_to_key_type(parts[0]), data: serialize_1.base_decode(parts[1]) });
43 | }
44 | else {
45 | throw new Error('Invalid encoded key format, must be :');
46 | }
47 | }
48 | toString() {
49 | return `${key_type_to_str(this.keyType)}:${serialize_1.base_encode(this.data)}`;
50 | }
51 | }
52 | exports.PublicKey = PublicKey;
53 | class KeyPair {
54 | /**
55 | * @param curve Name of elliptical curve, case-insensitive
56 | * @returns Random KeyPair based on the curve
57 | */
58 | static fromRandom(curve) {
59 | switch (curve.toUpperCase()) {
60 | case 'ED25519': return KeyPairEd25519.fromRandom();
61 | default: throw new Error(`Unknown curve ${curve}`);
62 | }
63 | }
64 | static fromString(encodedKey) {
65 | const parts = encodedKey.split(':');
66 | if (parts.length === 1) {
67 | return new KeyPairEd25519(parts[0]);
68 | }
69 | else if (parts.length === 2) {
70 | switch (parts[0].toUpperCase()) {
71 | case 'ED25519': return new KeyPairEd25519(parts[1]);
72 | default: throw new Error(`Unknown curve: ${parts[0]}`);
73 | }
74 | }
75 | else {
76 | throw new Error('Invalid encoded key format, must be :');
77 | }
78 | }
79 | }
80 | exports.KeyPair = KeyPair;
81 | /**
82 | * This class provides key pair functionality for Ed25519 curve:
83 | * generating key pairs, encoding key pairs, signing and verifying.
84 | */
85 | class KeyPairEd25519 extends KeyPair {
86 | /**
87 | * Construct an instance of key pair given a secret key.
88 | * It's generally assumed that these are encoded in base58.
89 | * @param {string} secretKey
90 | */
91 | constructor(secretKey) {
92 | super();
93 | const keyPair = tweetnacl_1.default.sign.keyPair.fromSecretKey(serialize_1.base_decode(secretKey));
94 | this.publicKey = new PublicKey({ keyType: KeyType.ED25519, data: keyPair.publicKey });
95 | this.secretKey = secretKey;
96 | }
97 | /**
98 | * Generate a new random keypair.
99 | * @example
100 | * const keyRandom = KeyPair.fromRandom();
101 | * keyRandom.publicKey
102 | * // returns [PUBLIC_KEY]
103 | *
104 | * keyRandom.secretKey
105 | * // returns [SECRET_KEY]
106 | */
107 | static fromRandom() {
108 | const newKeyPair = tweetnacl_1.default.sign.keyPair();
109 | return new KeyPairEd25519(serialize_1.base_encode(newKeyPair.secretKey));
110 | }
111 | sign(message) {
112 | const signature = tweetnacl_1.default.sign.detached(message, serialize_1.base_decode(this.secretKey));
113 | return { signature, publicKey: this.publicKey };
114 | }
115 | verify(message, signature) {
116 | return tweetnacl_1.default.sign.detached.verify(message, signature, this.publicKey.data);
117 | }
118 | toString() {
119 | return `ed25519:${this.secretKey}`;
120 | }
121 | getPublicKey() {
122 | return this.publicKey;
123 | }
124 | }
125 | exports.KeyPairEd25519 = KeyPairEd25519;
126 |
--------------------------------------------------------------------------------
/borsh-ts/test/serialize.test.js:
--------------------------------------------------------------------------------
1 | const borsh = require('../../lib/index');
2 | const BN = require('bn.js');
3 |
4 | class Assignable {
5 | constructor(properties) {
6 | Object.keys(properties).map((key) => {
7 | this[key] = properties[key];
8 | });
9 | }
10 | }
11 |
12 | class Test extends Assignable { }
13 |
14 | class Serializable {
15 | constructor(data) {
16 | this.data = data;
17 | }
18 |
19 | static borshDeserialize(reader) {
20 | return new Serializable(reader.readU8());
21 | }
22 |
23 | borshSerialize(writer) {
24 | writer.writeU8(this.data);
25 | }
26 | }
27 |
28 | test('serialize object', async () => {
29 | const value = new Test({ x: 255, y: 20, z: '123', q: [1, 2, 3] });
30 | const schema = new Map([[Test, { kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] }]]);
31 | const buf = borsh.serialize(schema, value);
32 | const newValue = borsh.deserialize(schema, Test, buf);
33 | expect(newValue.x).toEqual(255);
34 | expect(newValue.y.toString()).toEqual('20');
35 | expect(newValue.z).toEqual('123');
36 | expect(newValue.q).toEqual(new Uint8Array([1, 2, 3]));
37 | });
38 |
39 | test('serialize optional field', async () => {
40 | const schema = new Map([[Test, { kind: 'struct', fields: [['x', { kind: 'option', type: 'string' }]]}]]);
41 |
42 | let buf = borsh.serialize(schema, new Test({ x: '123', }));
43 | let newValue = borsh.deserialize(schema, Test, buf);
44 | expect(newValue.x).toEqual('123');
45 |
46 | buf = borsh.serialize(schema, new Test({ }));
47 | newValue = borsh.deserialize(schema, Test, buf);
48 | expect(newValue.x).toEqual(undefined);
49 | });
50 |
51 | test('serialize max uint', async () => {
52 | const u64MaxHex = 'ffffffffffffffff';
53 | const value = new Test({
54 | x: 255,
55 | y: 65535,
56 | z: 4294967295,
57 | q: new BN(u64MaxHex, 16),
58 | r: new BN(u64MaxHex.repeat(2), 16),
59 | s: new BN(u64MaxHex.repeat(4), 16),
60 | t: new BN(u64MaxHex.repeat(8), 16)
61 | });
62 | const schema = new Map([[Test, {
63 | kind: 'struct',
64 | fields: [
65 | ['x', 'u8'],
66 | ['y', 'u16'],
67 | ['z', 'u32'],
68 | ['q', 'u64'],
69 | ['r', 'u128'],
70 | ['s', 'u256'],
71 | ['t', 'u512']
72 | ]
73 | }]]);
74 | const buf = borsh.serialize(schema, value);
75 | const newValue = borsh.deserialize(schema, Test, buf);
76 | expect(newValue.x).toEqual(255);
77 | expect(newValue.y).toEqual(65535);
78 | expect(newValue.z).toEqual(4294967295);
79 | expect(newValue.q.toString()).toEqual('18446744073709551615');
80 | expect(newValue.r.toString()).toEqual('340282366920938463463374607431768211455');
81 | expect(newValue.s.toString()).toEqual('115792089237316195423570985008687907853269984665640564039457584007913129639935');
82 | expect(newValue.t.toString()).toEqual('13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095');
83 | });
84 |
85 | test('serialize/deserialize with class methods', () => {
86 | const item = new Serializable(10);
87 |
88 | const buf = borsh.serialize(null, item);
89 | const newValue = borsh.deserialize(null, Serializable, buf);
90 |
91 | expect(newValue).toEqual(item);
92 | });
93 |
94 | test('serialize/deserialize fixed array', () => {
95 | const value = new Test({
96 | a: ['hello', 'world']
97 | });
98 | const schema = new Map([[Test, {
99 | kind: 'struct',
100 | fields: [
101 | ['a', ['string', 2]]
102 | ]
103 | }]]);
104 |
105 | const buf = borsh.serialize(schema, value);
106 | const deserializedValue = borsh.deserialize(schema, Test, buf);
107 |
108 | expect(buf).toEqual(Buffer.from([5, 0, 0, 0, 104, 101, 108, 108, 111, 5, 0, 0, 0, 119, 111, 114, 108, 100]));
109 | expect(deserializedValue.a).toEqual(['hello', 'world']);
110 | });
111 |
112 | test('errors serializing fixed array of wrong size', () => {
113 | const value = new Test({
114 | a: ['hello', 'world', 'you']
115 | });
116 | const schema = new Map([[Test, {
117 | kind: 'struct',
118 | fields: [
119 | ['a', ['string', 2]]
120 | ]
121 | }]]);
122 |
123 | expect(() => borsh.serialize(schema, value)).toThrow('Expecting byte array of length 2, but got 3 bytes');
124 | });
125 |
126 | test('errors serializing fixed array of wrong type', () => {
127 | const value = new Test({
128 | a: [244, 34]
129 | });
130 | const schema = new Map([[Test, {
131 | kind: 'struct',
132 | fields: [
133 | ['a', ['string', 2]]
134 | ]
135 | }]]);
136 |
137 | expect(() => borsh.serialize(schema, value)).toThrow('The first argument must be of type string');
138 | });
139 |
140 | test('baseEncode string test', async () => {
141 | const encodedValue = borsh.baseEncode('244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM');
142 | const expectedValue = 'HKk9gqNj4xb4rLdJuzT5zzJbLa4vHBdYCxQT9H99csQh6nz3Hfpqn4jtWA92';
143 | expect(encodedValue).toEqual(expectedValue);
144 | });
145 |
146 | test('baseEncode array test', async () => {
147 | expect(borsh.baseEncode([1, 2, 3, 4, 5])).toEqual('7bWpTW');
148 | });
149 |
150 | test('baseDecode test', async () => {
151 | const value = 'HKk9gqNj4xb4rLdJu';
152 | const expectedDecodedArray = [3, 96, 254, 84, 10, 240, 93, 199, 52, 244, 164, 240, 6];
153 | const expectedBuffer = Buffer.from(expectedDecodedArray);
154 | expect(borsh.baseDecode(value)).toEqual(expectedBuffer);
155 | });
156 |
157 | test('base encode and decode test', async () => {
158 | const value = '244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM';
159 | expect(borsh.baseEncode(borsh.baseDecode(value))).toEqual(value);
160 | });
161 |
162 | test('serialize with custom writer/reader', async () => {
163 | class ExtendedWriter extends borsh.BinaryWriter {
164 | writeDate(value) {
165 | this.writeU64(value.getTime());
166 | }
167 | }
168 |
169 | class ExtendedReader extends borsh.BinaryReader {
170 | readDate() {
171 | const value = this.readU64();
172 | return new Date(value.toNumber());
173 | }
174 | }
175 |
176 | const time = 'Aug 12, 2021 12:00:00 UTC+00:00';
177 | const value = new Test({ x: new Date(time) });
178 | const schema = new Map([[Test, {kind: 'struct', fields: [['x', 'date']]}]]);
179 |
180 | const buf = borsh.serialize(schema, value, ExtendedWriter);
181 | const newValue = borsh.deserialize(schema, Test, buf, ExtendedReader);
182 | expect(newValue.x).toEqual(new Date(time));
183 | });
184 |
185 | test('serialize map', async () => {
186 | let map = new Map();
187 | for (let i = 0; i < 10; i++) {
188 | map.set(new BN(i * 10), "some string " + i.toString());
189 | }
190 | const value = new Test({ x: map });
191 | const schema = new Map([[ Test, {
192 | kind: 'struct',
193 | fields: [
194 | ['x', { kind: 'map', key: 'u64', value: 'string' }],
195 | ],
196 | }]]);
197 |
198 | const buf = borsh.serialize(schema, value);
199 | const deserialized = borsh.deserialize(schema, Test, buf);
200 | expect(deserialized.x.size).toEqual(10);
201 | deserialized.x.forEach((value, key) => {
202 | expect(value).toEqual("some string " + (key.toNumber() / 10).toString());
203 | });
204 | });
205 |
--------------------------------------------------------------------------------
/borsh-ts/test/fuzz/transaction-example/transaction.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 | Object.defineProperty(exports, "__esModule", { value: true });
6 | const js_sha256_1 = __importDefault(require("js-sha256"));
7 | const enums_1 = require("./enums");
8 | const serialize_1 = require("./serialize");
9 | const key_pair_1 = require("./key_pair");
10 | class FunctionCallPermission extends enums_1.Assignable {
11 | }
12 | exports.FunctionCallPermission = FunctionCallPermission;
13 | class FullAccessPermission extends enums_1.Assignable {
14 | }
15 | exports.FullAccessPermission = FullAccessPermission;
16 | class AccessKeyPermission extends enums_1.Enum {
17 | }
18 | exports.AccessKeyPermission = AccessKeyPermission;
19 | class AccessKey extends enums_1.Assignable {
20 | }
21 | exports.AccessKey = AccessKey;
22 | function fullAccessKey() {
23 | return new AccessKey({ nonce: 0, permission: new AccessKeyPermission({ fullAccess: new FullAccessPermission({}) }) });
24 | }
25 | exports.fullAccessKey = fullAccessKey;
26 | function functionCallAccessKey(receiverId, methodNames, allowance) {
27 | return new AccessKey({ nonce: 0, permission: new AccessKeyPermission({ functionCall: new FunctionCallPermission({ receiverId, allowance, methodNames }) }) });
28 | }
29 | exports.functionCallAccessKey = functionCallAccessKey;
30 | class IAction extends enums_1.Assignable {
31 | }
32 | exports.IAction = IAction;
33 | class CreateAccount extends IAction {
34 | }
35 | exports.CreateAccount = CreateAccount;
36 | class DeployContract extends IAction {
37 | }
38 | exports.DeployContract = DeployContract;
39 | class FunctionCall extends IAction {
40 | }
41 | exports.FunctionCall = FunctionCall;
42 | class Transfer extends IAction {
43 | }
44 | exports.Transfer = Transfer;
45 | class Stake extends IAction {
46 | }
47 | exports.Stake = Stake;
48 | class AddKey extends IAction {
49 | }
50 | exports.AddKey = AddKey;
51 | class DeleteKey extends IAction {
52 | }
53 | exports.DeleteKey = DeleteKey;
54 | class DeleteAccount extends IAction {
55 | }
56 | exports.DeleteAccount = DeleteAccount;
57 | function createAccount() {
58 | return new Action({ createAccount: new CreateAccount({}) });
59 | }
60 | exports.createAccount = createAccount;
61 | function deployContract(code) {
62 | return new Action({ deployContract: new DeployContract({ code }) });
63 | }
64 | exports.deployContract = deployContract;
65 | /**
66 | * Constructs {@link Action} instance representing contract method call.
67 | *
68 | * @param methodName the name of the method to call
69 | * @param args arguments to pass to method. Can be either plain JS object which gets serialized as JSON automatically
70 | * or `Uint8Array` instance which represents bytes passed as is.
71 | * @param gas max amount of gas that method call can use
72 | * @param deposit amount of NEAR (in yoctoNEAR) to send together with the call
73 | */
74 | function functionCall(methodName, args, gas, deposit) {
75 | const anyArgs = args;
76 | const isUint8Array = anyArgs.byteLength !== undefined && anyArgs.byteLength === anyArgs.length;
77 | const serializedArgs = isUint8Array ? args : Buffer.from(JSON.stringify(args));
78 | return new Action({ functionCall: new FunctionCall({ methodName, args: serializedArgs, gas, deposit }) });
79 | }
80 | exports.functionCall = functionCall;
81 | function transfer(deposit) {
82 | return new Action({ transfer: new Transfer({ deposit }) });
83 | }
84 | exports.transfer = transfer;
85 | function stake(stake, publicKey) {
86 | return new Action({ stake: new Stake({ stake, publicKey }) });
87 | }
88 | exports.stake = stake;
89 | function addKey(publicKey, accessKey) {
90 | return new Action({ addKey: new AddKey({ publicKey, accessKey }) });
91 | }
92 | exports.addKey = addKey;
93 | function deleteKey(publicKey) {
94 | return new Action({ deleteKey: new DeleteKey({ publicKey }) });
95 | }
96 | exports.deleteKey = deleteKey;
97 | function deleteAccount(beneficiaryId) {
98 | return new Action({ deleteAccount: new DeleteAccount({ beneficiaryId }) });
99 | }
100 | exports.deleteAccount = deleteAccount;
101 | class Signature extends enums_1.Assignable {
102 | }
103 | exports.Signature = Signature;
104 | class Transaction extends enums_1.Assignable {
105 | encode() {
106 | return serialize_1.serialize(exports.SCHEMA, this);
107 | }
108 | static decode(bytes) {
109 | return serialize_1.deserialize(exports.SCHEMA, Transaction, bytes);
110 | }
111 | }
112 | exports.Transaction = Transaction;
113 | class SignedTransaction extends enums_1.Assignable {
114 | encode() {
115 | return serialize_1.serialize(exports.SCHEMA, this);
116 | }
117 | static decode(bytes) {
118 | return serialize_1.deserialize(exports.SCHEMA, SignedTransaction, bytes);
119 | }
120 | }
121 | exports.SignedTransaction = SignedTransaction;
122 | /**
123 | * Contains a list of the valid transaction Actions available with this API
124 | */
125 | class Action extends enums_1.Enum {
126 | }
127 | exports.Action = Action;
128 | exports.SCHEMA = new Map([
129 | [Signature, { kind: 'struct', fields: [
130 | ['keyType', 'u8'],
131 | ['data', [64]]
132 | ] }],
133 | [SignedTransaction, { kind: 'struct', fields: [
134 | ['transaction', Transaction],
135 | ['signature', Signature]
136 | ] }],
137 | [Transaction, { kind: 'struct', fields: [
138 | ['signerId', 'string'],
139 | ['publicKey', key_pair_1.PublicKey],
140 | ['nonce', 'u64'],
141 | ['receiverId', 'string'],
142 | ['blockHash', [32]],
143 | ['actions', [Action]]
144 | ] }],
145 | [key_pair_1.PublicKey, { kind: 'struct', fields: [
146 | ['keyType', 'u8'],
147 | ['data', [32]]
148 | ] }],
149 | [AccessKey, { kind: 'struct', fields: [
150 | ['nonce', 'u64'],
151 | ['permission', AccessKeyPermission],
152 | ] }],
153 | [AccessKeyPermission, { kind: 'enum', field: 'enum', values: [
154 | ['functionCall', FunctionCallPermission],
155 | ['fullAccess', FullAccessPermission],
156 | ] }],
157 | [FunctionCallPermission, { kind: 'struct', fields: [
158 | ['allowance', { kind: 'option', type: 'u128' }],
159 | ['receiverId', 'string'],
160 | ['methodNames', ['string']],
161 | ] }],
162 | [FullAccessPermission, { kind: 'struct', fields: [] }],
163 | [Action, { kind: 'enum', field: 'enum', values: [
164 | ['createAccount', CreateAccount],
165 | ['deployContract', DeployContract],
166 | ['functionCall', FunctionCall],
167 | ['transfer', Transfer],
168 | ['stake', Stake],
169 | ['addKey', AddKey],
170 | ['deleteKey', DeleteKey],
171 | ['deleteAccount', DeleteAccount],
172 | ] }],
173 | [CreateAccount, { kind: 'struct', fields: [] }],
174 | [DeployContract, { kind: 'struct', fields: [
175 | ['code', ['u8']]
176 | ] }],
177 | [FunctionCall, { kind: 'struct', fields: [
178 | ['methodName', 'string'],
179 | ['args', ['u8']],
180 | ['gas', 'u64'],
181 | ['deposit', 'u128']
182 | ] }],
183 | [Transfer, { kind: 'struct', fields: [
184 | ['deposit', 'u128']
185 | ] }],
186 | [Stake, { kind: 'struct', fields: [
187 | ['stake', 'u128'],
188 | ['publicKey', key_pair_1.PublicKey]
189 | ] }],
190 | [AddKey, { kind: 'struct', fields: [
191 | ['publicKey', key_pair_1.PublicKey],
192 | ['accessKey', AccessKey]
193 | ] }],
194 | [DeleteKey, { kind: 'struct', fields: [
195 | ['publicKey', key_pair_1.PublicKey]
196 | ] }],
197 | [DeleteAccount, { kind: 'struct', fields: [
198 | ['beneficiaryId', 'string']
199 | ] }],
200 | ]);
201 | function createTransaction(signerId, publicKey, receiverId, nonce, actions, blockHash) {
202 | return new Transaction({ signerId, publicKey, nonce, receiverId, actions, blockHash });
203 | }
204 | exports.createTransaction = createTransaction;
205 | /**
206 | * Signs a given transaction from an account with given keys, applied to the given network
207 | * @param transaction The Transaction object to sign
208 | * @param signer The {Signer} object that assists with signing keys
209 | * @param accountId The human-readable NEAR account name
210 | * @param networkId The targeted network. (ex. default, betanet, etc…)
211 | */
212 | async function signTransactionObject(transaction, signer, accountId, networkId) {
213 | const message = serialize_1.serialize(exports.SCHEMA, transaction);
214 | const hash = new Uint8Array(js_sha256_1.default.sha256.array(message));
215 | const signature = await signer.signMessage(message, accountId, networkId);
216 | const signedTx = new SignedTransaction({
217 | transaction,
218 | signature: new Signature({ keyType: transaction.publicKey.keyType, data: signature.signature })
219 | });
220 | return [hash, signedTx];
221 | }
222 | async function signTransaction(...args) {
223 | if (args[0].constructor === Transaction) {
224 | const [transaction, signer, accountId, networkId] = args;
225 | return signTransactionObject(transaction, signer, accountId, networkId);
226 | }
227 | else {
228 | const [receiverId, nonce, actions, blockHash, signer, accountId, networkId] = args;
229 | const publicKey = await signer.getPublicKey(accountId, networkId);
230 | const transaction = createTransaction(accountId, publicKey, receiverId, nonce, actions, blockHash);
231 | return signTransactionObject(transaction, signer, accountId, networkId);
232 | }
233 | }
234 | exports.signTransaction = signTransaction;
235 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2019 Near
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/borsh-ts/index.ts:
--------------------------------------------------------------------------------
1 | import BN from "bn.js";
2 | import bs58 from "bs58";
3 |
4 | // TODO: Make sure this polyfill not included when not required
5 | import * as encoding from "text-encoding-utf-8";
6 | const ResolvedTextDecoder =
7 | typeof TextDecoder !== "function" ? encoding.TextDecoder : TextDecoder;
8 | const textDecoder = new ResolvedTextDecoder("utf-8", { fatal: true });
9 |
10 | export function baseEncode(value: Uint8Array | string): string {
11 | if (typeof value === "string") {
12 | value = Buffer.from(value, "utf8");
13 | }
14 | return bs58.encode(Buffer.from(value));
15 | }
16 |
17 | export function baseDecode(value: string): Buffer {
18 | return Buffer.from(bs58.decode(value));
19 | }
20 |
21 | const INITIAL_LENGTH = 1024;
22 |
23 | export type Schema = Map;
24 |
25 | export class BorshError extends Error {
26 | originalMessage: string;
27 | fieldPath: string[] = [];
28 |
29 | constructor(message: string) {
30 | super(message);
31 | this.originalMessage = message;
32 | }
33 |
34 | addToFieldPath(fieldName: string) {
35 | this.fieldPath.splice(0, 0, fieldName);
36 | // NOTE: Modifying message directly as jest doesn't use .toString()
37 | this.message = this.originalMessage + ": " + this.fieldPath.join(".");
38 | }
39 | }
40 |
41 | /// Binary encoder.
42 | export class BinaryWriter {
43 | buf: Buffer;
44 | length: number;
45 |
46 | public constructor() {
47 | this.buf = Buffer.alloc(INITIAL_LENGTH);
48 | this.length = 0;
49 | }
50 |
51 | maybeResize() {
52 | if (this.buf.length < 16 + this.length) {
53 | this.buf = Buffer.concat([this.buf, Buffer.alloc(INITIAL_LENGTH)]);
54 | }
55 | }
56 |
57 | public writeU8(value: number) {
58 | this.maybeResize();
59 | this.buf.writeUInt8(value, this.length);
60 | this.length += 1;
61 | }
62 |
63 | public writeU16(value: number) {
64 | this.maybeResize();
65 | this.buf.writeUInt16LE(value, this.length);
66 | this.length += 2;
67 | }
68 |
69 | public writeU32(value: number) {
70 | this.maybeResize();
71 | this.buf.writeUInt32LE(value, this.length);
72 | this.length += 4;
73 | }
74 |
75 | public writeU64(value: number | BN) {
76 | this.maybeResize();
77 | this.writeBuffer(Buffer.from(new BN(value).toArray("le", 8)));
78 | }
79 |
80 | public writeU128(value: number | BN) {
81 | this.maybeResize();
82 | this.writeBuffer(Buffer.from(new BN(value).toArray("le", 16)));
83 | }
84 |
85 | public writeU256(value: number | BN) {
86 | this.maybeResize();
87 | this.writeBuffer(Buffer.from(new BN(value).toArray("le", 32)));
88 | }
89 |
90 | public writeU512(value: number | BN) {
91 | this.maybeResize();
92 | this.writeBuffer(Buffer.from(new BN(value).toArray("le", 64)));
93 | }
94 |
95 | private writeBuffer(buffer: Buffer) {
96 | // Buffer.from is needed as this.buf.subarray can return plain Uint8Array in browser
97 | this.buf = Buffer.concat([
98 | Buffer.from(this.buf.subarray(0, this.length)),
99 | buffer,
100 | Buffer.alloc(INITIAL_LENGTH),
101 | ]);
102 | this.length += buffer.length;
103 | }
104 |
105 | public writeString(str: string) {
106 | this.maybeResize();
107 | const b = Buffer.from(str, "utf8");
108 | this.writeU32(b.length);
109 | this.writeBuffer(b);
110 | }
111 |
112 | public writeFixedArray(array: Uint8Array) {
113 | this.writeBuffer(Buffer.from(array));
114 | }
115 |
116 | public writeArray(array: any[], fn: any) {
117 | this.maybeResize();
118 | this.writeU32(array.length);
119 | for (const elem of array) {
120 | this.maybeResize();
121 | fn(elem);
122 | }
123 | }
124 |
125 | public toArray(): Uint8Array {
126 | return this.buf.subarray(0, this.length);
127 | }
128 | }
129 |
130 | function handlingRangeError(
131 | target: any,
132 | propertyKey: string,
133 | propertyDescriptor: PropertyDescriptor
134 | ) {
135 | const originalMethod = propertyDescriptor.value;
136 | propertyDescriptor.value = function (...args: any[]) {
137 | try {
138 | return originalMethod.apply(this, args);
139 | } catch (e) {
140 | if (e instanceof RangeError) {
141 | const code = (e as any).code;
142 | if (
143 | ["ERR_BUFFER_OUT_OF_BOUNDS", "ERR_OUT_OF_RANGE"].indexOf(code) >= 0
144 | ) {
145 | throw new BorshError("Reached the end of buffer when deserializing");
146 | }
147 | }
148 | throw e;
149 | }
150 | };
151 | }
152 |
153 | export class BinaryReader {
154 | buf: Buffer;
155 | offset: number;
156 |
157 | public constructor(buf: Buffer) {
158 | this.buf = buf;
159 | this.offset = 0;
160 | }
161 |
162 | @handlingRangeError
163 | readU8(): number {
164 | const value = this.buf.readUInt8(this.offset);
165 | this.offset += 1;
166 | return value;
167 | }
168 |
169 | @handlingRangeError
170 | readU16(): number {
171 | const value = this.buf.readUInt16LE(this.offset);
172 | this.offset += 2;
173 | return value;
174 | }
175 |
176 | @handlingRangeError
177 | readU32(): number {
178 | const value = this.buf.readUInt32LE(this.offset);
179 | this.offset += 4;
180 | return value;
181 | }
182 |
183 | @handlingRangeError
184 | readU64(): BN {
185 | const buf = this.readBuffer(8);
186 | return new BN(buf, "le");
187 | }
188 |
189 | @handlingRangeError
190 | readU128(): BN {
191 | const buf = this.readBuffer(16);
192 | return new BN(buf, "le");
193 | }
194 |
195 | @handlingRangeError
196 | readU256(): BN {
197 | const buf = this.readBuffer(32);
198 | return new BN(buf, "le");
199 | }
200 |
201 | @handlingRangeError
202 | readU512(): BN {
203 | const buf = this.readBuffer(64);
204 | return new BN(buf, "le");
205 | }
206 |
207 | private readBuffer(len: number): Buffer {
208 | if (this.offset + len > this.buf.length) {
209 | throw new BorshError(`Expected buffer length ${len} isn't within bounds`);
210 | }
211 | const result = this.buf.slice(this.offset, this.offset + len);
212 | this.offset += len;
213 | return result;
214 | }
215 |
216 | @handlingRangeError
217 | readString(): string {
218 | const len = this.readU32();
219 | const buf = this.readBuffer(len);
220 | try {
221 | // NOTE: Using TextDecoder to fail on invalid UTF-8
222 | return textDecoder.decode(buf);
223 | } catch (e) {
224 | throw new BorshError(`Error decoding UTF-8 string: ${e}`);
225 | }
226 | }
227 |
228 | @handlingRangeError
229 | readFixedArray(len: number): Uint8Array {
230 | return new Uint8Array(this.readBuffer(len));
231 | }
232 |
233 | @handlingRangeError
234 | readArray(fn: any): any[] {
235 | const len = this.readU32();
236 | const result = Array();
237 | for (let i = 0; i < len; ++i) {
238 | result.push(fn());
239 | }
240 | return result;
241 | }
242 | }
243 |
244 | function capitalizeFirstLetter(string) {
245 | return string.charAt(0).toUpperCase() + string.slice(1);
246 | }
247 |
248 | function serializeField(
249 | schema: Schema,
250 | fieldName: string,
251 | value: any,
252 | fieldType: any,
253 | writer: any
254 | ) {
255 | try {
256 | // TODO: Handle missing values properly (make sure they never result in just skipped write)
257 | if (typeof fieldType === "string") {
258 | writer[`write${capitalizeFirstLetter(fieldType)}`](value);
259 | } else if (fieldType instanceof Array) {
260 | if (typeof fieldType[0] === "number") {
261 | if (value.length !== fieldType[0]) {
262 | throw new BorshError(
263 | `Expecting byte array of length ${fieldType[0]}, but got ${value.length} bytes`
264 | );
265 | }
266 | writer.writeFixedArray(value);
267 | } else if (fieldType.length === 2 && typeof fieldType[1] === "number") {
268 | if (value.length !== fieldType[1]) {
269 | throw new BorshError(
270 | `Expecting byte array of length ${fieldType[1]}, but got ${value.length} bytes`
271 | );
272 | }
273 | for (let i = 0; i < fieldType[1]; i++) {
274 | serializeField(schema, null, value[i], fieldType[0], writer);
275 | }
276 | } else {
277 | writer.writeArray(value, (item: any) => {
278 | serializeField(schema, fieldName, item, fieldType[0], writer);
279 | });
280 | }
281 | } else if (fieldType.kind !== undefined) {
282 | switch (fieldType.kind) {
283 | case "option": {
284 | if (value === null || value === undefined) {
285 | writer.writeU8(0);
286 | } else {
287 | writer.writeU8(1);
288 | serializeField(schema, fieldName, value, fieldType.type, writer);
289 | }
290 | break;
291 | }
292 | case "map": {
293 | writer.writeU32(value.size);
294 | value.forEach((val, key) => {
295 | serializeField(schema, fieldName, key, fieldType.key, writer);
296 | serializeField(schema, fieldName, val, fieldType.value, writer);
297 | });
298 | break;
299 | }
300 | default:
301 | throw new BorshError(`FieldType ${fieldType} unrecognized`);
302 | }
303 | } else {
304 | serializeStruct(schema, value, writer);
305 | }
306 | } catch (error) {
307 | if (error instanceof BorshError) {
308 | error.addToFieldPath(fieldName);
309 | }
310 | throw error;
311 | }
312 | }
313 |
314 | function serializeStruct(schema: Schema, obj: any, writer: BinaryWriter) {
315 | if (typeof obj.borshSerialize === "function") {
316 | obj.borshSerialize(writer);
317 | return;
318 | }
319 |
320 | const structSchema = schema.get(obj.constructor);
321 | if (!structSchema) {
322 | throw new BorshError(`Class ${obj.constructor.name} is missing in schema`);
323 | }
324 |
325 | if (structSchema.kind === "struct") {
326 | structSchema.fields.map(([fieldName, fieldType]: [any, any]) => {
327 | serializeField(schema, fieldName, obj[fieldName], fieldType, writer);
328 | });
329 | } else if (structSchema.kind === "enum") {
330 | const name = obj[structSchema.field];
331 | for (let idx = 0; idx < structSchema.values.length; ++idx) {
332 | const [fieldName, fieldType]: [any, any] = structSchema.values[idx];
333 | if (fieldName === name) {
334 | writer.writeU8(idx);
335 | serializeField(schema, fieldName, obj[fieldName], fieldType, writer);
336 | break;
337 | }
338 | }
339 | } else {
340 | throw new BorshError(
341 | `Unexpected schema kind: ${structSchema.kind} for ${obj.constructor.name}`
342 | );
343 | }
344 | }
345 |
346 | /// Serialize given object using schema of the form:
347 | /// { class_name -> [ [field_name, field_type], .. ], .. }
348 | export function serialize(
349 | schema: Schema,
350 | obj: any,
351 | Writer = BinaryWriter
352 | ): Uint8Array {
353 | const writer = new Writer();
354 | serializeStruct(schema, obj, writer);
355 | return writer.toArray();
356 | }
357 |
358 | function deserializeField(
359 | schema: Schema,
360 | fieldName: string,
361 | fieldType: any,
362 | reader: BinaryReader
363 | ): any {
364 | try {
365 | if (typeof fieldType === "string") {
366 | return reader[`read${capitalizeFirstLetter(fieldType)}`]();
367 | }
368 |
369 | if (fieldType instanceof Array) {
370 | if (typeof fieldType[0] === "number") {
371 | return reader.readFixedArray(fieldType[0]);
372 | } else if (typeof fieldType[1] === "number") {
373 | const arr = [];
374 | for (let i = 0; i < fieldType[1]; i++) {
375 | arr.push(deserializeField(schema, null, fieldType[0], reader));
376 | }
377 | return arr;
378 | } else {
379 | return reader.readArray(() =>
380 | deserializeField(schema, fieldName, fieldType[0], reader)
381 | );
382 | }
383 | }
384 |
385 | if (fieldType.kind === "option") {
386 | const option = reader.readU8();
387 | if (option) {
388 | return deserializeField(schema, fieldName, fieldType.type, reader);
389 | }
390 |
391 | return undefined;
392 | }
393 | if (fieldType.kind === "map") {
394 | let map = new Map();
395 | const length = reader.readU32();
396 | for (let i = 0; i < length; i++) {
397 | const key = deserializeField(schema, fieldName, fieldType.key, reader);
398 | const val = deserializeField(
399 | schema,
400 | fieldName,
401 | fieldType.value,
402 | reader
403 | );
404 | map.set(key, val);
405 | }
406 | return map;
407 | }
408 |
409 | return deserializeStruct(schema, fieldType, reader);
410 | } catch (error) {
411 | if (error instanceof BorshError) {
412 | error.addToFieldPath(fieldName);
413 | }
414 | throw error;
415 | }
416 | }
417 |
418 | function deserializeStruct(
419 | schema: Schema,
420 | classType: any,
421 | reader: BinaryReader
422 | ) {
423 | if (typeof classType.borshDeserialize === "function") {
424 | return classType.borshDeserialize(reader);
425 | }
426 |
427 | const structSchema = schema.get(classType);
428 | if (!structSchema) {
429 | throw new BorshError(`Class ${classType.name} is missing in schema`);
430 | }
431 |
432 | if (structSchema.kind === "struct") {
433 | const result = {};
434 | for (const [fieldName, fieldType] of schema.get(classType).fields) {
435 | result[fieldName] = deserializeField(
436 | schema,
437 | fieldName,
438 | fieldType,
439 | reader
440 | );
441 | }
442 | return new classType(result);
443 | }
444 |
445 | if (structSchema.kind === "enum") {
446 | const idx = reader.readU8();
447 | if (idx >= structSchema.values.length) {
448 | throw new BorshError(`Enum index: ${idx} is out of range`);
449 | }
450 | const [fieldName, fieldType] = structSchema.values[idx];
451 | const fieldValue = deserializeField(schema, fieldName, fieldType, reader);
452 | return new classType({ [fieldName]: fieldValue });
453 | }
454 |
455 | throw new BorshError(
456 | `Unexpected schema kind: ${structSchema.kind} for ${classType.constructor.name}`
457 | );
458 | }
459 |
460 | /// Deserializes object from bytes using schema.
461 | export function deserialize(
462 | schema: Schema,
463 | classType: { new (args: any): T },
464 | buffer: Buffer,
465 | Reader = BinaryReader
466 | ): T {
467 | const reader = new Reader(buffer);
468 | const result = deserializeStruct(schema, classType, reader);
469 | if (reader.offset < buffer.length) {
470 | throw new BorshError(
471 | `Unexpected ${
472 | buffer.length - reader.offset
473 | } bytes after deserialized data`
474 | );
475 | }
476 | return result;
477 | }
478 |
479 | /// Deserializes object from bytes using schema, without checking the length read
480 | export function deserializeUnchecked(
481 | schema: Schema,
482 | classType: { new (args: any): T },
483 | buffer: Buffer,
484 | Reader = BinaryReader
485 | ): T {
486 | const reader = new Reader(buffer);
487 | return deserializeStruct(schema, classType, reader);
488 | }
489 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5 | }) : (function(o, m, k, k2) {
6 | if (k2 === undefined) k2 = k;
7 | o[k2] = m[k];
8 | }));
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
11 | }) : function(o, v) {
12 | o["default"] = v;
13 | });
14 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
15 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18 | return c > 3 && r && Object.defineProperty(target, key, r), r;
19 | };
20 | var __importStar = (this && this.__importStar) || function (mod) {
21 | if (mod && mod.__esModule) return mod;
22 | var result = {};
23 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 | __setModuleDefault(result, mod);
25 | return result;
26 | };
27 | var __importDefault = (this && this.__importDefault) || function (mod) {
28 | return (mod && mod.__esModule) ? mod : { "default": mod };
29 | };
30 | Object.defineProperty(exports, "__esModule", { value: true });
31 | exports.deserializeUnchecked = exports.deserialize = exports.serialize = exports.BinaryReader = exports.BinaryWriter = exports.BorshError = exports.baseDecode = exports.baseEncode = void 0;
32 | const bn_js_1 = __importDefault(require("bn.js"));
33 | const bs58_1 = __importDefault(require("bs58"));
34 | // TODO: Make sure this polyfill not included when not required
35 | const encoding = __importStar(require("text-encoding-utf-8"));
36 | const ResolvedTextDecoder = typeof TextDecoder !== "function" ? encoding.TextDecoder : TextDecoder;
37 | const textDecoder = new ResolvedTextDecoder("utf-8", { fatal: true });
38 | function baseEncode(value) {
39 | if (typeof value === "string") {
40 | value = Buffer.from(value, "utf8");
41 | }
42 | return bs58_1.default.encode(Buffer.from(value));
43 | }
44 | exports.baseEncode = baseEncode;
45 | function baseDecode(value) {
46 | return Buffer.from(bs58_1.default.decode(value));
47 | }
48 | exports.baseDecode = baseDecode;
49 | const INITIAL_LENGTH = 1024;
50 | class BorshError extends Error {
51 | constructor(message) {
52 | super(message);
53 | this.fieldPath = [];
54 | this.originalMessage = message;
55 | }
56 | addToFieldPath(fieldName) {
57 | this.fieldPath.splice(0, 0, fieldName);
58 | // NOTE: Modifying message directly as jest doesn't use .toString()
59 | this.message = this.originalMessage + ": " + this.fieldPath.join(".");
60 | }
61 | }
62 | exports.BorshError = BorshError;
63 | /// Binary encoder.
64 | class BinaryWriter {
65 | constructor() {
66 | this.buf = Buffer.alloc(INITIAL_LENGTH);
67 | this.length = 0;
68 | }
69 | maybeResize() {
70 | if (this.buf.length < 16 + this.length) {
71 | this.buf = Buffer.concat([this.buf, Buffer.alloc(INITIAL_LENGTH)]);
72 | }
73 | }
74 | writeU8(value) {
75 | this.maybeResize();
76 | this.buf.writeUInt8(value, this.length);
77 | this.length += 1;
78 | }
79 | writeU16(value) {
80 | this.maybeResize();
81 | this.buf.writeUInt16LE(value, this.length);
82 | this.length += 2;
83 | }
84 | writeU32(value) {
85 | this.maybeResize();
86 | this.buf.writeUInt32LE(value, this.length);
87 | this.length += 4;
88 | }
89 | writeU64(value) {
90 | this.maybeResize();
91 | this.writeBuffer(Buffer.from(new bn_js_1.default(value).toArray("le", 8)));
92 | }
93 | writeU128(value) {
94 | this.maybeResize();
95 | this.writeBuffer(Buffer.from(new bn_js_1.default(value).toArray("le", 16)));
96 | }
97 | writeU256(value) {
98 | this.maybeResize();
99 | this.writeBuffer(Buffer.from(new bn_js_1.default(value).toArray("le", 32)));
100 | }
101 | writeU512(value) {
102 | this.maybeResize();
103 | this.writeBuffer(Buffer.from(new bn_js_1.default(value).toArray("le", 64)));
104 | }
105 | writeBuffer(buffer) {
106 | // Buffer.from is needed as this.buf.subarray can return plain Uint8Array in browser
107 | this.buf = Buffer.concat([
108 | Buffer.from(this.buf.subarray(0, this.length)),
109 | buffer,
110 | Buffer.alloc(INITIAL_LENGTH),
111 | ]);
112 | this.length += buffer.length;
113 | }
114 | writeString(str) {
115 | this.maybeResize();
116 | const b = Buffer.from(str, "utf8");
117 | this.writeU32(b.length);
118 | this.writeBuffer(b);
119 | }
120 | writeFixedArray(array) {
121 | this.writeBuffer(Buffer.from(array));
122 | }
123 | writeArray(array, fn) {
124 | this.maybeResize();
125 | this.writeU32(array.length);
126 | for (const elem of array) {
127 | this.maybeResize();
128 | fn(elem);
129 | }
130 | }
131 | toArray() {
132 | return this.buf.subarray(0, this.length);
133 | }
134 | }
135 | exports.BinaryWriter = BinaryWriter;
136 | function handlingRangeError(target, propertyKey, propertyDescriptor) {
137 | const originalMethod = propertyDescriptor.value;
138 | propertyDescriptor.value = function (...args) {
139 | try {
140 | return originalMethod.apply(this, args);
141 | }
142 | catch (e) {
143 | if (e instanceof RangeError) {
144 | const code = e.code;
145 | if (["ERR_BUFFER_OUT_OF_BOUNDS", "ERR_OUT_OF_RANGE"].indexOf(code) >= 0) {
146 | throw new BorshError("Reached the end of buffer when deserializing");
147 | }
148 | }
149 | throw e;
150 | }
151 | };
152 | }
153 | class BinaryReader {
154 | constructor(buf) {
155 | this.buf = buf;
156 | this.offset = 0;
157 | }
158 | readU8() {
159 | const value = this.buf.readUInt8(this.offset);
160 | this.offset += 1;
161 | return value;
162 | }
163 | readU16() {
164 | const value = this.buf.readUInt16LE(this.offset);
165 | this.offset += 2;
166 | return value;
167 | }
168 | readU32() {
169 | const value = this.buf.readUInt32LE(this.offset);
170 | this.offset += 4;
171 | return value;
172 | }
173 | readU64() {
174 | const buf = this.readBuffer(8);
175 | return new bn_js_1.default(buf, "le");
176 | }
177 | readU128() {
178 | const buf = this.readBuffer(16);
179 | return new bn_js_1.default(buf, "le");
180 | }
181 | readU256() {
182 | const buf = this.readBuffer(32);
183 | return new bn_js_1.default(buf, "le");
184 | }
185 | readU512() {
186 | const buf = this.readBuffer(64);
187 | return new bn_js_1.default(buf, "le");
188 | }
189 | readBuffer(len) {
190 | if (this.offset + len > this.buf.length) {
191 | throw new BorshError(`Expected buffer length ${len} isn't within bounds`);
192 | }
193 | const result = this.buf.slice(this.offset, this.offset + len);
194 | this.offset += len;
195 | return result;
196 | }
197 | readString() {
198 | const len = this.readU32();
199 | const buf = this.readBuffer(len);
200 | try {
201 | // NOTE: Using TextDecoder to fail on invalid UTF-8
202 | return textDecoder.decode(buf);
203 | }
204 | catch (e) {
205 | throw new BorshError(`Error decoding UTF-8 string: ${e}`);
206 | }
207 | }
208 | readFixedArray(len) {
209 | return new Uint8Array(this.readBuffer(len));
210 | }
211 | readArray(fn) {
212 | const len = this.readU32();
213 | const result = Array();
214 | for (let i = 0; i < len; ++i) {
215 | result.push(fn());
216 | }
217 | return result;
218 | }
219 | }
220 | __decorate([
221 | handlingRangeError
222 | ], BinaryReader.prototype, "readU8", null);
223 | __decorate([
224 | handlingRangeError
225 | ], BinaryReader.prototype, "readU16", null);
226 | __decorate([
227 | handlingRangeError
228 | ], BinaryReader.prototype, "readU32", null);
229 | __decorate([
230 | handlingRangeError
231 | ], BinaryReader.prototype, "readU64", null);
232 | __decorate([
233 | handlingRangeError
234 | ], BinaryReader.prototype, "readU128", null);
235 | __decorate([
236 | handlingRangeError
237 | ], BinaryReader.prototype, "readU256", null);
238 | __decorate([
239 | handlingRangeError
240 | ], BinaryReader.prototype, "readU512", null);
241 | __decorate([
242 | handlingRangeError
243 | ], BinaryReader.prototype, "readString", null);
244 | __decorate([
245 | handlingRangeError
246 | ], BinaryReader.prototype, "readFixedArray", null);
247 | __decorate([
248 | handlingRangeError
249 | ], BinaryReader.prototype, "readArray", null);
250 | exports.BinaryReader = BinaryReader;
251 | function capitalizeFirstLetter(string) {
252 | return string.charAt(0).toUpperCase() + string.slice(1);
253 | }
254 | function serializeField(schema, fieldName, value, fieldType, writer) {
255 | try {
256 | // TODO: Handle missing values properly (make sure they never result in just skipped write)
257 | if (typeof fieldType === "string") {
258 | writer[`write${capitalizeFirstLetter(fieldType)}`](value);
259 | }
260 | else if (fieldType instanceof Array) {
261 | if (typeof fieldType[0] === "number") {
262 | if (value.length !== fieldType[0]) {
263 | throw new BorshError(`Expecting byte array of length ${fieldType[0]}, but got ${value.length} bytes`);
264 | }
265 | writer.writeFixedArray(value);
266 | }
267 | else if (fieldType.length === 2 && typeof fieldType[1] === "number") {
268 | if (value.length !== fieldType[1]) {
269 | throw new BorshError(`Expecting byte array of length ${fieldType[1]}, but got ${value.length} bytes`);
270 | }
271 | for (let i = 0; i < fieldType[1]; i++) {
272 | serializeField(schema, null, value[i], fieldType[0], writer);
273 | }
274 | }
275 | else {
276 | writer.writeArray(value, (item) => {
277 | serializeField(schema, fieldName, item, fieldType[0], writer);
278 | });
279 | }
280 | }
281 | else if (fieldType.kind !== undefined) {
282 | switch (fieldType.kind) {
283 | case "option": {
284 | if (value === null || value === undefined) {
285 | writer.writeU8(0);
286 | }
287 | else {
288 | writer.writeU8(1);
289 | serializeField(schema, fieldName, value, fieldType.type, writer);
290 | }
291 | break;
292 | }
293 | case "map": {
294 | writer.writeU32(value.size);
295 | value.forEach((val, key) => {
296 | serializeField(schema, fieldName, key, fieldType.key, writer);
297 | serializeField(schema, fieldName, val, fieldType.value, writer);
298 | });
299 | break;
300 | }
301 | default:
302 | throw new BorshError(`FieldType ${fieldType} unrecognized`);
303 | }
304 | }
305 | else {
306 | serializeStruct(schema, value, writer);
307 | }
308 | }
309 | catch (error) {
310 | if (error instanceof BorshError) {
311 | error.addToFieldPath(fieldName);
312 | }
313 | throw error;
314 | }
315 | }
316 | function serializeStruct(schema, obj, writer) {
317 | if (typeof obj.borshSerialize === "function") {
318 | obj.borshSerialize(writer);
319 | return;
320 | }
321 | const structSchema = schema.get(obj.constructor);
322 | if (!structSchema) {
323 | throw new BorshError(`Class ${obj.constructor.name} is missing in schema`);
324 | }
325 | if (structSchema.kind === "struct") {
326 | structSchema.fields.map(([fieldName, fieldType]) => {
327 | serializeField(schema, fieldName, obj[fieldName], fieldType, writer);
328 | });
329 | }
330 | else if (structSchema.kind === "enum") {
331 | const name = obj[structSchema.field];
332 | for (let idx = 0; idx < structSchema.values.length; ++idx) {
333 | const [fieldName, fieldType] = structSchema.values[idx];
334 | if (fieldName === name) {
335 | writer.writeU8(idx);
336 | serializeField(schema, fieldName, obj[fieldName], fieldType, writer);
337 | break;
338 | }
339 | }
340 | }
341 | else {
342 | throw new BorshError(`Unexpected schema kind: ${structSchema.kind} for ${obj.constructor.name}`);
343 | }
344 | }
345 | /// Serialize given object using schema of the form:
346 | /// { class_name -> [ [field_name, field_type], .. ], .. }
347 | function serialize(schema, obj, Writer = BinaryWriter) {
348 | const writer = new Writer();
349 | serializeStruct(schema, obj, writer);
350 | return writer.toArray();
351 | }
352 | exports.serialize = serialize;
353 | function deserializeField(schema, fieldName, fieldType, reader) {
354 | try {
355 | if (typeof fieldType === "string") {
356 | return reader[`read${capitalizeFirstLetter(fieldType)}`]();
357 | }
358 | if (fieldType instanceof Array) {
359 | if (typeof fieldType[0] === "number") {
360 | return reader.readFixedArray(fieldType[0]);
361 | }
362 | else if (typeof fieldType[1] === "number") {
363 | const arr = [];
364 | for (let i = 0; i < fieldType[1]; i++) {
365 | arr.push(deserializeField(schema, null, fieldType[0], reader));
366 | }
367 | return arr;
368 | }
369 | else {
370 | return reader.readArray(() => deserializeField(schema, fieldName, fieldType[0], reader));
371 | }
372 | }
373 | if (fieldType.kind === "option") {
374 | const option = reader.readU8();
375 | if (option) {
376 | return deserializeField(schema, fieldName, fieldType.type, reader);
377 | }
378 | return undefined;
379 | }
380 | if (fieldType.kind === "map") {
381 | let map = new Map();
382 | const length = reader.readU32();
383 | for (let i = 0; i < length; i++) {
384 | const key = deserializeField(schema, fieldName, fieldType.key, reader);
385 | const val = deserializeField(schema, fieldName, fieldType.value, reader);
386 | map.set(key, val);
387 | }
388 | return map;
389 | }
390 | return deserializeStruct(schema, fieldType, reader);
391 | }
392 | catch (error) {
393 | if (error instanceof BorshError) {
394 | error.addToFieldPath(fieldName);
395 | }
396 | throw error;
397 | }
398 | }
399 | function deserializeStruct(schema, classType, reader) {
400 | if (typeof classType.borshDeserialize === "function") {
401 | return classType.borshDeserialize(reader);
402 | }
403 | const structSchema = schema.get(classType);
404 | if (!structSchema) {
405 | throw new BorshError(`Class ${classType.name} is missing in schema`);
406 | }
407 | if (structSchema.kind === "struct") {
408 | const result = {};
409 | for (const [fieldName, fieldType] of schema.get(classType).fields) {
410 | result[fieldName] = deserializeField(schema, fieldName, fieldType, reader);
411 | }
412 | return new classType(result);
413 | }
414 | if (structSchema.kind === "enum") {
415 | const idx = reader.readU8();
416 | if (idx >= structSchema.values.length) {
417 | throw new BorshError(`Enum index: ${idx} is out of range`);
418 | }
419 | const [fieldName, fieldType] = structSchema.values[idx];
420 | const fieldValue = deserializeField(schema, fieldName, fieldType, reader);
421 | return new classType({ [fieldName]: fieldValue });
422 | }
423 | throw new BorshError(`Unexpected schema kind: ${structSchema.kind} for ${classType.constructor.name}`);
424 | }
425 | /// Deserializes object from bytes using schema.
426 | function deserialize(schema, classType, buffer, Reader = BinaryReader) {
427 | const reader = new Reader(buffer);
428 | const result = deserializeStruct(schema, classType, reader);
429 | if (reader.offset < buffer.length) {
430 | throw new BorshError(`Unexpected ${buffer.length - reader.offset} bytes after deserialized data`);
431 | }
432 | return result;
433 | }
434 | exports.deserialize = deserialize;
435 | /// Deserializes object from bytes using schema, without checking the length read
436 | function deserializeUnchecked(schema, classType, buffer, Reader = BinaryReader) {
437 | const reader = new Reader(buffer);
438 | return deserializeStruct(schema, classType, reader);
439 | }
440 | exports.deserializeUnchecked = deserializeUnchecked;
441 |
--------------------------------------------------------------------------------