├── .github └── workflows │ ├── publish.yml │ ├── static.yml │ └── tests.yml ├── .gitignore ├── .yarnrc.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── src ├── address │ ├── ADNLAddress.ts │ ├── Address.spec.ts │ ├── Address.ts │ ├── ExternalAddress.ts │ ├── contractAddress.spec.ts │ └── contractAddress.ts ├── boc │ ├── BitBuilder.spec.ts │ ├── BitBuilder.ts │ ├── BitReader.spec.ts │ ├── BitReader.ts │ ├── BitString.spec.ts │ ├── BitString.ts │ ├── Builder.spec.ts │ ├── Builder.ts │ ├── Cell.spec.ts │ ├── Cell.ts │ ├── CellType.spec.ts │ ├── CellType.ts │ ├── Slice.spec.ts │ ├── Slice.ts │ ├── Writable.ts │ ├── cell │ │ ├── LevelMask.ts │ │ ├── __testdata__ │ │ │ ├── accountProof.txt │ │ │ ├── accountState.txt │ │ │ ├── accountStateTest.txt │ │ │ ├── accountStateTestPruned.txt │ │ │ ├── block.txt │ │ │ ├── block2.txt │ │ │ ├── configProof.txt │ │ │ ├── largeBoc.txt │ │ │ ├── manyCells.txt │ │ │ ├── tx_with_merkle_body.json │ │ │ └── veryLarge.boc │ │ ├── descriptor.ts │ │ ├── exoticLibrary.ts │ │ ├── exoticMerkleProof.ts │ │ ├── exoticMerkleUpdate.ts │ │ ├── exoticPruned.ts │ │ ├── resolveExotic.ts │ │ ├── serialization.spec.ts │ │ ├── serialization.ts │ │ ├── utils │ │ │ └── topologicalSort.ts │ │ └── wonderCalculator.ts │ └── utils │ │ ├── paddedBits.ts │ │ ├── strings.spec.ts │ │ └── strings.ts ├── contract │ ├── ComputeError.ts │ ├── Contract.ts │ ├── ContractABI.ts │ ├── ContractProvider.ts │ ├── ContractState.ts │ ├── Sender.ts │ └── openContract.ts ├── crypto │ └── safeSign.ts ├── dict │ ├── Dictionary.spec.ts │ ├── Dictionary.ts │ ├── __snapshots__ │ │ ├── parseDict.spec.ts.snap │ │ └── serializeDict.spec.ts.snap │ ├── __testdata__ │ │ ├── config.txt │ │ └── empty_value.boc │ ├── generateMerkleProof.ts │ ├── generateMerkleUpdate.ts │ ├── parseDict.spec.ts │ ├── parseDict.ts │ ├── serializeDict.spec.ts │ ├── serializeDict.ts │ └── utils │ │ ├── findCommonPrefix.spec.ts │ │ ├── findCommonPrefix.ts │ │ ├── internalKeySerializer.spec.ts │ │ ├── internalKeySerializer.ts │ │ └── readUnaryLength.ts ├── index.ts ├── tuple │ ├── builder.ts │ ├── reader.spec.ts │ ├── reader.ts │ ├── tuple.spec.ts │ ├── tuple.ts │ └── ultra_deep_cons.json ├── types │ ├── Account.ts │ ├── AccountState.ts │ ├── AccountStatus.ts │ ├── AccountStatusChange.ts │ ├── AccountStorage.ts │ ├── CommonMessageInfo.spec.ts │ ├── CommonMessageInfo.ts │ ├── CommonMessageInfoRelaxed.ts │ ├── ComputeSkipReason.ts │ ├── CurrencyCollection.ts │ ├── DepthBalanceInfo.ts │ ├── ExtraCurrency.ts │ ├── HashUpdate.ts │ ├── LibRef.ts │ ├── MasterchainStateExtra.ts │ ├── Message.spec.ts │ ├── Message.ts │ ├── MessageRelaxed.spec.ts │ ├── MessageRelaxed.ts │ ├── OutList.spec.ts │ ├── OutList.ts │ ├── ReserveMode.ts │ ├── SendMode.ts │ ├── ShardAccount.spec.ts │ ├── ShardAccount.ts │ ├── ShardAccounts.ts │ ├── ShardIdent.ts │ ├── ShardStateUnsplit.spec.ts │ ├── ShardStateUnsplit.ts │ ├── SimpleLibrary.ts │ ├── SplitMergeInfo.ts │ ├── StateInit.spec.ts │ ├── StateInit.ts │ ├── StorageExtraInfo.ts │ ├── StorageInfo.spec.ts │ ├── StorageInfo.ts │ ├── StorageUsed.ts │ ├── TickTock.ts │ ├── Transaction.spec.ts │ ├── Transaction.ts │ ├── TransactionActionPhase.ts │ ├── TransactionBouncePhase.ts │ ├── TransactionComputePhase.ts │ ├── TransactionCreditPhase.ts │ ├── TransactionDescription.ts │ ├── TransactionStoragePhase.ts │ ├── __testdata__ │ │ └── configProof.json │ ├── _export.ts │ └── _helpers.ts └── utils │ ├── base32.spec.ts │ ├── base32.ts │ ├── bitsForNumber.spec.ts │ ├── bitsForNumber.ts │ ├── convert.spec.ts │ ├── convert.ts │ ├── crc16.spec.ts │ ├── crc16.ts │ ├── crc32c.spec.ts │ ├── crc32c.ts │ ├── getMethodId.ts │ ├── maybe.ts │ └── testAddress.ts ├── tsconfig.json └── yarn.lock /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | name: Publish to NPM 6 | jobs: 7 | publish: 8 | name: Publish 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: Use Node.js 18 13 | uses: actions/setup-node@v3 14 | with: 15 | node-version: '18.x' 16 | always-auth: true 17 | - name: Install Yarn 18 | run: npm install -g yarn 19 | - name: Install dependencies 20 | run: yarn 21 | - name: Build 22 | run: yarn build 23 | - name: Setup .yarnrc.yml 24 | run: | 25 | yarn config set npmAuthToken $NPM_AUTH_TOKEN 26 | yarn config set npmAlwaysAuth true 27 | env: 28 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 29 | - name: Publish 30 | env: 31 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 32 | run: yarn npm publish --access public 33 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Node.js 34 | uses: actions/setup-node@v3 35 | - run: npm install 36 | - run: npm run docs 37 | - name: Setup Pages 38 | uses: actions/configure-pages@v2 39 | - name: Upload artifact 40 | uses: actions/upload-pages-artifact@v1 41 | with: 42 | path: './docs' 43 | - name: Deploy to GitHub Pages 44 | id: deployment 45 | uses: actions/deploy-pages@v1 46 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for testing 2 | name: Run tests 3 | 4 | on: 5 | # Runs on pushes and PRs targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | pull_request: 10 | branches: ["main"] 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | - name: Node.js 19 | uses: actions/setup-node@v3 20 | - run: npm install 21 | - run: npm run test 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | docs 4 | coverage 5 | .idea 6 | 7 | 8 | # Yarn >2.0.0 9 | .yarn/* 10 | !.yarn/releases 11 | !.yarn/plugins 12 | !.yarn/sdks 13 | !.yarn/versions 14 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 Whales Corp. (all contributions until 2023-04-27) 4 | Copyright (c) 2023+ The TON Authors (all contributions from 2023-04-27) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💎 @ton/core 2 | 3 | Core TypeScript library that implements low level primitives for TON blockchain. 4 | 5 | ## How to install 6 | 7 | ```bash 8 | yarn add @ton/core 9 | ``` 10 | ```bash 11 | npm install @ton/core 12 | ``` 13 | 14 | ## Reference Documentation 15 | 16 | [Documentation](https://ton-org.github.io/ton-core/) 17 | 18 | 19 | ## Acknowledgements 20 | 21 | This library is developed by the [Whales Corp.](https://tonwhales.com/) and maintained by [Dan Volkov](https://github.com/dvlkv). 22 | 23 | # License 24 | 25 | MIT 26 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | testPathIgnorePatterns: ["/node_modules/","/dist/"] 6 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ton/core", 3 | "version": "0.60.1", 4 | "main": "dist/index.js", 5 | "repository": "https://github.com/ton-org/ton-core.git", 6 | "author": "Whales Corp. ", 7 | "license": "MIT", 8 | "files": [ 9 | "dist", 10 | "src" 11 | ], 12 | "scripts": { 13 | "docs": "rm -fr docs && typedoc src/index.ts", 14 | "build": "rm -fr dist && tsc --declaration", 15 | "test": "jest --verbose --runInBand", 16 | "release": "yarn build && yarn test && yarn release-it --npm.yarn1" 17 | }, 18 | "devDependencies": { 19 | "@release-it/keep-a-changelog": "^3.1.0", 20 | "@ton/crypto": "^3.2.0", 21 | "@types/jest": "^29.5.12", 22 | "@types/node": "^20.11.30", 23 | "jest": "^29.7.0", 24 | "prando": "^6.0.1", 25 | "release-it": "^15.5.1", 26 | "ton3-core": "^0.0.20", 27 | "ts-jest": "^29.1.2", 28 | "ts-node": "^10.9.2", 29 | "typedoc": "^0.25.12", 30 | "typescript": "^5.4.3" 31 | }, 32 | "peerDependencies": { 33 | "@ton/crypto": ">=3.2.0" 34 | }, 35 | "dependencies": { 36 | "symbol.inspect": "1.0.1" 37 | }, 38 | "publishConfig": { 39 | "access": "public", 40 | "registry": "https://registry.npmjs.org/" 41 | }, 42 | "release-it": { 43 | "github": { 44 | "release": true 45 | }, 46 | "plugins": { 47 | "@release-it/keep-a-changelog": { 48 | "filename": "CHANGELOG.md" 49 | } 50 | } 51 | }, 52 | "packageManager": "yarn@3.4.1+sha256.b51208ae422260539b3503fe5b596f4f236947d69ed1f8797f9830331a38c841" 53 | } 54 | -------------------------------------------------------------------------------- /src/address/ADNLAddress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import inspectSymbol from 'symbol.inspect'; 10 | import { base32Decode, base32Encode } from '../utils/base32'; 11 | import { crc16 } from '../utils/crc16'; 12 | 13 | export class ADNLAddress { 14 | 15 | static parseFriendly(src: string) { 16 | 17 | if (src.length !== 55) { 18 | throw Error('Invalid address'); 19 | } 20 | 21 | // Decoding 22 | src = 'f' + src; 23 | let decoded = base32Decode(src); 24 | if (decoded[0] !== 0x2d) { 25 | throw Error('Invalid address'); 26 | } 27 | let gotHash = decoded.slice(33); 28 | let hash = crc16(decoded.slice(0, 33)); 29 | if (!hash.equals(gotHash)) { 30 | throw Error('Invalid address'); 31 | } 32 | return new ADNLAddress(decoded.slice(1, 33)); 33 | } 34 | 35 | static parseRaw(src: string) { 36 | const data = Buffer.from(src, 'base64'); 37 | return new ADNLAddress(data); 38 | } 39 | 40 | readonly address: Buffer; 41 | 42 | constructor(address: Buffer) { 43 | if (address.length !== 32) { 44 | throw Error('Invalid address'); 45 | } 46 | this.address = address; 47 | } 48 | 49 | equals(b: ADNLAddress) { 50 | return this.address.equals(b.address); 51 | } 52 | 53 | toRaw = () => { 54 | return this.address.toString('hex').toUpperCase(); 55 | } 56 | 57 | toString = () => { 58 | let data = Buffer.concat([Buffer.from([0x2D]), this.address]); 59 | let hash = crc16(data); 60 | data = Buffer.concat([data, hash]); 61 | return base32Encode(data).slice(1); 62 | } 63 | 64 | [inspectSymbol] = () => this.toString(); 65 | } -------------------------------------------------------------------------------- /src/address/ExternalAddress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import inspectSymbol from 'symbol.inspect'; 10 | export class ExternalAddress { 11 | 12 | static isAddress(src: any): src is ExternalAddress { 13 | return src instanceof ExternalAddress; 14 | } 15 | 16 | readonly value: bigint; 17 | readonly bits: number; 18 | 19 | constructor(value: bigint, bits: number) { 20 | this.value = value; 21 | this.bits = bits; 22 | } 23 | 24 | toString() { 25 | return `External<${this.bits}:${this.value}>`; 26 | } 27 | 28 | [inspectSymbol] = () => this.toString() 29 | } -------------------------------------------------------------------------------- /src/address/contractAddress.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { Address } from "./Address"; 11 | import { contractAddress } from "./contractAddress"; 12 | 13 | describe('contractAddress', () => { 14 | it('should resolve address correctly', () => { 15 | let addr = contractAddress(0, { code: beginCell().storeUint(1, 8).endCell(), data: beginCell().storeUint(2, 8).endCell() }); 16 | expect(addr.equals(Address.parse('EQCSY_vTjwGrlvTvkfwhinJ60T2oiwgGn3U7Tpw24kupIhHz'))); 17 | }); 18 | }); -------------------------------------------------------------------------------- /src/address/contractAddress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { StateInit, storeStateInit } from "../types/StateInit"; 11 | import { Address } from "./Address"; 12 | 13 | export function contractAddress(workchain: number, init: StateInit) { 14 | let hash = beginCell() 15 | .store(storeStateInit(init)) 16 | .endCell() 17 | .hash(); 18 | return new Address(workchain, hash); 19 | } -------------------------------------------------------------------------------- /src/boc/Cell.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitString } from "./BitString"; 10 | import { Cell } from "./Cell"; 11 | import { CellType } from "./CellType"; 12 | 13 | describe('Cell', () => { 14 | it('should construct', () => { 15 | let cell = new Cell(); 16 | expect(cell.type).toBe(CellType.Ordinary); 17 | expect(cell.bits.equals(new BitString(Buffer.alloc(0), 0, 0))).toEqual(true); 18 | expect(cell.refs).toEqual([]); 19 | }); 20 | }); -------------------------------------------------------------------------------- /src/boc/CellType.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { CellType } from './CellType'; 10 | 11 | describe('CellType', () => { 12 | it('should match values in c++ code', () => { 13 | expect(CellType.Ordinary).toBe(-1); 14 | expect(CellType.PrunedBranch).toBe(1); 15 | expect(CellType.Library).toBe(2); 16 | expect(CellType.MerkleProof).toBe(3); 17 | expect(CellType.MerkleUpdate).toBe(4); 18 | }); 19 | }); -------------------------------------------------------------------------------- /src/boc/CellType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export enum CellType { 10 | Ordinary = -1, 11 | PrunedBranch = 1, 12 | Library = 2, 13 | MerkleProof = 3, 14 | MerkleUpdate = 4 15 | } -------------------------------------------------------------------------------- /src/boc/Writable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "./Builder"; 10 | 11 | export type Writable = { writeTo: (builder: Builder) => void }; -------------------------------------------------------------------------------- /src/boc/cell/LevelMask.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export class LevelMask { 10 | private _mask: number = 0; 11 | private _hashIndex: number 12 | private _hashCount: number 13 | 14 | constructor(mask: number = 0) { 15 | this._mask = mask; 16 | this._hashIndex = countSetBits(this._mask); 17 | this._hashCount = this._hashIndex + 1; 18 | } 19 | 20 | get value(): number { 21 | return this._mask; 22 | } 23 | 24 | get level(): number { 25 | return 32 - Math.clz32(this._mask); 26 | } 27 | 28 | get hashIndex(): number { 29 | return this._hashIndex; 30 | } 31 | 32 | get hashCount(): number { 33 | return this._hashCount; 34 | } 35 | 36 | apply(level: number): LevelMask { 37 | return new LevelMask(this._mask & ((1 << level) - 1)) 38 | } 39 | 40 | isSignificant(level: number): boolean { 41 | let res = level === 0 || (this._mask >> (level - 1)) % 2 !== 0; 42 | return res; 43 | // bool res = level == 0 | | ( (mask_ >> (level -1)) % 2 != 0); 44 | } 45 | } 46 | 47 | function countSetBits(n: number): number { 48 | n = n - ((n >> 1) & 0x55555555) 49 | n = (n & 0x33333333) + ((n >> 2) & 0x33333333) 50 | 51 | return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24 52 | } -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/accountProof.txt: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/accountStateTest.txt: -------------------------------------------------------------------------------- 1 | te6ccgECFgEAAzwAAnHAC2sf/Hy34aMM7n9f9/V+ThHDehjH71LWBETy/JrTirPCLIWQQx1iCWAAABo03x9sGW4gl8XD00ABAgEU/wD0pBP0vPLICwMAUQAAKwIpqaMXw+Q7b1IiPXMEBAINhh9rwzJYtGNen/6gFDXD2gd0GaxAAgEgBAUCAUgGBwT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xITFBUC5tAB0NMDIXGwkl8E4CLXScEgkl8E4ALTHyGCEHBsdWe9IoIQZHN0cr2wkl8F4AP6QDAg+kQByMoHy//J0O1E0IEBQNch9AQwXIEBCPQKb6Exs5JfB+AF0z/IJYIQcGx1Z7qSODDjDQOCEGRzdHK6kl8G4w0ICQIBIAoLAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAwNAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCAVgODwARuMl+1E0NcLH4AD2ynftRNCBAUDXIfQEMALIygfL/8nQAYEBCPQKb6ExgAgEgEBEAGa3OdqJoQCBrkOuF/8AAGa8d9qJoQBBrkOuFj8AAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1U -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/accountStateTestPruned.txt: -------------------------------------------------------------------------------- 1 | te6ccgEBBAEArwAJRgPIr248LcbQSSCsDD5Rb27WLhRGYiTEGG+uChgAAXoNHAAIASJxwAtrH/x8t+GjDO5/X/f1fk4Rw3oYx+9S1gRE8vya04qzwiyFkEMdYglgAAAaNN8fbBluIJfFw9NAAgMoSAEB/rX/aCDi/w2Ug+fg1iyBfYRniftK5YDIeIZtlZ2r1cAAByhIAQEg0z54hgTX/ohMEnHs6qluCydagWgxQoxSyLwK8qfAOQAA -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/block2.txt: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/tx_with_merkle_body.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Transaction with merkle proof in body", 3 | "link": "https://testnet.tonscan.org/tx/ymdvDzDSHIgo0QlEJHlwhbYD6ZHSaUPuF-5dd6xLCJY=", 4 | "kind": "transaction", 5 | "boc": "b5ee9c7201021c010004260003b5792fb2fb7884d2a79f8e5b1279264597682fd7e56cf3ccfebea767db7173526f100000a2261348e01ab0389959f7f3c33161c3e4bf3a5901c38958667d64b5603ea04397c1d44279400000a1f24f06c056453860b0003476245d680102030201e00405008272d96846fe22c11b2cbc067eea6a82f1b332efa12da7070d4e90ee6c9bd56388009f339073f094314d4a2b696c2face70a1a07882e875bd28aa243d0a0538e291002110cae650619760604401a1b01df880125f65f6f109a54f3f1cb624f24c8b2ed05fafcad9e799fd7d4ecfb6e2e6a4de2044942e0fdde60708999830ca7800441f5cc83bdacc4b308ea56a28d39cd0d82e2c8ecfd45ccaf81a95d04b896c13c3583a8dcabf41812ba9d50018e917836c81000000003229c3178000000d01c060101df07018032000f1e41cb30becd660a374c510bcd742b99682d17958ca64e1f9d598b6ae48f65202faf080000000000000000000000000000419d5d4d00000000000000000801cf280125f65f6f109a54f3f1cb624f24c8b2ed05fafcad9e799fd7d4ecfb6e2e6a4de300078f20e5985f66b3051ba62885e6ba15ccb4168bcac653270fceacc5b57247b29017d7840070eb8b0678525200001444c2691c04c8a70c1620ceaea68000000000000000400809460329fe4b78e00eea1a217eb3fe13cddfedab08022cf926f82a08343cff3be3342e0008092201200a0b284801018eeca88229bd7b563d72ba57749cd8c63f8efa7d47f3e4f74bc7c51847ec45be00072201200c0d2201200e0f28480101fe18b21f54a2802d6fef56513063a78c4af471bf0e24c31e00793949c91aac39000622012010112848010155cdeed7850ef4313f673c311f5c39bec3c161940c0a71cadde5a031f7db13a7000528480101b988fbf55f0ef7e992d36862abf33933601f50e1eb72c71762d5225bb843c87e0004220120121328480101960d9b2f2590c46bca66ac776d6048598c153f8cf05c980a1042e8003f24928300032201201415284801016604e5bef768c9ed879cdb892c0cf2076208d4d4a41b3aecec00b045eefb6c530002220120161722012018192848010155fae57e9a2b8351802cb998e175e2b93b3dc247592edb1901d07d0097902140000128480101f142b2da4d0e106b131f3640bd8f3cad72b53a3d6153c668fe75db1de12ec1ff0000008118f1e3ac53631fcb4844506477b86e3fffbef1c88e09633956a96a6112ff7d612513ceba691b3a6a0a29131524aa081cdc20820a40cb3ae22b01445499b7618c20009d417f03138800000000000000000e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006fc9f0ccf44c78519c000000000002000000000002c3a7bf1a1987b4997fd6e16077ca4c5a9c62dd0bde5c7cd809ef35f2cbfaf24444d07b1c", 6 | "hash": "ca676f0f30d21c8828d1094424797085b603e991d26943ee17ee5d77ac4b0896" 7 | } -------------------------------------------------------------------------------- /src/boc/cell/__testdata__/veryLarge.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ton-org/ton-core/1860a37a4d27a7a7ce52ccc91618b534595097fa/src/boc/cell/__testdata__/veryLarge.boc -------------------------------------------------------------------------------- /src/boc/cell/descriptor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitString } from "../BitString"; 10 | import { Cell } from "../Cell"; 11 | import { CellType } from "../CellType"; 12 | import { bitsToPaddedBuffer } from "../utils/paddedBits"; 13 | 14 | export function getRefsDescriptor(refs: Cell[], levelMask: number, type: CellType) { 15 | return refs.length + (type !== CellType.Ordinary ? 1 : 0) * 8 + levelMask * 32; 16 | } 17 | 18 | export function getBitsDescriptor(bits: BitString) { 19 | let len = bits.length; 20 | return Math.ceil(len / 8) + Math.floor(len / 8); 21 | } 22 | 23 | export function getRepr(originalBits: BitString, bits: BitString, refs: Cell[], level: number, levelMask: number, type: CellType) { 24 | 25 | // Allocate 26 | const bitsLen = Math.ceil(bits.length / 8); 27 | const repr = Buffer.alloc(2 + bitsLen + (2 + 32) * refs.length); 28 | 29 | // Write descriptors 30 | let reprCursor = 0; 31 | repr[reprCursor++] = getRefsDescriptor(refs, levelMask, type); 32 | repr[reprCursor++] = getBitsDescriptor(originalBits); 33 | 34 | // Write bits 35 | bitsToPaddedBuffer(bits).copy(repr, reprCursor); 36 | reprCursor += bitsLen; 37 | 38 | // Write refs 39 | for (const c of refs) { 40 | let childDepth: number; 41 | if (type == CellType.MerkleProof || type == CellType.MerkleUpdate) { 42 | childDepth = c.depth(level + 1); 43 | } else { 44 | childDepth = c.depth(level); 45 | } 46 | repr[reprCursor++] = Math.floor(childDepth / 256); 47 | repr[reprCursor++] = childDepth % 256; 48 | } 49 | for (const c of refs) { 50 | let childHash: Buffer; 51 | if (type == CellType.MerkleProof || type == CellType.MerkleUpdate) { 52 | childHash = c.hash(level + 1); 53 | } else { 54 | childHash = c.hash(level); 55 | } 56 | childHash.copy(repr, reprCursor); 57 | reprCursor += 32; 58 | } 59 | 60 | // Result 61 | return repr; 62 | } -------------------------------------------------------------------------------- /src/boc/cell/exoticLibrary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitReader } from "../BitReader"; 10 | import { BitString } from "../BitString"; 11 | import { Cell } from "../Cell"; 12 | 13 | export function exoticLibrary(bits: BitString, refs: Cell[]) { 14 | const reader = new BitReader(bits); 15 | 16 | // type + hash 17 | const size = 8 + 256; 18 | 19 | if (bits.length !== size) { 20 | throw new Error(`Library cell must have exactly (8 + 256) bits, got "${bits.length}"`); 21 | } 22 | 23 | // Check type 24 | let type = reader.loadUint(8); 25 | if (type !== 2) { 26 | throw new Error(`Library cell must have type 2, got "${type}"`); 27 | } 28 | 29 | return {}; 30 | } -------------------------------------------------------------------------------- /src/boc/cell/exoticMerkleProof.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitReader } from "../BitReader"; 10 | import { BitString } from "../BitString"; 11 | import { Cell } from "../Cell"; 12 | import { beginCell } from '../Builder'; 13 | 14 | export function exoticMerkleProof(bits: BitString, refs: Cell[]) { 15 | const reader = new BitReader(bits); 16 | 17 | // type + hash + depth 18 | const size = 8 + 256 + 16; 19 | 20 | if (bits.length !== size) { 21 | throw new Error(`Merkle Proof cell must have exactly (8 + 256 + 16) bits, got "${bits.length}"`); 22 | } 23 | 24 | if (refs.length !== 1) { 25 | throw new Error(`Merkle Proof cell must have exactly 1 ref, got "${refs.length}"`); 26 | } 27 | 28 | // Check type 29 | let type = reader.loadUint(8); 30 | if (type !== 3) { 31 | throw new Error(`Merkle Proof cell must have type 3, got "${type}"`); 32 | } 33 | 34 | // Check data 35 | const proofHash = reader.loadBuffer(32); 36 | const proofDepth = reader.loadUint(16); 37 | const refHash = refs[0].hash(0) 38 | const refDepth = refs[0].depth(0); 39 | 40 | if (proofDepth !== refDepth) { 41 | throw new Error(`Merkle Proof cell ref depth must be exactly "${proofDepth}", got "${refDepth}"`); 42 | } 43 | 44 | if (!proofHash.equals(refHash)) { 45 | throw new Error(`Merkle Proof cell ref hash must be exactly "${proofHash.toString('hex')}", got "${refHash.toString('hex')}"`); 46 | } 47 | 48 | return { 49 | proofDepth, 50 | proofHash 51 | }; 52 | } 53 | 54 | export function convertToMerkleProof(c: Cell): Cell { 55 | return beginCell() 56 | .storeUint(3, 8) 57 | .storeBuffer(c.hash(0)) 58 | .storeUint(c.depth(0), 16) 59 | .storeRef(c) 60 | .endCell({ exotic: true }); 61 | } -------------------------------------------------------------------------------- /src/boc/cell/exoticMerkleUpdate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitReader } from "../BitReader"; 10 | import { BitString } from "../BitString"; 11 | import { Cell } from "../Cell"; 12 | 13 | export function exoticMerkleUpdate(bits: BitString, refs: Cell[]) { 14 | const reader = new BitReader(bits); 15 | 16 | // type + hash + hash + depth + depth 17 | const size = 8 + (2 * (256 + 16)); 18 | 19 | if (bits.length !== size) { 20 | throw new Error(`Merkle Update cell must have exactly (8 + (2 * (256 + 16))) bits, got "${bits.length}"`); 21 | } 22 | 23 | if (refs.length !== 2) { 24 | throw new Error(`Merkle Update cell must have exactly 2 refs, got "${refs.length}"`); 25 | } 26 | 27 | let type = reader.loadUint(8); 28 | if (type !== 4) { 29 | throw new Error(`Merkle Update cell type must be exactly 4, got "${type}"`) 30 | } 31 | 32 | const proofHash1 = reader.loadBuffer(32); 33 | const proofHash2 = reader.loadBuffer(32); 34 | const proofDepth1 = reader.loadUint(16); 35 | const proofDepth2 = reader.loadUint(16); 36 | 37 | if (proofDepth1 !== refs[0].depth(0)) { 38 | throw new Error(`Merkle Update cell ref depth must be exactly "${proofDepth1}", got "${refs[0].depth(0)}"`); 39 | } 40 | 41 | if (!proofHash1.equals(refs[0].hash(0))) { 42 | throw new Error(`Merkle Update cell ref hash must be exactly "${proofHash1.toString('hex')}", got "${refs[0].hash(0).toString('hex')}"`); 43 | } 44 | 45 | if (proofDepth2 !== refs[1].depth(0)) { 46 | throw new Error(`Merkle Update cell ref depth must be exactly "${proofDepth2}", got "${refs[1].depth(0)}"`); 47 | } 48 | 49 | if (!proofHash2.equals(refs[1].hash(0))) { 50 | throw new Error(`Merkle Update cell ref hash must be exactly "${proofHash2.toString('hex')}", got "${refs[1].hash(0).toString('hex')}"`); 51 | } 52 | 53 | return { 54 | proofDepth1, 55 | proofDepth2, 56 | proofHash1, 57 | proofHash2 58 | }; 59 | } -------------------------------------------------------------------------------- /src/boc/cell/exoticPruned.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitReader } from "../BitReader"; 10 | import { BitString } from "../BitString"; 11 | import { Cell } from "../Cell"; 12 | import { LevelMask } from "./LevelMask"; 13 | 14 | export type ExoticPruned = { 15 | mask: number; 16 | pruned: { depth: number, hash: Buffer }[] 17 | } 18 | 19 | export function exoticPruned(bits: BitString, refs: Cell[]): ExoticPruned { 20 | 21 | let reader = new BitReader(bits); 22 | 23 | // Check type 24 | let type = reader.loadUint(8); 25 | if (type !== 1) { 26 | throw new Error(`Pruned branch cell must have type 1, got "${type}"`); 27 | } 28 | 29 | // Check refs 30 | if (refs.length !== 0) { 31 | throw new Error(`Pruned Branch cell can't has refs, got "${refs.length}"`); 32 | } 33 | 34 | 35 | // Resolve cell 36 | let mask: LevelMask; 37 | if (bits.length === 280) { 38 | 39 | // Special case for config proof 40 | // This test proof is generated in the moment of voting for a slashing 41 | // it seems that tools generate it incorrectly and therefore doesn't have mask in it 42 | // so we need to hardcode it equal to 1 43 | 44 | mask = new LevelMask(1); 45 | 46 | } else { 47 | 48 | // Check level 49 | mask = new LevelMask(reader.loadUint(8)); 50 | if (mask.level < 1 || mask.level > 3) { 51 | throw new Error(`Pruned Branch cell level must be >= 1 and <= 3, got "${mask.level}/${mask.value}"`); 52 | } 53 | 54 | // Read pruned 55 | const size = 8 + 8 + (mask.apply(mask.level - 1).hashCount * (256 /* Hash */ + 16 /* Depth */)); 56 | if (bits.length !== size) { 57 | throw new Error(`Pruned branch cell must have exactly ${size} bits, got "${bits.length}"`); 58 | } 59 | } 60 | 61 | // Read pruned 62 | 63 | let pruned: { depth: number, hash: Buffer }[] = []; 64 | let hashes: Buffer[] = []; 65 | let depths: number[] = []; 66 | for (let i = 0; i < mask.level; i++) { 67 | hashes.push(reader.loadBuffer(32)); 68 | } 69 | for (let i = 0; i < mask.level; i++) { 70 | depths.push(reader.loadUint(16)); 71 | } 72 | for (let i = 0; i < mask.level; i++) { 73 | pruned.push({ 74 | depth: depths[i], 75 | hash: hashes[i] 76 | }); 77 | } 78 | 79 | return { 80 | mask: mask.value, 81 | pruned 82 | }; 83 | } -------------------------------------------------------------------------------- /src/boc/cell/resolveExotic.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitReader } from "../BitReader"; 10 | import { BitString } from "../BitString"; 11 | import { Cell } from "../Cell"; 12 | import { CellType } from "../CellType"; 13 | import { exoticLibrary } from "./exoticLibrary"; 14 | import { exoticMerkleProof } from "./exoticMerkleProof"; 15 | import { exoticMerkleUpdate } from "./exoticMerkleUpdate"; 16 | import { exoticPruned } from "./exoticPruned"; 17 | import { LevelMask } from "./LevelMask"; 18 | 19 | function resolvePruned(bits: BitString, refs: Cell[]): { type: CellType, depths: number[], hashes: Buffer[], mask: LevelMask } { 20 | 21 | // Parse pruned cell 22 | let pruned = exoticPruned(bits, refs); 23 | 24 | // Calculate parameters 25 | let depths: number[] = []; 26 | let hashes: Buffer[] = []; 27 | let mask = new LevelMask(pruned.mask); 28 | for (let i = 0; i < pruned.pruned.length; i++) { 29 | depths.push(pruned.pruned[i].depth); 30 | hashes.push(pruned.pruned[i].hash); 31 | } 32 | 33 | return { 34 | type: CellType.PrunedBranch, 35 | depths, 36 | hashes, 37 | mask 38 | }; 39 | } 40 | 41 | function resolveLibrary(bits: BitString, refs: Cell[]): { type: CellType, depths: number[], hashes: Buffer[], mask: LevelMask } { 42 | 43 | // Parse library cell 44 | let pruned = exoticLibrary(bits, refs); 45 | 46 | // Calculate parameters 47 | let depths: number[] = []; 48 | let hashes: Buffer[] = []; 49 | let mask = new LevelMask(); 50 | 51 | return { 52 | type: CellType.Library, 53 | depths, 54 | hashes, 55 | mask 56 | }; 57 | } 58 | 59 | 60 | function resolveMerkleProof(bits: BitString, refs: Cell[]): { type: CellType, depths: number[], hashes: Buffer[], mask: LevelMask } { 61 | 62 | // Parse merkle proof cell 63 | let merkleProof = exoticMerkleProof(bits, refs); 64 | 65 | // Calculate parameters 66 | let depths: number[] = []; 67 | let hashes: Buffer[] = []; 68 | let mask = new LevelMask(refs[0].level() >> 1); 69 | 70 | return { 71 | type: CellType.MerkleProof, 72 | depths, 73 | hashes, 74 | mask 75 | }; 76 | } 77 | 78 | function resolveMerkleUpdate(bits: BitString, refs: Cell[]): { type: CellType, depths: number[], hashes: Buffer[], mask: LevelMask } { 79 | 80 | // Parse merkle proof cell 81 | let merkleUpdate = exoticMerkleUpdate(bits, refs); 82 | 83 | // Calculate parameters 84 | let depths: number[] = []; 85 | let hashes: Buffer[] = []; 86 | let mask = new LevelMask((refs[0].level() | refs[1].level()) >> 1); 87 | 88 | return { 89 | type: CellType.MerkleUpdate, 90 | depths, 91 | hashes, 92 | mask 93 | }; 94 | } 95 | 96 | export function resolveExotic(bits: BitString, refs: Cell[]): { type: CellType, depths: number[], hashes: Buffer[], mask: LevelMask } { 97 | let reader = new BitReader(bits); 98 | let type = reader.preloadUint(8); 99 | 100 | if (type === 1) { 101 | return resolvePruned(bits, refs); 102 | } 103 | 104 | if (type === 2) { 105 | return resolveLibrary(bits, refs); 106 | } 107 | 108 | if (type === 3) { 109 | return resolveMerkleProof(bits, refs); 110 | } 111 | 112 | if (type === 4) { 113 | return resolveMerkleUpdate(bits, refs); 114 | } 115 | 116 | throw Error('Invalid exotic cell type: ' + type); 117 | } -------------------------------------------------------------------------------- /src/boc/cell/utils/topologicalSort.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Cell } from "../../Cell"; 10 | 11 | export function topologicalSort(src: Cell) { 12 | let pending: Cell[] = [src]; 13 | let allCells = new Map(); 14 | let notPermCells = new Set(); 15 | let sorted: string[] = []; 16 | while (pending.length > 0) { 17 | const cells = [...pending]; 18 | pending = []; 19 | for (let cell of cells) { 20 | const hash = cell.hash().toString('hex'); 21 | if (allCells.has(hash)) { 22 | continue; 23 | } 24 | notPermCells.add(hash); 25 | allCells.set(hash, { cell: cell, refs: cell.refs.map((v) => v.hash().toString('hex')) }); 26 | for (let r of cell.refs) { 27 | pending.push(r); 28 | } 29 | } 30 | } 31 | let tempMark = new Set(); 32 | function visit(hash: string) { 33 | if (!notPermCells.has(hash)) { 34 | return; 35 | } 36 | if (tempMark.has(hash)) { 37 | throw Error('Not a DAG'); 38 | } 39 | tempMark.add(hash); 40 | let refs = allCells.get(hash)!.refs; 41 | for (let ci = refs.length - 1; ci >= 0; ci--) { 42 | visit(refs[ci]); 43 | } 44 | sorted.push(hash); 45 | tempMark.delete(hash); 46 | notPermCells.delete(hash); 47 | } 48 | while (notPermCells.size > 0) { 49 | const id = Array.from(notPermCells)[0]; 50 | visit(id); 51 | } 52 | 53 | let indexes = new Map(); 54 | for (let i = 0; i < sorted.length; i++) { 55 | indexes.set(sorted[sorted.length-i-1], i); 56 | } 57 | 58 | let result: { cell: Cell, refs: number[] }[] = []; 59 | for (let i = sorted.length - 1; i >= 0; i--) { 60 | let ent = sorted[i]; 61 | const rrr = allCells.get(ent)!; 62 | result.push({ cell: rrr.cell, refs: rrr.refs.map((v) => indexes.get(v)!) }); 63 | } 64 | 65 | return result; 66 | } -------------------------------------------------------------------------------- /src/boc/cell/wonderCalculator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitString } from "../BitString"; 10 | import { CellType } from "../CellType"; 11 | import { Cell } from '../Cell'; 12 | import { LevelMask } from "./LevelMask"; 13 | import { ExoticPruned, exoticPruned } from "./exoticPruned"; 14 | import { exoticMerkleProof } from "./exoticMerkleProof"; 15 | import { getRepr } from "./descriptor"; 16 | import { sha256_sync } from "@ton/crypto"; 17 | import { exoticMerkleUpdate } from "./exoticMerkleUpdate"; 18 | import { exoticLibrary } from "./exoticLibrary"; 19 | 20 | // 21 | // This function replicates unknown logic of resolving cell data 22 | // https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 23 | // 24 | export function wonderCalculator(type: CellType, bits: BitString, refs: Cell[]): { mask: LevelMask, hashes: Buffer[], depths: number[] } { 25 | 26 | // 27 | // Resolving level mask 28 | // 29 | 30 | let levelMask: LevelMask; 31 | let pruned: ExoticPruned | null = null; 32 | if (type === CellType.Ordinary) { 33 | let mask = 0; 34 | for (let r of refs) { 35 | mask = mask | r.mask.value; 36 | } 37 | levelMask = new LevelMask(mask); 38 | } else if (type === CellType.PrunedBranch) { 39 | 40 | // Parse pruned 41 | pruned = exoticPruned(bits, refs); 42 | 43 | // Load level 44 | levelMask = new LevelMask(pruned.mask); 45 | 46 | } else if (type === CellType.MerkleProof) { 47 | 48 | // Parse proof 49 | let loaded = exoticMerkleProof(bits, refs); 50 | 51 | // Load level 52 | levelMask = new LevelMask(refs[0].mask.value >> 1); 53 | } else if (type === CellType.MerkleUpdate) { 54 | 55 | // Parse update 56 | let loaded = exoticMerkleUpdate(bits, refs); 57 | 58 | // Load level 59 | levelMask = new LevelMask((refs[0].mask.value | refs[1].mask.value) >> 1); 60 | } else if (type === CellType.Library) { 61 | 62 | // Parse library 63 | let loaded = exoticLibrary(bits, refs); 64 | 65 | // Load level 66 | levelMask = new LevelMask(); 67 | } else { 68 | throw new Error("Unsupported exotic type"); 69 | } 70 | 71 | // 72 | // Calculate hashes and depths 73 | // NOTE: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 74 | // 75 | 76 | let depths: number[] = []; 77 | let hashes: Buffer[] = []; 78 | 79 | let hashCount = type === CellType.PrunedBranch ? 1 : levelMask.hashCount; 80 | let totalHashCount = levelMask.hashCount; 81 | let hashIOffset = totalHashCount - hashCount; 82 | for (let levelI = 0, hashI = 0; levelI <= levelMask.level; levelI++) { 83 | 84 | if (!levelMask.isSignificant(levelI)) { 85 | continue; 86 | } 87 | 88 | if (hashI < hashIOffset) { 89 | hashI++; 90 | continue; 91 | } 92 | 93 | // 94 | // Bits 95 | // 96 | 97 | let currentBits: BitString; 98 | if (hashI === hashIOffset) { 99 | if (!(levelI === 0 || type === CellType.PrunedBranch)) { 100 | throw Error('Invalid'); 101 | } 102 | currentBits = bits; 103 | } else { 104 | if (!(levelI !== 0 && type !== CellType.PrunedBranch)) { 105 | throw Error('Invalid: ' + levelI + ', ' + type); 106 | } 107 | currentBits = new BitString(hashes[hashI - hashIOffset - 1], 0, 256); 108 | } 109 | 110 | // 111 | // Depth 112 | // 113 | 114 | let currentDepth = 0; 115 | for (let c of refs) { 116 | let childDepth: number; 117 | if (type == CellType.MerkleProof || type == CellType.MerkleUpdate) { 118 | childDepth = c.depth(levelI + 1); 119 | } else { 120 | childDepth = c.depth(levelI); 121 | } 122 | currentDepth = Math.max(currentDepth, childDepth); 123 | } 124 | if (refs.length > 0) { 125 | currentDepth++; 126 | } 127 | 128 | // 129 | // Hash 130 | // 131 | 132 | let repr = getRepr(bits, currentBits, refs, levelI, levelMask.apply(levelI).value, type); 133 | let hash = sha256_sync(repr); 134 | 135 | // 136 | // Persist next 137 | // 138 | 139 | let destI = hashI - hashIOffset; 140 | depths[destI] = currentDepth; 141 | hashes[destI] = hash; 142 | 143 | // 144 | // Next 145 | // 146 | 147 | hashI++; 148 | } 149 | 150 | // 151 | // Calculate hash and depth for all levels 152 | // 153 | 154 | let resolvedHashes: Buffer[] = []; 155 | let resolvedDepths: number[] = []; 156 | if (pruned) { 157 | for (let i = 0; i < 4; i++) { 158 | const { hashIndex } = levelMask.apply(i); 159 | const { hashIndex: thisHashIndex } = levelMask; 160 | if (hashIndex !== thisHashIndex) { 161 | resolvedHashes.push(pruned.pruned[hashIndex].hash); 162 | resolvedDepths.push(pruned.pruned[hashIndex].depth); 163 | } else { 164 | resolvedHashes.push(hashes[0]); 165 | resolvedDepths.push(depths[0]); 166 | } 167 | } 168 | } else { 169 | for (let i = 0; i < 4; i++) { 170 | resolvedHashes.push(hashes[levelMask.apply(i).hashIndex]); 171 | resolvedDepths.push(depths[levelMask.apply(i).hashIndex]); 172 | } 173 | } 174 | 175 | // 176 | // Result 177 | // 178 | 179 | return { 180 | mask: levelMask, 181 | hashes: resolvedHashes, 182 | depths: resolvedDepths 183 | }; 184 | } -------------------------------------------------------------------------------- /src/boc/utils/paddedBits.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { BitBuilder } from "../BitBuilder"; 10 | import { BitString } from "../BitString"; 11 | 12 | export function bitsToPaddedBuffer(bits: BitString) { 13 | 14 | // Create builder 15 | let builder = new BitBuilder(Math.ceil(bits.length / 8) * 8); 16 | builder.writeBits(bits); 17 | 18 | // Apply padding 19 | let padding = Math.ceil(bits.length / 8) * 8 - bits.length; 20 | for (let i = 0; i < padding; i++) { 21 | if (i === 0) { 22 | builder.writeBit(1); 23 | } else { 24 | builder.writeBit(0); 25 | } 26 | } 27 | 28 | return builder.buffer(); 29 | } 30 | export function paddedBufferToBits(buff: Buffer) { 31 | let bitLen = 0; 32 | // Finding rightmost non-zero byte in the buffer 33 | for( let i = buff.length - 1; i >= 0; i--) { 34 | if(buff[i] !== 0) { 35 | const testByte = buff[i]; 36 | // Looking for a rightmost set padding bit 37 | let bitPos = testByte & -testByte; 38 | if((bitPos & 1) == 0) { 39 | // It's power of 2 (only one bit set) 40 | bitPos = Math.log2(bitPos) + 1; 41 | } 42 | if(i > 0){ 43 | // If we are dealing with more than 1 byte buffer 44 | bitLen = i << 3; //Number of full bytes * 8 45 | } 46 | bitLen += 8 - bitPos; 47 | break; 48 | } 49 | } 50 | return new BitString(buff, 0, bitLen); 51 | } 52 | -------------------------------------------------------------------------------- /src/boc/utils/strings.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { comment } from '../../types/_helpers'; 10 | import { readString, stringToCell } from "./strings"; 11 | 12 | describe('strings', () => { 13 | let cases: string[][] = [ 14 | ['123'], 15 | ['12345678901234567890123456789012345678901234567890123456789012345678901234567890'], 16 | ['привет мир 👀 привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀'] 17 | ]; 18 | 19 | it.each(cases)('should serialize and parse strings', (c) => { 20 | let cell = stringToCell(c); 21 | expect(readString(cell.beginParse())).toEqual(c); 22 | }); 23 | 24 | it.each(cases)('should serialize and parse string with padded slice', (c) => { 25 | let cell = comment(c); 26 | 27 | expect(readString(cell.beginParse().skip(32))).toEqual(c); 28 | }) 29 | }); -------------------------------------------------------------------------------- /src/boc/utils/strings.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../Builder"; 10 | import { Cell } from "../Cell"; 11 | import { Slice } from "../Slice"; 12 | 13 | function readBuffer(slice: Slice) { 14 | // Check consistency 15 | if (slice.remainingBits % 8 !== 0) { 16 | throw new Error(`Invalid string length: ${slice.remainingBits}`); 17 | } 18 | if (slice.remainingRefs !== 0 && slice.remainingRefs !== 1) { 19 | throw new Error(`invalid number of refs: ${slice.remainingRefs}`); 20 | } 21 | 22 | // Read string 23 | let res: Buffer 24 | if (slice.remainingBits === 0) { 25 | res = Buffer.alloc(0); 26 | } else { 27 | res = slice.loadBuffer(slice.remainingBits / 8); 28 | } 29 | 30 | // Read tail 31 | if (slice.remainingRefs === 1) { 32 | res = Buffer.concat([res, readBuffer(slice.loadRef().beginParse())]); 33 | } 34 | 35 | return res; 36 | } 37 | 38 | export function readString(slice: Slice) { 39 | return readBuffer(slice).toString(); 40 | } 41 | 42 | function writeBuffer(src: Buffer, builder: Builder) { 43 | if (src.length > 0) { 44 | let bytes = Math.floor(builder.availableBits / 8); 45 | if (src.length > bytes) { 46 | let a = src.subarray(0, bytes); 47 | let t = src.subarray(bytes); 48 | builder = builder.storeBuffer(a); 49 | let bb = beginCell(); 50 | writeBuffer(t, bb); 51 | builder = builder.storeRef(bb.endCell()); 52 | } else { 53 | builder = builder.storeBuffer(src); 54 | } 55 | } 56 | } 57 | 58 | export function stringToCell(src: string): Cell { 59 | let builder = beginCell(); 60 | writeBuffer(Buffer.from(src), builder); 61 | return builder.endCell(); 62 | } 63 | 64 | export function writeString(src: string, builder: Builder) { 65 | writeBuffer(Buffer.from(src), builder); 66 | } -------------------------------------------------------------------------------- /src/contract/ComputeError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Maybe } from "../utils/maybe"; 10 | 11 | export class ComputeError extends Error { 12 | exitCode: number; 13 | debugLogs: string | null; 14 | logs: string | null; 15 | 16 | constructor(message: string, exitCode: number, opts?: { debugLogs?: Maybe, logs?: Maybe }) { 17 | super(message); 18 | this.exitCode = exitCode; 19 | this.debugLogs = opts && opts.debugLogs ? opts.debugLogs : null; 20 | this.logs = opts && opts.logs ? opts.logs : null; 21 | Object.setPrototypeOf(this, ComputeError.prototype); 22 | } 23 | } -------------------------------------------------------------------------------- /src/contract/Contract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { Cell } from "../boc/Cell"; 11 | import { StateInit } from "../types/StateInit"; 12 | import { Maybe } from "../utils/maybe"; 13 | import { ContractABI } from "./ContractABI"; 14 | 15 | export interface Contract { 16 | readonly address: Address; 17 | readonly init?: Maybe; 18 | readonly abi?: Maybe; 19 | } -------------------------------------------------------------------------------- /src/contract/ContractABI.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Maybe } from "../utils/maybe"; 10 | 11 | export type ABIError = { 12 | message: string 13 | }; 14 | 15 | export type ABITypeRef = { 16 | kind: 'simple', 17 | type: string, 18 | optional?: Maybe, 19 | format?: Maybe, 20 | } | { 21 | kind: 'dict', 22 | format?: Maybe, 23 | key: string, 24 | keyFormat?: Maybe, 25 | value: string, 26 | valueFormat?: Maybe, 27 | }; 28 | 29 | export type ABIField = { 30 | name: string, 31 | type: ABITypeRef 32 | } 33 | 34 | export type ABIType = { 35 | name: string, 36 | header?: Maybe, 37 | fields: ABIField[], 38 | }; 39 | 40 | export type ABIArgument = { 41 | name: string, 42 | type: ABITypeRef 43 | } 44 | 45 | export type ABIGetter = { 46 | name: string, 47 | methodId?: Maybe, 48 | arguments?: Maybe, 49 | returnType?: Maybe, 50 | }; 51 | 52 | export type ABIReceiverMessage = { 53 | kind: 'typed', 54 | type: string 55 | } | { 56 | kind: 'any', 57 | } | { 58 | kind: 'empty', 59 | } | { 60 | kind: 'text', 61 | text?: Maybe 62 | }; 63 | 64 | export type ABIReceiver = { 65 | receiver: 'internal' | 'external'; 66 | message: ABIReceiverMessage; 67 | } 68 | 69 | export type ContractABI = { 70 | name?: Maybe, 71 | types?: Maybe, 72 | errors?: Maybe<{ [key: number]: ABIError }>, 73 | getters?: Maybe; 74 | receivers?: Maybe; 75 | }; -------------------------------------------------------------------------------- /src/contract/ContractProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Cell } from "../boc/Cell"; 10 | import { SendMode } from "../types/SendMode"; 11 | import { TupleReader } from "../tuple/reader"; 12 | import { TupleItem } from "../tuple/tuple"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { ContractState } from "./ContractState"; 15 | import { Sender } from './Sender'; 16 | import { Contract } from './Contract'; 17 | import { Address } from "../address/Address"; 18 | import { Transaction } from "../types/Transaction"; 19 | import { ExtraCurrency } from '../types/ExtraCurrency'; 20 | import { OpenedContract } from "./openContract"; 21 | 22 | export type ContractGetMethodResult = { 23 | stack: TupleReader; 24 | gasUsed?: Maybe; 25 | logs?: Maybe; 26 | } 27 | 28 | export interface ContractProvider { 29 | getState(): Promise; 30 | get(name: string | number, args: TupleItem[]): Promise; 31 | external(message: Cell): Promise; 32 | internal(via: Sender, args: { value: bigint | string, extracurrency?: ExtraCurrency, bounce?: Maybe, sendMode?: SendMode, body?: Maybe }): Promise; 33 | open(contract: T): OpenedContract; 34 | getTransactions(address: Address, lt: bigint, hash: Buffer, limit?: number): Promise; 35 | } 36 | -------------------------------------------------------------------------------- /src/contract/ContractState.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Maybe } from "../utils/maybe"; 10 | import { ExtraCurrency } from '../types/ExtraCurrency'; 11 | 12 | export type ContractState = { 13 | balance: bigint, 14 | extracurrency: Maybe, 15 | last: { lt: bigint, hash: Buffer } | null, 16 | state: { type: 'uninit' } | { type: 'active', code: Maybe, data: Maybe } | { type: 'frozen', stateHash: Buffer } 17 | }; -------------------------------------------------------------------------------- /src/contract/Sender.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { Cell } from "../boc/Cell"; 11 | import { SendMode } from "../types/SendMode"; 12 | import { StateInit } from "../types/StateInit"; 13 | import { ExtraCurrency } from "../types/ExtraCurrency"; 14 | import { Maybe } from "../utils/maybe"; 15 | 16 | export type SenderArguments = { 17 | value: bigint, 18 | to: Address, 19 | extracurrency?: Maybe, 20 | sendMode?: Maybe, 21 | bounce?: Maybe, 22 | init?: Maybe, 23 | body?: Maybe 24 | } 25 | 26 | export interface Sender { 27 | readonly address?: Address; 28 | send(args: SenderArguments): Promise; 29 | } -------------------------------------------------------------------------------- /src/contract/openContract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { Cell } from "../boc/Cell"; 11 | import { StateInit } from "../types/StateInit"; 12 | import { Contract } from "./Contract"; 13 | import { ContractProvider } from "./ContractProvider"; 14 | 15 | export type OpenedContract = { 16 | [P in keyof F]: P extends `${'get' | 'send' | 'is'}${string}` 17 | ? (F[P] extends (x: ContractProvider, ...args: infer P) => infer R ? (...args: P) => R : never) 18 | : F[P]; 19 | } 20 | 21 | export function openContract(src: T, factory: (params: { address: Address, init: StateInit | null }) => ContractProvider): OpenedContract { 22 | 23 | // Resolve parameters 24 | let address: Address; 25 | let init: StateInit | null = null; 26 | 27 | if (!Address.isAddress(src.address)) { 28 | throw Error('Invalid address'); 29 | } 30 | address = src.address; 31 | if (src.init) { 32 | if (!(src.init.code instanceof Cell)) { 33 | throw Error('Invalid init.code'); 34 | } 35 | if (!(src.init.data instanceof Cell)) { 36 | throw Error('Invalid init.data'); 37 | } 38 | init = src.init; 39 | } 40 | 41 | // Create executor 42 | let executor = factory({ address, init }); 43 | 44 | // Create proxy 45 | return new Proxy(src as any, { 46 | get(target, prop) { 47 | const value = target[prop]; 48 | if (typeof prop === 'string' && (prop.startsWith('get') || prop.startsWith('send') || prop.startsWith('is'))) { 49 | if (typeof value === 'function') { 50 | return (...args: any[]) => value.apply(target, [executor, ...args]); 51 | } 52 | } 53 | return value; 54 | } 55 | }) as OpenedContract; 56 | } 57 | -------------------------------------------------------------------------------- /src/crypto/safeSign.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { sha256_sync, sign, signVerify } from "@ton/crypto"; 10 | import { Cell } from "../boc/Cell"; 11 | 12 | const MIN_SEED_LENGTH = 8; 13 | const MAX_SEED_LENGTH = 64; 14 | 15 | function createSafeSignHash(cell: Cell, seed: string) { 16 | let seedData = Buffer.from(seed); 17 | if (seedData.length > MAX_SEED_LENGTH) { 18 | throw Error('Seed can\t be longer than 64 bytes'); 19 | } 20 | if (seedData.length < MIN_SEED_LENGTH) { 21 | throw Error('Seed must be at least 8 bytes'); 22 | } 23 | return sha256_sync(Buffer.concat([Buffer.from([0xff, 0xff]), seedData, cell.hash()])); 24 | } 25 | 26 | export function safeSign(cell: Cell, secretKey: Buffer, seed: string = 'ton-safe-sign-magic') { 27 | return sign(createSafeSignHash(cell, seed), secretKey); 28 | } 29 | 30 | export function safeSignVerify(cell: Cell, signature: Buffer, publicKey: Buffer, seed: string = 'ton-safe-sign-magic') { 31 | return signVerify(createSafeSignHash(cell, seed), signature, publicKey); 32 | } -------------------------------------------------------------------------------- /src/dict/__testdata__/empty_value.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ton-org/ton-core/1860a37a4d27a7a7ce52ccc91618b534595097fa/src/dict/__testdata__/empty_value.boc -------------------------------------------------------------------------------- /src/dict/generateMerkleProof.ts: -------------------------------------------------------------------------------- 1 | import { beginCell } from '../boc/Builder'; 2 | import { Cell } from '../boc/Cell'; 3 | import { Slice } from '../boc/Slice'; 4 | import { DictionaryKeyTypes, Dictionary, DictionaryKey } from './Dictionary'; 5 | import { readUnaryLength } from './utils/readUnaryLength'; 6 | import { convertToMerkleProof } from '../boc/cell/exoticMerkleProof'; 7 | 8 | function convertToPrunedBranch(c: Cell): Cell { 9 | return beginCell() 10 | .storeUint(1, 8) 11 | .storeUint(1, 8) 12 | .storeBuffer(c.hash(0)) 13 | .storeUint(c.depth(0), 16) 14 | .endCell({ exotic: true }); 15 | } 16 | 17 | function doGenerateMerkleProof( 18 | prefix: string, 19 | slice: Slice, 20 | n: number, 21 | keys: string[] 22 | ): Cell { 23 | // Reading label 24 | const originalCell = slice.asCell(); 25 | 26 | if (keys.length == 0) { 27 | // no keys to prove, prune the whole subdict 28 | return convertToPrunedBranch(originalCell); 29 | } 30 | 31 | let lb0 = slice.loadBit() ? 1 : 0; 32 | let prefixLength = 0; 33 | let pp = prefix; 34 | 35 | if (lb0 === 0) { 36 | // Short label detected 37 | 38 | // Read 39 | prefixLength = readUnaryLength(slice); 40 | 41 | // Read prefix 42 | for (let i = 0; i < prefixLength; i++) { 43 | pp += slice.loadBit() ? '1' : '0'; 44 | } 45 | } else { 46 | let lb1 = slice.loadBit() ? 1 : 0; 47 | if (lb1 === 0) { 48 | // Long label detected 49 | prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); 50 | for (let i = 0; i < prefixLength; i++) { 51 | pp += slice.loadBit() ? '1' : '0'; 52 | } 53 | } else { 54 | // Same label detected 55 | let bit = slice.loadBit() ? '1' : '0'; 56 | prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); 57 | for (let i = 0; i < prefixLength; i++) { 58 | pp += bit; 59 | } 60 | } 61 | } 62 | 63 | if (n - prefixLength === 0) { 64 | return originalCell; 65 | } else { 66 | let sl = originalCell.beginParse(); 67 | let left = sl.loadRef(); 68 | let right = sl.loadRef(); 69 | // NOTE: Left and right branches are implicitly contain prefixes '0' and '1' 70 | if (!left.isExotic) { 71 | const leftKeys = keys.filter((key) => { 72 | return pp + '0' === key.slice(0, pp.length + 1); 73 | }); 74 | left = doGenerateMerkleProof( 75 | pp + '0', 76 | left.beginParse(), 77 | n - prefixLength - 1, 78 | leftKeys 79 | ); 80 | } 81 | if (!right.isExotic) { 82 | const rightKeys = keys.filter((key) => { 83 | return pp + '1' === key.slice(0, pp.length + 1); 84 | }); 85 | right = doGenerateMerkleProof( 86 | pp + '1', 87 | right.beginParse(), 88 | n - prefixLength - 1, 89 | rightKeys 90 | ); 91 | } 92 | 93 | return beginCell() 94 | .storeSlice(sl) 95 | .storeRef(left) 96 | .storeRef(right) 97 | .endCell(); 98 | } 99 | } 100 | 101 | export function generateMerkleProofDirect( 102 | dict: Dictionary, 103 | keys: K[], 104 | keyObject: DictionaryKey 105 | ): Cell { 106 | keys.forEach((key) => { 107 | if (!dict.has(key)) { 108 | throw new Error(`Trying to generate merkle proof for a missing key "${key}"`) 109 | } 110 | }) 111 | const s = beginCell().storeDictDirect(dict).asSlice(); 112 | return doGenerateMerkleProof( 113 | '', 114 | s, 115 | keyObject.bits, 116 | keys.map((key) => keyObject.serialize(key).toString(2).padStart(keyObject.bits, '0')) 117 | ); 118 | } 119 | 120 | export function generateMerkleProof( 121 | dict: Dictionary, 122 | keys: K[], 123 | keyObject: DictionaryKey 124 | ): Cell { 125 | return convertToMerkleProof(generateMerkleProofDirect(dict, keys, keyObject)); 126 | } 127 | -------------------------------------------------------------------------------- /src/dict/generateMerkleUpdate.ts: -------------------------------------------------------------------------------- 1 | import { beginCell } from '../boc/Builder'; 2 | import { Cell } from '../boc/Cell'; 3 | import { DictionaryKeyTypes, Dictionary, DictionaryKey } from './Dictionary'; 4 | import { generateMerkleProof } from './generateMerkleProof'; 5 | 6 | function convertToMerkleUpdate(c1: Cell, c2: Cell): Cell { 7 | return beginCell() 8 | .storeUint(4, 8) 9 | .storeBuffer(c1.hash(0)) 10 | .storeBuffer(c2.hash(0)) 11 | .storeUint(c1.depth(0), 16) 12 | .storeUint(c2.depth(0), 16) 13 | .storeRef(c1) 14 | .storeRef(c2) 15 | .endCell({ exotic: true }); 16 | } 17 | 18 | export function generateMerkleUpdate( 19 | dict: Dictionary, 20 | key: K, 21 | keyObject: DictionaryKey, 22 | newValue: V 23 | ): Cell { 24 | const oldProof = generateMerkleProof(dict, [key], keyObject).refs[0]; 25 | dict.set(key, newValue); 26 | const newProof = generateMerkleProof(dict, [key], keyObject).refs[0]; 27 | return convertToMerkleUpdate(oldProof, newProof); 28 | } 29 | -------------------------------------------------------------------------------- /src/dict/parseDict.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { parseDict } from "./parseDict"; 12 | 13 | function storeBits(builder: Builder, src: string) { 14 | for (let s of src) { 15 | if (s === '0') { 16 | builder.storeBit(0); 17 | } else { 18 | builder.storeBit(1); 19 | } 20 | } 21 | return builder; 22 | } 23 | 24 | describe('parseDict', () => { 25 | it('should parse the one from documentation', () => { 26 | let root = storeBits(beginCell(), '11001000') 27 | .storeRef(storeBits(beginCell(), '011000') 28 | .storeRef(storeBits(beginCell(), '1010011010000000010101001')) 29 | .storeRef(storeBits(beginCell(), '1010000010000000100100001')) 30 | ) 31 | .storeRef(storeBits(beginCell(), '1011111011111101111100100001')) 32 | .endCell(); 33 | 34 | let loaded = parseDict(root.beginParse(), 16, (src) => src); 35 | expect(loaded.get(13n)!.loadUint(16)).toBe(169); 36 | expect(loaded.get(17n)!.loadUint(16)).toBe(289); 37 | expect(loaded.get(239n)!.loadUint(16)).toBe(57121); 38 | }); 39 | 40 | it('should parse with single node', () => { 41 | let root = beginCell() 42 | .storeBuffer(Buffer.from('a01f6e01b8f0a32c242ce41087ffee755406d9bcf9059a75e6b28d4af2a8250b73a8ee6b2800', 'hex')) 43 | .endCell(); 44 | let loaded = parseDict(root.beginParse(), 256, (src) => src); 45 | expect(loaded).toMatchSnapshot(); 46 | }); 47 | 48 | it('should parse dict with exotics', () => { 49 | const dict = Buffer.from('te6cckECMQEABTYAAhOCCc7v0txVpwSwAQIDEwEE53fpbirTglgEAwIISAEBs+lknRDMs3k2joGjp+jknI61P2rMabC6L/qACC9w7jkAAQhIAQGcUdBjRLK2XTGk56evuoGpCTwBOhaNJ3gUFm8TAe0n5QAyAxMBAye9rIc4cIC4MAYFCEgBAW8xXyW0o5rBLIX+pOz+eoPl5Z0fBZeD+gw+8nlzCIBhAAADEwEBQI3GZN/+jNgvBwgDEwEATAHi3hq0fjgKCQgISAEB7X4mvTbvptXZtPaqq5gTrwdCqEJEl390/UB0ycmJCL4AAAhIAQH6TA1tA7w5MqlUZE/iIYZlmhFY/0nMfG9YEH4IA4oG9AAmAxEA/JVwvbaVd7guDAsISAEB16y7YCM4yG1hDzXPs2L9dvwYsYEkdrb8qZoGeOZl/PUAAAIRAOC+0jznnr0oLQ0CEQDgeIc2I3nB6A8OCEgBAe5bbwbC8lILAkcW7BvTGqfH7ackw/xrJ+4xJ9g0lay7ACICDwDcWf0tZ7wILBACDwDPXe4GVoRIEhEISAEBpqnx4FY+VMV5fZOCgk11aYemGilh+4jfDQXGfVnuO2QAHwIPAMwQLzuW7ygrEwIPAMGD4CnDLugVFAhIAQHhkmjGsVW1E//8jS7VtFUP/nG+13eBz2DH8b6lkRkfowATAg8AwL3BnpH5iCoWAg8AwFv9OrsVqBgXCEgBAUnJQzkhkkoQxP70VqQNlWC2ClDLq6thxGgnH+VDOUIdABICDwDAI9NSaezIKRkCDQC0EKoS2YgbGghIAQEUW2BGLrIxcR0xUglz9exM5sN90zhfxgdRBW0FkjOQBQAPAg0ApaMONrGIHRwISAEB20MCSueWmfetSar0Li+5Q8Ip7t01JoPqgAJhVAvv4PEADAINAKFiMLr7SCgeAg0AoV/mfAxIJx8CDQCgRMc88eghIAhIAQHFrdac2QaoB3A0l38UmVSRNUC4pYwh2FyGJ5Vl+MyhJgAJAgkAbRF4aCYiAgkAZzzR6CQjCEgBAax1zsp9u3C3amDOdSJD9mQdDCqhiDj+ZgliaFHwLgWgAAIBj7rR38jjeSour5CAiFzP5jBGI24SWg7B0O37+3W43agB1e+xhmG5Sli1Zv3ZU7LzkGnD7l80gqgY14NkUTcQu0YAAC5TRp8sgyUISAEBnuyDAqn2pYKXQ/wsg9nT0mXlzYlz/XD92d92SJgRWv8AAQhIAQHEi2bfoY75oVrUbsi8DucSPG1oWEcd3KHUGYp0M+RQMQACCEgBAWJ+cuHH5x28R42OXHVN6a1Jf7VXJHmTxS88Gp/rZs2wAAIISAEBOz2DLgJl9RiydhyAtaSVoJad3MYUNnANbILzFnfSgEkADQhIAQHMnAtnlPhbBcz2DH0IRfSKuD1YfeBVgFaujjkW+iHqDgAPCEgBAVyyY7T30fyOwTmieJ8TthLedj5loRH+lxKqP5GbivrBABcISAEBuusLuLgfE7diC4/aes/bPk0SUgkgruJUU+h2pBoayZwAFwhIAQFmUFOfgo2uUi6cXR4FvV6TYWbPc7i/d5MmEQOA4FGbgQAbCEgBAV7HcGhkdhUswzXpHOx7WG6dvrqJjJdLMlC+kTQaJBqhACMISAEBG14jhyXXJ7RLRZCnKyrvsoR9OsBOvnfdOdK6ADqrS88AIQhIAQFZ+0nqF8AuEf14qz7cSjcXQjasTFC5jvk1aLELPzNMHAAnCEgBAdzBkxtJradUwhDmKe2fCZCcreVUbqwio3hW3tN6N2dBAhTWF3cC', 'base64'); 50 | const cs = Cell.fromBoc(dict)[0].beginParse(); 51 | let loaded = parseDict(cs.loadRef().beginParse(), 256, (src) => src); 52 | expect(loaded).toMatchSnapshot(); 53 | }); 54 | }); -------------------------------------------------------------------------------- /src/dict/parseDict.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Slice } from "../boc/Slice"; 10 | 11 | function readUnaryLength(slice: Slice) { 12 | let res = 0; 13 | while (slice.loadBit()) { 14 | res++; 15 | } 16 | return res; 17 | } 18 | 19 | function doParse(prefix: string, slice: Slice, n: number, res: Map, extractor: (src: Slice) => V) { 20 | 21 | // Reading label 22 | let lb0 = slice.loadBit() ? 1 : 0; 23 | let prefixLength = 0; 24 | let pp = prefix; 25 | 26 | if (lb0 === 0) { 27 | // Short label detected 28 | 29 | // Read 30 | prefixLength = readUnaryLength(slice); 31 | 32 | // Read prefix 33 | for (let i = 0; i < prefixLength; i++) { 34 | pp += slice.loadBit() ? '1' : '0'; 35 | } 36 | } else { 37 | let lb1 = slice.loadBit() ? 1 : 0; 38 | if (lb1 === 0) { 39 | // Long label detected 40 | prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); 41 | for (let i = 0; i < prefixLength; i++) { 42 | pp += slice.loadBit() ? '1' : '0'; 43 | } 44 | } else { 45 | // Same label detected 46 | let bit = slice.loadBit() ? '1' : '0'; 47 | prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); 48 | for (let i = 0; i < prefixLength; i++) { 49 | pp += bit; 50 | } 51 | } 52 | } 53 | 54 | if (n - prefixLength === 0) { 55 | res.set(BigInt('0b' + pp), extractor(slice)); 56 | } else { 57 | let left = slice.loadRef(); 58 | let right = slice.loadRef(); 59 | // NOTE: Left and right branches are implicitly contain prefixes '0' and '1' 60 | if (!left.isExotic) { 61 | doParse(pp + '0', left.beginParse(), n - prefixLength - 1, res, extractor); 62 | } 63 | if (!right.isExotic) { 64 | doParse(pp + '1', right.beginParse(), n - prefixLength - 1, res, extractor); 65 | } 66 | } 67 | } 68 | 69 | export function parseDict(sc: Slice | null, keySize: number, extractor: (src: Slice) => V) { 70 | let res: Map = new Map(); 71 | if (sc) { 72 | doParse('', sc, keySize, res, extractor); 73 | } 74 | return res; 75 | } -------------------------------------------------------------------------------- /src/dict/serializeDict.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { serializeDict } from "./serializeDict"; 11 | 12 | describe('serializeDict', () => { 13 | it('should build prefix tree', () => { 14 | 15 | // From docs 16 | const map = new Map(); 17 | map.set(13n, 169n); 18 | map.set(17n, 289n); 19 | map.set(239n, 57121n); 20 | 21 | // Test serialization 22 | let builder = beginCell(); 23 | serializeDict(map, 16, (src, cell) => cell.storeUint(src, 16), builder); 24 | let root = builder.endCell(); 25 | expect(root).toMatchSnapshot(); 26 | }); 27 | }); -------------------------------------------------------------------------------- /src/dict/utils/findCommonPrefix.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { findCommonPrefix } from "./findCommonPrefix"; 10 | 11 | describe('findCommonPrefix', () => { 12 | it('should find common prefix', () => { 13 | expect(findCommonPrefix([ 14 | '0000111', 15 | '0101111', 16 | '0001111' 17 | ])).toEqual('0'); 18 | expect(findCommonPrefix([ 19 | '0000111', 20 | '0001111', 21 | '0000101' 22 | ])).toEqual('000'); 23 | expect(findCommonPrefix([ 24 | '0000111', 25 | '1001111', 26 | '0000101' 27 | ])).toEqual(''); 28 | }) 29 | }); -------------------------------------------------------------------------------- /src/dict/utils/findCommonPrefix.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export function findCommonPrefix(src: string[], startPos = 0) { 10 | 11 | // Corner cases 12 | if (src.length === 0) { 13 | return ''; 14 | } 15 | 16 | let r = src[0].slice(startPos); 17 | 18 | for (let i = 1; i < src.length; i++) { 19 | const s = src[i]; 20 | while (s.indexOf(r, startPos) !== startPos) { 21 | r = r.substring(0, r.length - 1); 22 | 23 | if (r === '') { 24 | return r; 25 | } 26 | } 27 | } 28 | 29 | return r; 30 | } -------------------------------------------------------------------------------- /src/dict/utils/internalKeySerializer.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../../address/Address"; 10 | import { BitString } from "../../boc/BitString"; 11 | import { testAddress } from "../../utils/testAddress"; 12 | import { deserializeInternalKey, serializeInternalKey } from "./internalKeySerializer"; 13 | 14 | describe('internalKeySerializer', () => { 15 | it('should serialize numbers', () => { 16 | let cs = [0, -1, 1, 123123123, -123123123]; 17 | for (let c of cs) { 18 | expect(deserializeInternalKey(serializeInternalKey(c))).toBe(c); 19 | } 20 | }); 21 | it('should serialize bignumbers', () => { 22 | let cs = [0n, -1n, 1n, 123123123n, -123123123n, 1231231231231237812683128376123n, -1231273612873681263871263871263n]; 23 | for (let c of cs) { 24 | expect(deserializeInternalKey(serializeInternalKey(c))).toBe(c); 25 | } 26 | }); 27 | it('should serialize addresses', () => { 28 | let cs = [testAddress(0, '1'), testAddress(-1, '1'), testAddress(0, '2'), testAddress(0, '4')]; 29 | for (let c of cs) { 30 | expect((deserializeInternalKey(serializeInternalKey(c)) as Address).equals(c)).toBe(true); 31 | } 32 | }); 33 | it('should serialize buffers', () => { 34 | let cs = [Buffer.from('00', 'hex'), Buffer.from('ff', 'hex'), Buffer.from('0f', 'hex'), Buffer.from('0f000011002233456611', 'hex')]; 35 | for (let c of cs) { 36 | expect((deserializeInternalKey(serializeInternalKey(c)) as Buffer).equals(c)).toBe(true); 37 | } 38 | }); 39 | it('should serialize bit strings', () => { 40 | let cs = [Buffer.from('00', 'hex'), Buffer.from('ff', 'hex'), Buffer.from('0f', 'hex'), Buffer.from('0f000011002233456611', 'hex')]; 41 | for (let c of cs) { 42 | for(let i = 0; i < c.length * 8 - 1; i++) { 43 | let bs = new BitString(c, 0, c.length * 8 - i); 44 | const res = deserializeInternalKey(serializeInternalKey(bs)) as BitString; 45 | expect(res.equals(bs)).toBe(true); 46 | } 47 | } 48 | }) 49 | }); 50 | -------------------------------------------------------------------------------- /src/dict/utils/internalKeySerializer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../../address/Address"; 10 | import { BitString } from "../../boc/BitString"; 11 | import { bitsToPaddedBuffer, paddedBufferToBits } from "../../boc/utils/paddedBits"; 12 | 13 | export function serializeInternalKey(value: any): string { 14 | if (typeof value === 'number') { 15 | if (!Number.isSafeInteger(value)) { 16 | throw Error('Invalid key type: not a safe integer: ' + value); 17 | } 18 | return 'n:' + value.toString(10); 19 | } else if (typeof value === 'bigint') { 20 | return 'b:' + value.toString(10); 21 | } else if (Address.isAddress(value)) { 22 | return 'a:' + value.toString(); 23 | } else if (Buffer.isBuffer(value)) { 24 | return 'f:' + value.toString('hex'); 25 | } else if(BitString.isBitString(value)) { 26 | return 'B:' + value.toString(); 27 | } else { 28 | throw Error('Invalid key type'); 29 | } 30 | } 31 | 32 | export function deserializeInternalKey(value: string): any { 33 | let k = value.slice(0, 2); 34 | let v = value.slice(2); 35 | if (k === 'n:') { 36 | return parseInt(v, 10); 37 | } else if (k === 'b:') { 38 | return BigInt(v); 39 | } else if (k === 'a:') { 40 | return Address.parse(v); 41 | } else if (k === 'f:') { 42 | return Buffer.from(v, 'hex'); 43 | } 44 | else if (k === 'B:') { 45 | 46 | const lastDash = v.slice(-1) == "_"; 47 | const isPadded = lastDash || v.length % 2 != 0; 48 | if(isPadded) { 49 | let charLen = lastDash ? v.length - 1 : v.length; 50 | const padded = v.substr(0, charLen) + "0"; //Padding 51 | if((!lastDash) && ((charLen & 1) !== 0)){ 52 | // Four bit nibmle without padding 53 | return new BitString(Buffer.from(padded, 'hex'), 0, charLen << 2); 54 | } 55 | else { 56 | return paddedBufferToBits(Buffer.from(padded, 'hex')); 57 | } 58 | } 59 | else { 60 | return new BitString(Buffer.from(v, 'hex'), 0, v.length << 2); 61 | } 62 | } 63 | throw Error('Invalid key type: ' + k); 64 | } 65 | -------------------------------------------------------------------------------- /src/dict/utils/readUnaryLength.ts: -------------------------------------------------------------------------------- 1 | import { Slice } from '../../boc/Slice'; 2 | 3 | export function readUnaryLength(slice: Slice) { 4 | let res = 0; 5 | while (slice.loadBit()) { 6 | res++; 7 | } 8 | return res; 9 | } 10 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // Address 10 | export { Address, address } from './address/Address'; 11 | export { ExternalAddress } from './address/ExternalAddress'; 12 | export { ADNLAddress } from './address/ADNLAddress'; 13 | export { contractAddress } from './address/contractAddress'; 14 | 15 | // BitString 16 | export { BitString } from './boc/BitString'; 17 | export { BitReader } from './boc/BitReader'; 18 | export { BitBuilder } from './boc/BitBuilder'; 19 | 20 | // Cell 21 | export { Builder, beginCell } from './boc/Builder'; 22 | export { Slice } from './boc/Slice'; 23 | export { CellType } from './boc/CellType'; 24 | export { Cell } from './boc/Cell'; 25 | export { Writable } from './boc/Writable'; 26 | 27 | // Dict 28 | export { Dictionary, DictionaryKey, DictionaryKeyTypes, DictionaryValue } from './dict/Dictionary'; 29 | 30 | // Exotics 31 | export { exoticMerkleProof, convertToMerkleProof } from './boc/cell/exoticMerkleProof'; 32 | export { exoticMerkleUpdate } from './boc/cell/exoticMerkleUpdate'; 33 | export { exoticPruned } from './boc/cell/exoticPruned'; 34 | 35 | // Merkle trees 36 | export { generateMerkleProof, generateMerkleProofDirect } from './dict/generateMerkleProof' 37 | export { generateMerkleUpdate } from './dict/generateMerkleUpdate' 38 | 39 | // Tuples 40 | export { Tuple, TupleItem, TupleItemNull, TupleItemInt, TupleItemNaN, TupleItemCell, TupleItemSlice, TupleItemBuilder } from './tuple/tuple'; 41 | export { parseTuple, serializeTuple } from './tuple/tuple'; 42 | export { TupleReader } from './tuple/reader'; 43 | export { TupleBuilder } from './tuple/builder'; 44 | 45 | // Types 46 | export * from './types/_export'; 47 | 48 | // Contract 49 | export { Contract } from './contract/Contract'; 50 | export { ContractProvider, ContractGetMethodResult } from './contract/ContractProvider'; 51 | export { ContractState } from './contract/ContractState'; 52 | export { Sender, SenderArguments } from './contract/Sender'; 53 | export { openContract, OpenedContract } from './contract/openContract'; 54 | export { ComputeError } from './contract/ComputeError'; 55 | export { 56 | ContractABI, 57 | ABIError, 58 | ABITypeRef, 59 | ABIField, 60 | ABIArgument, 61 | ABIGetter, 62 | ABIType, 63 | ABIReceiverMessage, 64 | ABIReceiver 65 | } from './contract/ContractABI'; 66 | 67 | // Utility 68 | export { toNano, fromNano } from './utils/convert'; 69 | export { crc16 } from './utils/crc16'; 70 | export { crc32c } from './utils/crc32c'; 71 | export { base32Decode, base32Encode } from './utils/base32'; 72 | export { getMethodId } from './utils/getMethodId'; 73 | 74 | // Crypto 75 | export { safeSign, safeSignVerify } from './crypto/safeSign'; 76 | -------------------------------------------------------------------------------- /src/tuple/builder.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { beginCell } from "../boc/Builder"; 11 | import { Cell } from "../boc/Cell"; 12 | import { Slice } from "../boc/Slice"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { TupleItem } from "./tuple"; 15 | 16 | export class TupleBuilder { 17 | private _tuple: TupleItem[] = []; 18 | 19 | writeNumber(v?: Maybe) { 20 | if (v === null || v === undefined) { 21 | this._tuple.push({ type: 'null' }); 22 | } else { 23 | this._tuple.push({ type: 'int', value: BigInt(v) }); 24 | } 25 | } 26 | 27 | writeBoolean(v?: Maybe) { 28 | if (v === null || v === undefined) { 29 | this._tuple.push({ type: 'null' }); 30 | } else { 31 | this._tuple.push({ type: 'int', value: v ? -1n : 0n }); 32 | } 33 | } 34 | 35 | writeBuffer(v?: Maybe) { 36 | if (v === null || v === undefined) { 37 | this._tuple.push({ type: 'null' }); 38 | } else { 39 | this._tuple.push({ type: 'slice', cell: beginCell().storeBuffer(v).endCell() }); 40 | } 41 | } 42 | 43 | writeString(v?: Maybe) { 44 | if (v === null || v === undefined) { 45 | this._tuple.push({ type: 'null' }); 46 | } else { 47 | this._tuple.push({ type: 'slice', cell: beginCell().storeStringTail(v).endCell() }); 48 | } 49 | } 50 | 51 | writeCell(v?: Maybe) { 52 | if (v === null || v === undefined) { 53 | this._tuple.push({ type: 'null' }); 54 | } else { 55 | if (v instanceof Cell) { 56 | this._tuple.push({ type: 'cell', cell: v }); 57 | } else if (v instanceof Slice) { 58 | this._tuple.push({ type: 'cell', cell: v.asCell() }); 59 | } 60 | } 61 | } 62 | 63 | writeSlice(v?: Maybe) { 64 | if (v === null || v === undefined) { 65 | this._tuple.push({ type: 'null' }); 66 | } else { 67 | if (v instanceof Cell) { 68 | this._tuple.push({ type: 'slice', cell: v }); 69 | } else if (v instanceof Slice) { 70 | this._tuple.push({ type: 'slice', cell: v.asCell() }); 71 | } 72 | } 73 | } 74 | 75 | writeBuilder(v?: Maybe) { 76 | if (v === null || v === undefined) { 77 | this._tuple.push({ type: 'null' }); 78 | } else { 79 | if (v instanceof Cell) { 80 | this._tuple.push({ type: 'builder', cell: v }); 81 | } else if (v instanceof Slice) { 82 | this._tuple.push({ type: 'builder', cell: v.asCell() }); 83 | } 84 | } 85 | } 86 | 87 | writeTuple(v?: Maybe) { 88 | if (v === null || v === undefined) { 89 | this._tuple.push({ type: 'null' }); 90 | } else { 91 | this._tuple.push({ type: 'tuple', items: v }); 92 | } 93 | } 94 | 95 | writeAddress(v?: Maybe
) { 96 | if (v === null || v === undefined) { 97 | this._tuple.push({ type: 'null' }); 98 | } else { 99 | this._tuple.push({ type: 'slice', cell: beginCell().storeAddress(v).endCell() }); 100 | } 101 | } 102 | 103 | build() { 104 | return [...this._tuple]; 105 | } 106 | } -------------------------------------------------------------------------------- /src/tuple/reader.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { TupleReader } from "./reader"; 10 | import { TupleItem } from "./tuple"; 11 | import fs from 'fs'; 12 | 13 | describe('tuple', () => { 14 | it('should read cons', () => { 15 | const cons: TupleItem[] = [ 16 | { 17 | "type": "tuple", 18 | "items": [ 19 | { "type": "int", "value": BigInt(1) }, 20 | { 21 | "type": "tuple", 22 | "items": [ 23 | { "type": "int", "value": BigInt(2) }, 24 | { 25 | "type": "tuple", 26 | "items": [ 27 | { "type": "int", "value": BigInt(3) }, 28 | { "type": "null" } 29 | ] 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | const r = new TupleReader(cons); 37 | 38 | const items: TupleItem[] = [ 39 | { 40 | "type": "int", 41 | "value": BigInt(1) 42 | }, 43 | { 44 | "type": "int", 45 | "value": BigInt(2) 46 | }, 47 | { 48 | "type": "int", 49 | "value": BigInt(3) 50 | }, 51 | ] 52 | 53 | expect(r.readLispList()).toEqual(items); 54 | }); 55 | 56 | it('should read ultra deep cons', () => { 57 | let fContent = fs.readFileSync('./src/tuple/ultra_deep_cons.json'); 58 | const cons: TupleItem[] = JSON.parse(fContent.toString()); 59 | 60 | const result = []; 61 | for (let index = 0; index < 187; index++) { 62 | if (![11,82,116,154].includes(index)) { 63 | result.push({"type":"int","value": index.toString()}) 64 | } 65 | } 66 | 67 | expect(new TupleReader(cons).readLispList()).toEqual(result); 68 | }); 69 | 70 | it('should raise error on nontuple element in chain', () => { 71 | 72 | const cons: TupleItem[] = [ 73 | { 74 | "type": "int", 75 | "value": BigInt(1) 76 | } 77 | ]; 78 | 79 | const r = new TupleReader(cons); 80 | expect(() => r.readLispListDirect()).toThrowError('Lisp list consists only from (any, tuple) elements'); 81 | }); 82 | 83 | it('should return empty list if tuple is null', () => { 84 | const cons: TupleItem[] = [ 85 | { 86 | type: 'null' 87 | } 88 | ]; 89 | 90 | let r = new TupleReader(cons); 91 | expect(r.readLispList()).toEqual([]); 92 | 93 | r = new TupleReader(cons); 94 | expect(r.readLispListDirect()).toEqual([]); 95 | }); 96 | }) -------------------------------------------------------------------------------- /src/tuple/tuple.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { beginCell } from "../boc/Builder"; 11 | import { Cell } from "../boc/Cell"; 12 | import { parseTuple, serializeTuple } from "./tuple"; 13 | 14 | describe('tuple', () => { 15 | it('should serialize tuple with numbers', () => { 16 | let serialized = serializeTuple([{ 17 | "type": "int", "value": BigInt("-1") 18 | }, { 19 | "type": "int", "value": BigInt("-1") 20 | }, { 21 | "type": "int", "value": BigInt("49800000000") 22 | }, { 23 | "type": "int", "value": BigInt("100000000") 24 | }, { 25 | "type": "int", "value": BigInt("100000000") 26 | }, { 27 | "type": "int", "value": BigInt("2500") 28 | }, { 29 | "type": "int", "value": BigInt("100000000") 30 | }]); 31 | expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual('te6ccgEBCAEAWQABGAAABwEAAAAABfXhAAEBEgEAAAAAAAAJxAIBEgEAAAAABfXhAAMBEgEAAAAABfXhAAQBEgEAAAALmE+yAAUBEgH//////////wYBEgH//////////wcAAA=='); 32 | }); 33 | 34 | it('should serialize tuple long numbers', () => { 35 | const golden = 'te6ccgEBAgEAKgABSgAAAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqt4e0IsLXV0BAAA='; 36 | let serialized = serializeTuple([ 37 | { 38 | "type": "int", "value": BigInt("12312312312312323421") 39 | } 40 | ]); 41 | expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual(golden); 42 | }); 43 | 44 | it('should serialize slices', () => { 45 | const golden = 'te6ccgEBAwEAHwACDwAAAQQAB0AgAQIAAAAd4GEghEZ4iF1r9AWzyJs4'; 46 | let serialized = serializeTuple([ 47 | { 48 | "type": "slice", "cell": beginCell().storeCoins(BigInt("123123123123123234211234123123123")).endCell() 49 | } 50 | ]); 51 | expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual(golden); 52 | }); 53 | 54 | it('should serialize address', () => { 55 | const golden = 'te6ccgEBAwEAMgACDwAAAQQAELAgAQIAAABDn_k3QjSzAxvCFAxl2WAXIYvKOdG_BD9NlNG8vx1vw1C00A'; 56 | let serialized = serializeTuple([ 57 | { 58 | "type": "slice", "cell": beginCell().storeAddress(Address.parse('kf_JuhGlmBjeEKBjLssAuQxeUc6N-CH6bKaN5fjrfhqFpqVQ')).endCell() 59 | } 60 | ]); 61 | expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64url')).toEqual(golden); 62 | }); 63 | 64 | it('should serialize int', () => { 65 | const golden = 'te6ccgEBAgEAKgABSgAAAQIAyboRpZgY3hCgYy7LALkMXlHOjfgh+mymjeX4634ahaYBAAA='; 66 | let serialized = serializeTuple([ 67 | { 68 | "type": "int", "value": BigInt('91243637913382117273357363328745502088904016167292989471764554225637796775334') 69 | } 70 | ]); 71 | expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual(golden); 72 | }); 73 | 74 | it('should serialize tuples', () => { 75 | let golden = 'te6ccgEBEAEAjgADDAAABwcABAEIDQESAf//////////AgESAQAAAAAAAAADAwESAQAAAAAAAAACBAESAQAAAAAAAAABBQECAAYBEgEAAAAAAAAAAQcAAAIACQwCAAoLABIBAAAAAAAAAHsAEgEAAAAAAAHimQECAw8BBgcAAQ4BCQQAB0AgDwAd4GEghEZ4iF1r9AWzyJs4'; 76 | const st = parseTuple(Cell.fromBoc(Buffer.from(golden, 'base64'))[0]); 77 | let gs = serializeTuple(st); 78 | // console.warn(inspect(parseStack(gs), false, null, true)); 79 | // console.warn(inspect(st, false, null, true)); 80 | expect(gs.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual(golden); 81 | // let serialized = serializeStack([ 82 | // { 83 | // type: 'int', value: new BN(1) 84 | // }, 85 | // { 86 | // type: 'null' 87 | // }, 88 | // { 89 | // type: 'int', value: new BN(1) 90 | // }, 91 | // { 92 | // type: 'int', value: new BN(2) 93 | // }, 94 | // { 95 | // type: 'int', value: new BN(3) 96 | // }, 97 | // { 98 | // type: 'int', value: new BN(-1) 99 | // }, 100 | // ]); 101 | // expect(serialized.toBoc({ idx: false, crc32: false }).toString('base64')).toEqual(golden); 102 | }) 103 | 104 | it('should parse large tuple from emulator', () => { 105 | let boc = 'te6cckECCAEAAwgAAg8AAAEEAD+AYAECAAAB/tC/0YDQuNCy0LXRgiDQvNC40YAg8J+RgCDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LgDAf7QstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIgBAH+0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIAUB/vCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9EGAf6A0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC1BwDc0YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYDQv9GA0LjQstC10YIg0LzQuNGAIPCfkYBG2Y9A'; 106 | let cell = Cell.fromBase64(boc); 107 | parseTuple(cell); 108 | }); 109 | }); -------------------------------------------------------------------------------- /src/types/Account.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address" 10 | import { Builder } from "../boc/Builder" 11 | import { Slice } from "../boc/Slice" 12 | import { AccountStorage, loadAccountStorage, storeAccountStorage } from "./AccountStorage" 13 | import { loadStorageInfo, StorageInfo, storeStorageInfo } from "./StorageInfo" 14 | 15 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L231 16 | // account_none$0 = Account; 17 | // account$1 addr:MsgAddressInt storage_stat:StorageInfo storage:AccountStorage = Account; 18 | 19 | export type Account = { 20 | addr: Address, 21 | storageStats: StorageInfo, 22 | storage: AccountStorage 23 | } 24 | 25 | export function loadAccount(slice: Slice): Account { 26 | return { 27 | addr: slice.loadAddress(), 28 | storageStats: loadStorageInfo(slice), 29 | storage: loadAccountStorage(slice) 30 | }; 31 | } 32 | 33 | export function storeAccount(src: Account) { 34 | return (builder: Builder) => { 35 | builder.storeAddress(src.addr); 36 | builder.store(storeStorageInfo(src.storageStats)); 37 | builder.store(storeAccountStorage(src.storage)); 38 | }; 39 | } -------------------------------------------------------------------------------- /src/types/AccountState.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { loadStateInit, StateInit, storeStateInit } from "./StateInit"; 12 | 13 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L239 14 | // account_uninit$00 = AccountState; 15 | // account_active$1 _:StateInit = AccountState; 16 | // account_frozen$01 state_hash:bits256 = AccountState; 17 | 18 | export type AccountState = 19 | | AccountStateUninit 20 | | AccountStateActive 21 | | AccountStateFrozen; 22 | 23 | export type AccountStateUninit = { type: 'uninit' }; 24 | export type AccountStateActive = { type: 'active', state: StateInit }; 25 | export type AccountStateFrozen = { type: 'frozen', stateHash: bigint }; 26 | 27 | export function loadAccountState(cs: Slice): AccountState { 28 | if (cs.loadBit()) { 29 | return { type: 'active', state: loadStateInit(cs) }; 30 | } else if (cs.loadBit()) { 31 | return { type: 'frozen', stateHash: cs.loadUintBig(256) }; 32 | } else { 33 | return { type: 'uninit' }; 34 | } 35 | } 36 | 37 | export function storeAccountState(src: AccountState) { 38 | return (builder: Builder) => { 39 | if (src.type === 'active') { 40 | builder.storeBit(true); 41 | builder.store(storeStateInit(src.state)); 42 | } else if (src.type === 'frozen') { 43 | builder.storeBit(false); 44 | builder.storeBit(true); 45 | builder.storeUint(src.stateHash, 256); 46 | } else if (src.type === 'uninit') { 47 | builder.storeBit(false); 48 | builder.storeBit(false); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/types/AccountStatus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L243 13 | // acc_state_uninit$00 = AccountStatus; 14 | // acc_state_frozen$01 = AccountStatus; 15 | // acc_state_active$10 = AccountStatus; 16 | // acc_state_nonexist$11 = AccountStatus; 17 | 18 | export type AccountStatus = 'uninitialized' | 'frozen' | 'active' | 'non-existing'; 19 | 20 | /** 21 | * Load account state from slice 22 | * @param slice 23 | * @returns AccountState 24 | */ 25 | export function loadAccountStatus(slice: Slice): AccountStatus { 26 | const status = slice.loadUint(2); 27 | if (status === 0x00) { 28 | return 'uninitialized'; 29 | } 30 | if (status === 0x01) { 31 | return 'frozen'; 32 | } 33 | if (status === 0x02) { 34 | return 'active'; 35 | } 36 | if (status === 0x03) { 37 | return 'non-existing'; 38 | } 39 | throw Error('Invalid data'); 40 | } 41 | 42 | /** 43 | * Store account state to builder 44 | * @param src account state 45 | * @param builder buidler 46 | */ 47 | export function storeAccountStatus(src: AccountStatus) { 48 | return (builder: Builder) => { 49 | if (src === 'uninitialized') { 50 | builder.storeUint(0x00, 2); 51 | } else if (src === 'frozen') { 52 | builder.storeUint(0x01, 2); 53 | } else if (src === 'active') { 54 | builder.storeUint(0x02, 2); 55 | } else if (src === 'non-existing') { 56 | builder.storeUint(0x03, 2); 57 | } else { 58 | throw Error('Invalid data'); 59 | } 60 | return builder; 61 | }; 62 | } -------------------------------------------------------------------------------- /src/types/AccountStatusChange.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // acst_unchanged$0 = AccStatusChange; // x -> x 13 | // acst_frozen$10 = AccStatusChange; // init -> frozen 14 | // acst_deleted$11 = AccStatusChange; // frozen -> deleted 15 | 16 | export type AccountStatusChange = 'unchanged' | 'frozen' | 'deleted'; 17 | 18 | export function loadAccountStatusChange(slice: Slice): AccountStatusChange { 19 | if (!slice.loadBit()) { 20 | return 'unchanged'; 21 | } 22 | if (slice.loadBit()) { 23 | return 'deleted'; 24 | } else { 25 | return 'frozen'; 26 | } 27 | } 28 | 29 | export function storeAccountStatusChange(src: AccountStatusChange) { 30 | return (builder: Builder) => { 31 | if (src == 'unchanged') { 32 | builder.storeBit(0); 33 | } else if (src === 'frozen') { 34 | builder.storeBit(1); 35 | builder.storeBit(0); 36 | } else if (src === 'deleted') { 37 | builder.storeBit(1); 38 | builder.storeBit(1); 39 | } else { 40 | throw Error('Invalid account status change'); 41 | } 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/types/AccountStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { AccountState, loadAccountState, storeAccountState } from "./AccountState"; 12 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L235 15 | // account_storage$_ last_trans_lt:uint64 balance:CurrencyCollection state:AccountState 16 | // = AccountStorage; 17 | 18 | export type AccountStorage = { 19 | lastTransLt: bigint, 20 | balance: CurrencyCollection, 21 | state: AccountState 22 | }; 23 | 24 | export function loadAccountStorage(slice: Slice): AccountStorage { 25 | return { 26 | lastTransLt: slice.loadUintBig(64), 27 | balance: loadCurrencyCollection(slice), 28 | state: loadAccountState(slice) 29 | }; 30 | } 31 | 32 | export function storeAccountStorage(src: AccountStorage) { 33 | return (builder: Builder) => { 34 | builder.storeUint(src.lastTransLt, 64); 35 | builder.store(storeCurrencyCollection(src.balance)); 36 | builder.store(storeAccountState(src.state)); 37 | }; 38 | } -------------------------------------------------------------------------------- /src/types/CommonMessageInfo.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { testAddress, testExternalAddress } from "../utils/testAddress"; 11 | import { CommonMessageInfo, loadCommonMessageInfo, storeCommonMessageInfo } from "./CommonMessageInfo"; 12 | 13 | describe('CommonMessageInfo', () => { 14 | it('should serialize external-in messages', () => { 15 | let msg: CommonMessageInfo = { 16 | type: 'external-in', 17 | src: testExternalAddress('addr-2'), 18 | dest: testAddress(0, 'addr-1'), 19 | importFee: 0n 20 | }; 21 | let cell = beginCell().store(storeCommonMessageInfo(msg)).endCell(); 22 | let msg2 = loadCommonMessageInfo(cell.beginParse()); 23 | let cell2 = beginCell().store(storeCommonMessageInfo(msg2)).endCell(); 24 | expect(cell.equals(cell2)).toBe(true); 25 | }); 26 | }); -------------------------------------------------------------------------------- /src/types/CommonMessageInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { ExternalAddress } from "../address/ExternalAddress"; 11 | import { Builder } from "../boc/Builder"; 12 | import { Slice } from "../boc/Slice"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 15 | 16 | 17 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L123 18 | // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool 19 | // src:MsgAddressInt dest:MsgAddressInt 20 | // value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams 21 | // created_lt:uint64 created_at:uint32 = CommonMsgInfo; 22 | // ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt 23 | // import_fee:Grams = CommonMsgInfo; 24 | // ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt 25 | // created_lt:uint64 created_at:uint32 = CommonMsgInfo; 26 | 27 | export type CommonMessageInfo = 28 | | CommonMessageInfoInternal 29 | | CommonMessageInfoExternalOut 30 | | CommonMessageInfoExternalIn; 31 | 32 | export type CommonMessageInfoInternal = { 33 | type: 'internal', 34 | ihrDisabled: boolean, 35 | bounce: boolean, 36 | bounced: boolean, 37 | src: Address, 38 | dest: Address, 39 | value: CurrencyCollection, 40 | ihrFee: bigint, 41 | forwardFee: bigint, 42 | createdLt: bigint, 43 | createdAt: number 44 | }; 45 | 46 | export type CommonMessageInfoExternalIn = { 47 | type: 'external-in', 48 | src?: Maybe, 49 | dest: Address, 50 | importFee: bigint 51 | }; 52 | 53 | export type CommonMessageInfoExternalOut = { 54 | type: 'external-out', 55 | src: Address, 56 | dest?: Maybe, 57 | createdLt: bigint, 58 | createdAt: number 59 | }; 60 | 61 | export function loadCommonMessageInfo(slice: Slice): CommonMessageInfo { 62 | 63 | // Internal message 64 | if (!slice.loadBit()) { 65 | 66 | const ihrDisabled = slice.loadBit(); 67 | const bounce = slice.loadBit(); 68 | const bounced = slice.loadBit(); 69 | const src = slice.loadAddress(); 70 | const dest = slice.loadAddress(); 71 | const value = loadCurrencyCollection(slice); 72 | const ihrFee = slice.loadCoins(); 73 | const forwardFee = slice.loadCoins(); 74 | const createdLt = slice.loadUintBig(64); 75 | const createdAt = slice.loadUint(32); 76 | 77 | return { 78 | type: 'internal', 79 | ihrDisabled, 80 | bounce, 81 | bounced, 82 | src, 83 | dest, 84 | value, 85 | ihrFee, 86 | forwardFee, 87 | createdLt, 88 | createdAt, 89 | }; 90 | } 91 | 92 | // External In mesage 93 | if (!slice.loadBit()) { 94 | const src = slice.loadMaybeExternalAddress(); 95 | const dest = slice.loadAddress()!; 96 | const importFee = slice.loadCoins(); 97 | 98 | return { 99 | type: 'external-in', 100 | src, 101 | dest, 102 | importFee, 103 | }; 104 | } 105 | 106 | // External Out message 107 | const src = slice.loadAddress()!; 108 | const dest = slice.loadMaybeExternalAddress(); 109 | const createdLt = slice.loadUintBig(64); 110 | const createdAt = slice.loadUint(32); 111 | 112 | return { 113 | type: 'external-out', 114 | src, 115 | dest, 116 | createdLt, 117 | createdAt, 118 | }; 119 | } 120 | 121 | export function storeCommonMessageInfo(source: CommonMessageInfo) { 122 | return (builder: Builder) => { 123 | if (source.type === 'internal') { 124 | builder.storeBit(0); 125 | builder.storeBit(source.ihrDisabled); 126 | builder.storeBit(source.bounce); 127 | builder.storeBit(source.bounced); 128 | builder.storeAddress(source.src); 129 | builder.storeAddress(source.dest); 130 | builder.store(storeCurrencyCollection(source.value)); 131 | builder.storeCoins(source.ihrFee); 132 | builder.storeCoins(source.forwardFee); 133 | builder.storeUint(source.createdLt, 64); 134 | builder.storeUint(source.createdAt, 32); 135 | } else if (source.type === 'external-in') { 136 | builder.storeBit(1); 137 | builder.storeBit(0); 138 | builder.storeAddress(source.src); 139 | builder.storeAddress(source.dest); 140 | builder.storeCoins(source.importFee); 141 | } else if (source.type === 'external-out') { 142 | builder.storeBit(1); 143 | builder.storeBit(1); 144 | builder.storeAddress(source.src); 145 | builder.storeAddress(source.dest); 146 | builder.storeUint(source.createdLt, 64); 147 | builder.storeUint(source.createdAt, 32); 148 | } else { 149 | throw new Error('Unknown CommonMessageInfo type'); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/types/CommonMessageInfoRelaxed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { ExternalAddress } from "../address/ExternalAddress"; 11 | import { Builder } from "../boc/Builder"; 12 | import { Slice } from "../boc/Slice"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 15 | 16 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L132 17 | // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool 18 | // src:MsgAddress dest:MsgAddressInt 19 | // value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams 20 | // created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; 21 | // ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt 22 | // created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; 23 | 24 | export type CommonMessageInfoRelaxed = 25 | | CommonMessageInfoRelaxedInternal 26 | | CommonMessageInfoRelaxedExternalOut; 27 | 28 | export type CommonMessageInfoRelaxedInternal = { 29 | type: 'internal', 30 | ihrDisabled: boolean, 31 | bounce: boolean, 32 | bounced: boolean, 33 | src?: Maybe
, 34 | dest: Address, 35 | value: CurrencyCollection, 36 | ihrFee: bigint, 37 | forwardFee: bigint, 38 | createdLt: bigint, 39 | createdAt: number 40 | }; 41 | 42 | export type CommonMessageInfoRelaxedExternalOut = { 43 | type: 'external-out', 44 | src?: Maybe
, 45 | dest?: Maybe, 46 | createdLt: bigint, 47 | createdAt: number 48 | }; 49 | 50 | export function loadCommonMessageInfoRelaxed(slice: Slice): CommonMessageInfoRelaxed { 51 | 52 | // Internal message 53 | if (!slice.loadBit()) { 54 | 55 | const ihrDisabled = slice.loadBit(); 56 | const bounce = slice.loadBit(); 57 | const bounced = slice.loadBit(); 58 | const src = slice.loadMaybeAddress(); 59 | const dest = slice.loadAddress(); 60 | const value = loadCurrencyCollection(slice); 61 | const ihrFee = slice.loadCoins(); 62 | const forwardFee = slice.loadCoins(); 63 | const createdLt = slice.loadUintBig(64); 64 | const createdAt = slice.loadUint(32); 65 | 66 | return { 67 | type: 'internal', 68 | ihrDisabled, 69 | bounce, 70 | bounced, 71 | src, 72 | dest, 73 | value, 74 | ihrFee, 75 | forwardFee, 76 | createdLt, 77 | createdAt, 78 | }; 79 | } 80 | 81 | // External In mesage 82 | if (!slice.loadBit()) { 83 | throw Error('External In message is not possible for CommonMessageInfoRelaxed'); 84 | } 85 | 86 | // External Out message 87 | const src = slice.loadMaybeAddress()!; 88 | const dest = slice.loadMaybeExternalAddress(); 89 | const createdLt = slice.loadUintBig(64); 90 | const createdAt = slice.loadUint(32); 91 | 92 | return { 93 | type: 'external-out', 94 | src, 95 | dest, 96 | createdLt, 97 | createdAt, 98 | }; 99 | } 100 | 101 | export function storeCommonMessageInfoRelaxed(source: CommonMessageInfoRelaxed) { 102 | return (builder: Builder) => { 103 | if (source.type === 'internal') { 104 | builder.storeBit(0); 105 | builder.storeBit(source.ihrDisabled); 106 | builder.storeBit(source.bounce); 107 | builder.storeBit(source.bounced); 108 | builder.storeAddress(source.src); 109 | builder.storeAddress(source.dest); 110 | builder.store(storeCurrencyCollection(source.value)); 111 | builder.storeCoins(source.ihrFee); 112 | builder.storeCoins(source.forwardFee); 113 | builder.storeUint(source.createdLt, 64); 114 | builder.storeUint(source.createdAt, 32); 115 | } else if (source.type === 'external-out') { 116 | builder.storeBit(1); 117 | builder.storeBit(1); 118 | builder.storeAddress(source.src); 119 | builder.storeAddress(source.dest); 120 | builder.storeUint(source.createdLt, 64); 121 | builder.storeUint(source.createdAt, 32); 122 | } else { 123 | throw new Error('Unknown CommonMessageInfo type'); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /src/types/ComputeSkipReason.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | 10 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L306 11 | // cskip_no_state$00 = ComputeSkipReason; 12 | // cskip_bad_state$01 = ComputeSkipReason; 13 | // cskip_no_gas$10 = ComputeSkipReason; 14 | 15 | import { Builder } from "../boc/Builder"; 16 | import { Slice } from "../boc/Slice"; 17 | 18 | export type ComputeSkipReason = 'no-state' | 'bad-state' | 'no-gas'; 19 | 20 | export function loadComputeSkipReason(slice: Slice): ComputeSkipReason { 21 | let reason = slice.loadUint(2); 22 | if (reason === 0x00) { 23 | return 'no-state'; 24 | } else if (reason === 0x01) { 25 | return 'bad-state'; 26 | } else if (reason === 0x02) { 27 | return 'no-gas'; 28 | } 29 | throw new Error(`Unknown ComputeSkipReason: ${reason}`); 30 | } 31 | 32 | export function storeComputeSkipReason(src: ComputeSkipReason) { 33 | return (builder: Builder) => { 34 | if (src === 'no-state') { 35 | builder.storeUint(0x00, 2); 36 | } else if (src === 'bad-state') { 37 | builder.storeUint(0x01, 2); 38 | } else if (src === 'no-gas') { 39 | builder.storeUint(0x02, 2); 40 | } else { 41 | throw new Error(`Unknown ComputeSkipReason: ${src}`); 42 | } 43 | }; 44 | } -------------------------------------------------------------------------------- /src/types/CurrencyCollection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Dictionary } from "../dict/Dictionary"; 12 | import { Maybe } from "../utils/maybe"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L120 15 | // extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) 16 | // = ExtraCurrencyCollection; 17 | // currencies$_ grams:Grams other:ExtraCurrencyCollection 18 | // = CurrencyCollection; 19 | 20 | export interface CurrencyCollection { other?: Maybe>, coins: bigint }; 21 | 22 | export function loadCurrencyCollection(slice: Slice): CurrencyCollection { 23 | const coins = slice.loadCoins(); 24 | const other = slice.loadDict(Dictionary.Keys.Uint(32), Dictionary.Values.BigVarUint(5 /* log2(32) */)); 25 | if (other.size === 0) { 26 | return { coins }; 27 | } else { 28 | return { other, coins }; 29 | } 30 | } 31 | 32 | export function storeCurrencyCollection(collection: CurrencyCollection) { 33 | return (builder: Builder) => { 34 | builder.storeCoins(collection.coins); 35 | if (collection.other) { 36 | builder.storeDict(collection.other); 37 | } else { 38 | builder.storeBit(0); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/types/DepthBalanceInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection" 12 | 13 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L259 14 | // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo; 15 | 16 | export type DepthBalanceInfo = { 17 | splitDepth: number, 18 | balance: CurrencyCollection 19 | }; 20 | 21 | export function loadDepthBalanceInfo(slice: Slice): DepthBalanceInfo { 22 | let splitDepth = slice.loadUint(5); 23 | return { 24 | splitDepth, 25 | balance: loadCurrencyCollection(slice) 26 | }; 27 | } 28 | 29 | export function storeDepthBalanceInfo(src: DepthBalanceInfo) { 30 | return (builder: Builder) => { 31 | builder.storeUint(src.splitDepth, 5); 32 | builder.store(storeCurrencyCollection(src.balance)); 33 | }; 34 | } -------------------------------------------------------------------------------- /src/types/ExtraCurrency.ts: -------------------------------------------------------------------------------- 1 | import { Builder, beginCell } from '../boc/Builder'; 2 | import { Cell } from '../boc/Cell'; 3 | import { Slice } from '../boc/Slice'; 4 | import { Dictionary } from '../dict/Dictionary'; 5 | 6 | export type ExtraCurrency = { 7 | [k: number] : bigint 8 | } 9 | 10 | export function loadExtraCurrency(data: Slice | Cell | Dictionary) { 11 | let ecDict = data instanceof Dictionary ? data : Dictionary.loadDirect(Dictionary.Keys.Uint(32), Dictionary.Values.BigVarUint(5), data); 12 | let ecMap: ExtraCurrency = {}; 13 | 14 | for(let [k, v] of ecDict) { 15 | ecMap[k] = v; 16 | } 17 | 18 | return ecMap; 19 | } 20 | 21 | export function loadMaybeExtraCurrency(data: Slice) { 22 | const ecData = data.loadMaybeRef(); 23 | return ecData === null ? ecData : loadExtraCurrency(ecData); 24 | } 25 | 26 | export function storeExtraCurrency(extracurrency: ExtraCurrency) { 27 | return (builder: Builder) => { 28 | builder.storeDict(packExtraCurrencyDict(extracurrency)); 29 | } 30 | } 31 | 32 | export function packExtraCurrencyDict(extracurrency: ExtraCurrency) { 33 | const resEc = Dictionary.empty(Dictionary.Keys.Uint(32), Dictionary.Values.BigVarUint(5)); 34 | Object.entries(extracurrency).map(([k, v]) => resEc.set(Number(k), v)); 35 | return resEc; 36 | } 37 | 38 | export function packExtraCurrencyCell(extracurrency: ExtraCurrency) { 39 | return beginCell().storeDictDirect( 40 | packExtraCurrencyDict(extracurrency) 41 | ).endCell(); 42 | } 43 | -------------------------------------------------------------------------------- /src/types/HashUpdate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L273 13 | // update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256 14 | // = HASH_UPDATE X; 15 | 16 | export type HashUpdate = { oldHash: Buffer, newHash: Buffer }; 17 | 18 | export function loadHashUpdate(slice: Slice): HashUpdate { 19 | if (slice.loadUint(8) !== 0x72) { 20 | throw Error('Invalid data'); 21 | } 22 | const oldHash = slice.loadBuffer(32); 23 | const newHash = slice.loadBuffer(32); 24 | return { oldHash, newHash }; 25 | } 26 | 27 | export function storeHashUpdate(src: HashUpdate) { 28 | return (builder: Builder) => { 29 | builder.storeUint(0x72, 8); 30 | builder.storeBuffer(src.oldHash); 31 | builder.storeBuffer(src.newHash); 32 | }; 33 | } -------------------------------------------------------------------------------- /src/types/LibRef.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | 13 | // Source: https://github.com/ton-blockchain/ton/blob/128a85bee568e84146f1e985a92ea85011d1e380/crypto/block/block.tlb#L385-L386 14 | // libref_hash$0 lib_hash:bits256 = LibRef; 15 | // libref_ref$1 library:^Cell = LibRef; 16 | 17 | 18 | export interface LibRefHash { 19 | type: 'hash'; 20 | libHash: Buffer; 21 | } 22 | 23 | export interface LibRefRef { 24 | type: 'ref'; 25 | library: Cell; 26 | } 27 | 28 | export type LibRef = LibRefHash | LibRefRef; 29 | 30 | export function loadLibRef(slice: Slice): LibRef { 31 | const type = slice.loadUint(1); 32 | if (type === 0) { 33 | return { 34 | type: 'hash', 35 | libHash: slice.loadBuffer(32) 36 | }; 37 | } 38 | else { 39 | return { 40 | type: 'ref', 41 | library: slice.loadRef() 42 | }; 43 | } 44 | } 45 | 46 | export function storeLibRef(src: LibRef) { 47 | return (builder: Builder) => { 48 | if (src.type === 'hash') { 49 | builder.storeUint(0, 1); 50 | builder.storeBuffer(src.libHash); 51 | } 52 | else { 53 | builder.storeUint(1, 1); 54 | builder.storeRef(src.library); 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/types/MasterchainStateExtra.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | import { Dictionary } from "../dict/Dictionary"; 13 | import { CurrencyCollection, loadCurrencyCollection } from "./CurrencyCollection"; 14 | 15 | // Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L509 16 | // _ config_addr:bits256 config:^(Hashmap 32 ^Cell) 17 | // = ConfigParams; 18 | // Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/block.tlb#L534 19 | // masterchain_state_extra#cc26 20 | // shard_hashes:ShardHashes 21 | // config:ConfigParams 22 | // ^[ flags:(## 16) { flags <= 1 } 23 | // validator_info:ValidatorInfo 24 | // prev_blocks:OldMcBlocksInfo 25 | // after_key_block:Bool 26 | // last_key_block:(Maybe ExtBlkRef) 27 | // block_create_stats:(flags . 0)?BlockCreateStats ] 28 | // global_balance:CurrencyCollection 29 | // = McStateExtra; 30 | 31 | export type MasterchainStateExtra = { 32 | configAddress: bigint; 33 | config: Dictionary; 34 | globalBalance: CurrencyCollection 35 | }; 36 | 37 | export function loadMasterchainStateExtra(cs: Slice): MasterchainStateExtra { 38 | 39 | // Check magic 40 | if (cs.loadUint(16) !== 0xcc26) { 41 | throw Error('Invalid data'); 42 | } 43 | 44 | // Skip shard_hashes 45 | if (cs.loadBit()) { 46 | cs.loadRef(); 47 | } 48 | 49 | // Read config 50 | let configAddress = cs.loadUintBig(256); 51 | let config = Dictionary.load(Dictionary.Keys.Int(32), Dictionary.Values.Cell(), cs); 52 | 53 | // Rad global balance 54 | const globalBalance = loadCurrencyCollection(cs); 55 | 56 | return { 57 | config, 58 | configAddress, 59 | globalBalance 60 | }; 61 | } -------------------------------------------------------------------------------- /src/types/Message.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import {loadMessage, storeMessage} from "./Message"; 12 | 13 | describe('Message', () => { 14 | it('should handle edge case with extra currency', () => { 15 | const tx = 'te6cckEBBwEA3QADs2gB7ix8WDhQdzzFOCf6hmZ2Dzw2vFNtbavUArvbhXqqqmEAMpuMhx8zp7O3wqMokkuyFkklKpftc4Dh9_5bvavmCo-UXR6uVOIGMkCwAAAAAAC3GwLLUHl_4AYCAQCA_____________________________________________________________________________________gMBPAUEAwFDoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAUACAAAAAAAAAANoAAAAAEIDF-r-4Q'; 16 | const cell = Cell.fromBase64(tx); 17 | const message = loadMessage(cell.beginParse()); 18 | let stored = beginCell() 19 | .store(storeMessage(message)) 20 | .endCell(); 21 | expect(stored.equals(cell)).toBe(true); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/types/Message.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | import { DictionaryValue } from "../dict/Dictionary"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { CommonMessageInfo, loadCommonMessageInfo, storeCommonMessageInfo } from "./CommonMessageInfo"; 15 | import { loadStateInit, StateInit, storeStateInit } from "./StateInit"; 16 | 17 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L147 18 | // message$_ {X:Type} info:CommonMsgInfo 19 | // init:(Maybe (Either StateInit ^StateInit)) 20 | // body:(Either X ^X) = Message X; 21 | 22 | export type Message = { 23 | info: CommonMessageInfo, 24 | init?: Maybe, 25 | body: Cell 26 | }; 27 | 28 | export function loadMessage(slice: Slice): Message { 29 | const info = loadCommonMessageInfo(slice); 30 | let init: StateInit | null = null; 31 | if (slice.loadBit()) { 32 | if (!slice.loadBit()) { 33 | init = loadStateInit(slice); 34 | } else { 35 | init = loadStateInit(slice.loadRef().beginParse()); 36 | } 37 | } 38 | const body = slice.loadBit() ? slice.loadRef() : slice.asCell(); 39 | 40 | return { 41 | info, 42 | init, 43 | body 44 | }; 45 | } 46 | 47 | export function storeMessage(message: Message, opts?: { forceRef?: boolean }) { 48 | return (builder: Builder) => { 49 | 50 | // Store CommonMsgInfo 51 | builder.store(storeCommonMessageInfo(message.info)); 52 | 53 | // Store init 54 | if (message.init) { 55 | builder.storeBit(true); 56 | let initCell = beginCell().store(storeStateInit(message.init)); 57 | 58 | // Check if need to store it in ref 59 | let needRef = false; 60 | if (opts && opts.forceRef) { 61 | needRef = true; 62 | } else { 63 | needRef = builder.availableBits - 2 /* At least two bits for ref flags */ < initCell.bits + message.body.bits.length; 64 | } 65 | 66 | // Persist init 67 | if (needRef) { 68 | builder.storeBit(true); 69 | builder.storeRef(initCell); 70 | } else { 71 | builder.storeBit(false); 72 | builder.storeBuilder(initCell); 73 | } 74 | } else { 75 | builder.storeBit(false); 76 | } 77 | 78 | // Store body 79 | let needRef = false; 80 | if (opts && opts.forceRef) { 81 | needRef = true; 82 | } else { 83 | needRef = builder.availableBits - 1 /* At least one bit for ref flag */ < message.body.bits.length || 84 | builder.refs + message.body.refs.length > 4; 85 | } 86 | if (needRef) { 87 | builder.storeBit(true); 88 | builder.storeRef(message.body); 89 | } else { 90 | builder.storeBit(false); 91 | builder.storeBuilder(message.body.asBuilder()); 92 | } 93 | }; 94 | } 95 | 96 | export const MessageValue: DictionaryValue = { 97 | serialize(src, builder) { 98 | builder.storeRef(beginCell() 99 | .store(storeMessage(src))); 100 | }, 101 | parse(slice) { 102 | return loadMessage(slice.loadRef().beginParse()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/types/MessageRelaxed.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { loadMessageRelaxed, storeMessageRelaxed } from "./MessageRelaxed"; 12 | 13 | describe('MessageRelaxed', () => { 14 | it('should parse message relaxed', () => { 15 | const state = 'te6ccsEBAgEAkQA3kQFoYgBgSQkXjXbkhpC1sju4zUJsLIAoavunKbfNsPFbk9jXL6BfXhAAAAAAAAAAAAAAAAAAAQEAsA+KfqUAAAAAAAAAAEO5rKAIAboVCXedy2J0RCseg4yfdNFtU8/BfiaHVEPkH/ze1W+fABicYUqh1j9Lnqv9ZhECm0XNPaB7/HcwoBb3AJnYYfqByAvrwgCqR2XE'; 16 | const cell = Cell.fromBoc(Buffer.from(state, 'base64'))[0]; 17 | const relaxed = loadMessageRelaxed(cell.beginParse()); 18 | let stored = beginCell() 19 | .store(storeMessageRelaxed(relaxed)) 20 | .endCell(); 21 | expect(stored.equals(cell)).toBe(true); 22 | }); 23 | 24 | it('should store exotic message relaxed', () => { 25 | let boc = 'te6cckEBBgEApwAJRgMNtncFfUUJSR6XK02Y/bjHpB1pj8VtOlnKAxgDtajfKgACASIFgZABAwIoSAEBN4Yioo+yQnBEkgpN5SV1lnSGuoJhL3ShCi0dcMHbuFcAACIBIAUEAE2/fOtFTZyY8zlmFJ8dch//XZQ4QApiXOGPZXvjFv5j0LSgZ7ckWPAoSAEBr+h0Em3TbCgl+CpPMKKoQskNFu4vLU/8w4Zuaz7PRP8AAOG0rdg='; 26 | let cell = Cell.fromBase64(boc); 27 | 28 | let payload = beginCell().store(storeMessageRelaxed({ 29 | body: cell, 30 | info: { 31 | createdAt: 0, 32 | createdLt: 0n, 33 | type: 'external-out', 34 | dest: null, 35 | src: null, 36 | } 37 | })).endCell(); 38 | }); 39 | }); -------------------------------------------------------------------------------- /src/types/MessageRelaxed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | import { Maybe } from "../utils/maybe"; 13 | import { CommonMessageInfoRelaxed, loadCommonMessageInfoRelaxed, storeCommonMessageInfoRelaxed } from "./CommonMessageInfoRelaxed"; 14 | import { loadStateInit, StateInit, storeStateInit } from "./StateInit"; 15 | 16 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L151 17 | // message$_ {X:Type} info:CommonMsgInfoRelaxed 18 | // init:(Maybe (Either StateInit ^StateInit)) 19 | // body:(Either X ^X) = MessageRelaxed X; 20 | 21 | export type MessageRelaxed = { 22 | info: CommonMessageInfoRelaxed, 23 | init?: Maybe, 24 | body: Cell 25 | }; 26 | 27 | export function loadMessageRelaxed(slice: Slice): MessageRelaxed { 28 | const info = loadCommonMessageInfoRelaxed(slice); 29 | let init: StateInit | null = null; 30 | if (slice.loadBit()) { 31 | if (!slice.loadBit()) { 32 | init = loadStateInit(slice); 33 | } else { 34 | init = loadStateInit(slice.loadRef().beginParse()); 35 | } 36 | } 37 | const body = slice.loadBit() ? slice.loadRef() : slice.asCell(); 38 | 39 | return { 40 | info, 41 | init, 42 | body 43 | }; 44 | } 45 | 46 | export function storeMessageRelaxed(message: MessageRelaxed, opts?: { forceRef?: boolean }) { 47 | return (builder: Builder) => { 48 | 49 | // Store CommonMsgInfo 50 | builder.store(storeCommonMessageInfoRelaxed(message.info)); 51 | 52 | // Store init 53 | if (message.init) { 54 | builder.storeBit(true); 55 | let initCell = beginCell().store(storeStateInit(message.init)); 56 | 57 | // Check if ref is needed 58 | let needRef = false; 59 | if (opts && opts.forceRef) { 60 | needRef = true; 61 | } else { 62 | if (builder.availableBits - 2 /* At least on byte for ref flag */ >= initCell.bits) { 63 | needRef = false; 64 | } else { 65 | needRef = true; 66 | } 67 | } 68 | 69 | // Store ref 70 | if (needRef) { 71 | builder.storeBit(true); 72 | builder.storeRef(initCell); 73 | } else { 74 | builder.storeBit(false); 75 | builder.storeBuilder(initCell); 76 | } 77 | } else { 78 | builder.storeBit(false); 79 | } 80 | 81 | // Store body 82 | let needRef = false; 83 | if (opts && opts.forceRef) { 84 | needRef = true; 85 | } else { 86 | 87 | /* 88 | 1. If at least one bit for ref flag 89 | 2. If enough space for refs 90 | 3. If not exotic 91 | */ 92 | 93 | if (builder.availableBits - 1 >= message.body.bits.length && 94 | builder.refs + message.body.refs.length <= 4 && 95 | !message.body.isExotic) { 96 | needRef = false; 97 | } else { 98 | needRef = true; 99 | } 100 | } 101 | if (needRef) { 102 | builder.storeBit(true); 103 | builder.storeRef(message.body); 104 | } else { 105 | builder.storeBit(false); 106 | builder.storeBuilder(message.body.asBuilder()); 107 | } 108 | }; 109 | } -------------------------------------------------------------------------------- /src/types/OutList.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | 10 | import { SendMode } from "./SendMode"; 11 | import {loadMessageRelaxed, MessageRelaxed, storeMessageRelaxed} from "./MessageRelaxed"; 12 | import {beginCell, Builder} from "../boc/Builder"; 13 | import { Cell } from "../boc/Cell"; 14 | import {Slice} from "../boc/Slice"; 15 | import {loadStorageInfo, storeStorageInfo} from "./StorageInfo"; 16 | import {loadAccountStorage, storeAccountStorage} from "./AccountStorage"; 17 | import {Account} from "./Account"; 18 | import {loadMessage} from "./Message"; 19 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 20 | import { SimpleLibrary } from "./SimpleLibrary"; 21 | import { LibRef, loadLibRef, storeLibRef } from "./LibRef"; 22 | import { ReserveMode } from "./ReserveMode"; 23 | 24 | export interface OutActionSendMsg { 25 | type: 'sendMsg', 26 | mode: SendMode; 27 | outMsg: MessageRelaxed; 28 | } 29 | 30 | export interface OutActionSetCode { 31 | type: 'setCode', 32 | newCode: Cell; 33 | } 34 | 35 | export interface OutActionReserve { 36 | type: 'reserve', 37 | mode: ReserveMode; 38 | currency: CurrencyCollection; 39 | } 40 | 41 | export interface OutActionChangeLibrary { 42 | type: 'changeLibrary', 43 | mode: number; 44 | libRef: LibRef; 45 | } 46 | 47 | export type OutAction = OutActionSendMsg | OutActionSetCode | OutActionReserve | OutActionChangeLibrary; 48 | 49 | export function storeOutAction(action: OutAction) { 50 | switch (action.type) { 51 | case 'sendMsg': 52 | return storeOutActionSendMsg(action); 53 | case 'setCode': 54 | return storeOutActionSetCode(action); 55 | case 'reserve': 56 | return storeOutActionReserve(action); 57 | case 'changeLibrary': 58 | return storeOutActionChangeLibrary(action); 59 | default: 60 | throw new Error(`Unknown action type ${(action as OutAction).type}`) 61 | } 62 | } 63 | 64 | /* 65 | action_send_msg#0ec3c86d mode:(## 8) 66 | out_msg:^(MessageRelaxed Any) = OutAction; 67 | */ 68 | const outActionSendMsgTag = 0x0ec3c86d; 69 | function storeOutActionSendMsg(action: OutActionSendMsg) { 70 | return (builder: Builder) => { 71 | builder.storeUint(outActionSendMsgTag, 32) 72 | .storeUint(action.mode, 8) 73 | .storeRef(beginCell().store(storeMessageRelaxed(action.outMsg)).endCell()); 74 | } 75 | } 76 | 77 | /* 78 | action_set_code#ad4de08e new_code:^Cell = OutAction; 79 | */ 80 | const outActionSetCodeTag = 0xad4de08e; 81 | function storeOutActionSetCode(action: OutActionSetCode) { 82 | return (builder: Builder) => { 83 | builder.storeUint(outActionSetCodeTag, 32).storeRef(action.newCode); 84 | } 85 | } 86 | 87 | /* 88 | action_reserve_currency#36e6b809 mode:(## 8) 89 | currency:CurrencyCollection = OutAction; 90 | */ 91 | const outActionReserveTag = 0x36e6b809; 92 | function storeOutActionReserve(action: OutActionReserve) { 93 | return (builder: Builder) => { 94 | builder.storeUint(outActionReserveTag, 32) 95 | .storeUint(action.mode, 8) 96 | .store(storeCurrencyCollection(action.currency)); 97 | } 98 | } 99 | 100 | /* 101 | action_change_library#26fa1dd4 mode:(## 7) 102 | libref:LibRef = OutAction; 103 | */ 104 | const outActionChangeLibraryTag = 0x26fa1dd4; 105 | function storeOutActionChangeLibrary(action: OutActionChangeLibrary) { 106 | return (builder: Builder) => { 107 | builder.storeUint(outActionChangeLibraryTag, 32) 108 | .storeUint(action.mode, 7) 109 | .store(storeLibRef(action.libRef)); 110 | } 111 | } 112 | 113 | 114 | export function loadOutAction(slice: Slice): OutAction { 115 | const tag = slice.loadUint(32); 116 | if (tag === outActionSendMsgTag) { 117 | const mode = slice.loadUint(8); 118 | const outMsg = loadMessageRelaxed(slice.loadRef().beginParse()); 119 | 120 | return { 121 | type: 'sendMsg', 122 | mode, 123 | outMsg 124 | } 125 | } 126 | 127 | if (tag === outActionSetCodeTag) { 128 | const newCode = slice.loadRef(); 129 | 130 | return { 131 | type: 'setCode', 132 | newCode 133 | } 134 | } 135 | 136 | if (tag === outActionReserveTag) { 137 | const mode = slice.loadUint(8); 138 | const currency = loadCurrencyCollection(slice); 139 | 140 | return { 141 | type: 'reserve', 142 | mode, 143 | currency 144 | } 145 | } 146 | 147 | if (tag === outActionChangeLibraryTag) { 148 | const mode = slice.loadUint(7); 149 | const libRef = loadLibRef(slice); 150 | 151 | return { 152 | type: 'changeLibrary', 153 | mode, 154 | libRef 155 | } 156 | } 157 | 158 | throw new Error(`Unknown out action tag 0x${tag.toString(16)}`); 159 | } 160 | 161 | 162 | /* 163 | out_list_empty$_ = OutList 0; 164 | out_list$_ {n:#} prev:^(OutList n) action:OutAction 165 | = OutList (n + 1); 166 | */ 167 | export function storeOutList(actions: OutAction[]) { 168 | const cell = actions.reduce((cell, action) => beginCell() 169 | .storeRef(cell) 170 | .store(storeOutAction(action)) 171 | .endCell(), 172 | beginCell().endCell() 173 | ); 174 | 175 | return (builder: Builder) => { 176 | builder.storeSlice(cell.beginParse()); 177 | } 178 | } 179 | 180 | export function loadOutList(slice: Slice): OutAction[] { 181 | const actions: OutAction[] = []; 182 | while (slice.remainingRefs) { 183 | const nextCell = slice.loadRef(); 184 | 185 | actions.push(loadOutAction(slice)); 186 | slice = nextCell.beginParse(); 187 | } 188 | 189 | return actions.reverse(); 190 | } 191 | 192 | -------------------------------------------------------------------------------- /src/types/ReserveMode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export enum ReserveMode { 10 | THIS_AMOUNT = 0, 11 | LEAVE_THIS_AMOUNT = 1, 12 | AT_MOST_THIS_AMOUNT = 2, 13 | LEAVE_MAX_THIS_AMOUNT = 3, 14 | BEFORE_BALANCE_PLUS_THIS_AMOUNT = 4, 15 | LEAVE_BBALANCE_PLUS_THIS_AMOUNT = 5, 16 | BEFORE_BALANCE_MINUS_THIS_AMOUNT = 12, 17 | LEAVE_BEFORE_BALANCE_MINUS_THIS_AMOUNT = 13 18 | } 19 | -------------------------------------------------------------------------------- /src/types/SendMode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export enum SendMode { 10 | CARRY_ALL_REMAINING_BALANCE = 128, 11 | CARRY_ALL_REMAINING_INCOMING_VALUE = 64, 12 | DESTROY_ACCOUNT_IF_ZERO = 32, 13 | PAY_GAS_SEPARATELY = 1, 14 | IGNORE_ERRORS = 2, 15 | NONE = 0 16 | } 17 | -------------------------------------------------------------------------------- /src/types/ShardAccount.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { inspect } from "util"; 10 | import { beginCell } from "../boc/Builder"; 11 | import { Cell } from "../boc/Cell"; 12 | import { loadShardAccount, storeShardAccount } from "./ShardAccount"; 13 | 14 | describe('ShardAccount', () => { 15 | it('should parse tonkite cell', () => { 16 | const boc = Buffer.from('te6cckEBBAEA7wABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAnfACD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqCAkCIGAAAACAAAAAAAAAAGgN4Lazp2QAAE0ACAwCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjPUU3w=', 'base64'); 17 | const cell = Cell.fromBoc(boc)[0]; 18 | const shardAccount = loadShardAccount(cell.beginParse()); 19 | const stored = beginCell().store(storeShardAccount(shardAccount)).endCell(); 20 | expect(cell.equals(stored)).toBe(true); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/types/ShardAccount.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { Account, loadAccount, storeAccount } from "./Account"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L256 15 | // account_descr$_ account:^Account last_trans_hash:bits256 16 | // last_trans_lt:uint64 = ShardAccount; 17 | 18 | export type ShardAccount = { 19 | account?: Maybe; 20 | lastTransactionHash: bigint; 21 | lastTransactionLt: bigint; 22 | }; 23 | 24 | export function loadShardAccount(slice: Slice): ShardAccount { 25 | let accountRef = slice.loadRef(); 26 | let account: Account | undefined = undefined; 27 | if (!accountRef.isExotic) { 28 | let accountSlice = accountRef.beginParse(); 29 | if (accountSlice.loadBit()) { 30 | account = loadAccount(accountSlice); 31 | } 32 | } 33 | 34 | return { 35 | account, 36 | lastTransactionHash: slice.loadUintBig(256), 37 | lastTransactionLt: slice.loadUintBig(64) 38 | }; 39 | } 40 | 41 | export function storeShardAccount(src: ShardAccount) { 42 | return (builder: Builder) => { 43 | if (src.account) { 44 | builder.storeRef(beginCell() 45 | .storeBit(true) 46 | .store(storeAccount(src.account)) 47 | ); 48 | } else { 49 | builder.storeRef(beginCell() 50 | .storeBit(false) 51 | ); 52 | } 53 | builder.storeUint(src.lastTransactionHash, 256); 54 | builder.storeUint(src.lastTransactionLt, 64); 55 | }; 56 | } -------------------------------------------------------------------------------- /src/types/ShardAccounts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Dictionary, DictionaryValue } from "../dict/Dictionary"; 12 | import { DepthBalanceInfo, loadDepthBalanceInfo, storeDepthBalanceInfo } from "./DepthBalanceInfo"; 13 | import { loadShardAccount, ShardAccount, storeShardAccount } from "./ShardAccount"; 14 | 15 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L261 16 | // _ (HashmapAugE 256 ShardAccount DepthBalanceInfo) = ShardAccounts; 17 | 18 | export type ShardAccountRef = { 19 | shardAccount: ShardAccount 20 | depthBalanceInfo: DepthBalanceInfo 21 | }; 22 | 23 | export const ShardAccountRefValue: DictionaryValue = { 24 | parse: (cs: Slice) => { 25 | let depthBalanceInfo = loadDepthBalanceInfo(cs); 26 | let shardAccount = loadShardAccount(cs); 27 | return { 28 | depthBalanceInfo, 29 | shardAccount 30 | } 31 | }, 32 | serialize(src, builder) { 33 | builder.store(storeDepthBalanceInfo(src.depthBalanceInfo)); 34 | builder.store(storeShardAccount(src.shardAccount)); 35 | }, 36 | }; 37 | 38 | export function loadShardAccounts(cs: Slice): Dictionary { 39 | return Dictionary.load(Dictionary.Keys.BigUint(256), ShardAccountRefValue, cs); 40 | } 41 | 42 | export function storeShardAccounts(src: Dictionary) { 43 | return (Builder: Builder) => { 44 | Builder.storeDict(src); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/types/ShardIdent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder" 10 | import { Slice } from "../boc/Slice" 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L384 13 | // shard_ident$00 shard_pfx_bits:(#<= 60) 14 | // workchain_id:int32 shard_prefix:uint64 = ShardIdent; 15 | 16 | export type ShardIdent = { 17 | shardPrefixBits: number, 18 | workchainId: number, 19 | shardPrefix: bigint 20 | } 21 | 22 | export function loadShardIdent(slice: Slice): ShardIdent { 23 | if (slice.loadUint(2) !== 0) { 24 | throw Error('Invalid data') 25 | } 26 | return { 27 | shardPrefixBits: slice.loadUint(6), 28 | workchainId: slice.loadInt(32), 29 | shardPrefix: slice.loadUintBig(64) 30 | } 31 | } 32 | 33 | export function storeShardIdent(src: ShardIdent) { 34 | return (builder: Builder) => { 35 | builder.storeUint(0, 2); 36 | builder.storeUint(src.shardPrefixBits, 6); 37 | builder.storeInt(src.workchainId, 32); 38 | builder.storeUint(src.shardPrefix, 64); 39 | }; 40 | } -------------------------------------------------------------------------------- /src/types/ShardStateUnsplit.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Cell } from "../boc/Cell"; 10 | import { loadShardStateUnsplit } from "./ShardStateUnsplit"; 11 | 12 | describe('ShardStateUnsplit', () => { 13 | it('should parse ShardState', () => { 14 | const state = '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'; 15 | const cell = Cell.fromBoc(Buffer.from(state, 'base64'))[0]; 16 | loadShardStateUnsplit(cell.beginParse()); 17 | }) 18 | it('should parse account state with exotic cells', () => { 19 | let data = require('./__testdata__/configProof'); 20 | const cell = Cell.fromBoc(Buffer.from(data, 'base64'))[0]; 21 | loadShardStateUnsplit(cell.beginParse()); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/types/ShardStateUnsplit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Slice } from "../boc/Slice" 10 | import { Dictionary } from "../dict/Dictionary" 11 | import { Maybe } from "../utils/maybe" 12 | import { loadMasterchainStateExtra, MasterchainStateExtra } from "./MasterchainStateExtra" 13 | import { ShardAccount } from "./ShardAccount" 14 | import { loadShardAccounts, ShardAccountRef } from "./ShardAccounts" 15 | import { loadShardIdent, ShardIdent } from "./ShardIdent" 16 | 17 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L396 18 | // shard_state#9023afe2 global_id:int32 19 | // shard_id:ShardIdent 20 | // seq_no:uint32 vert_seq_no:# 21 | // gen_utime:uint32 gen_lt:uint64 22 | // min_ref_mc_seqno:uint32 23 | // out_msg_queue_info:^OutMsgQueueInfo 24 | // before_split:(## 1) 25 | // accounts:^ShardAccounts 26 | // ^[ overload_history:uint64 underload_history:uint64 27 | // total_balance:CurrencyCollection 28 | // total_validator_fees:CurrencyCollection 29 | // libraries:(HashmapE 256 LibDescr) 30 | // master_ref:(Maybe BlkMasterInfo) ] 31 | // custom:(Maybe ^McStateExtra) 32 | // = ShardStateUnsplit; 33 | 34 | export type ShardStateUnsplit = { 35 | globalId: number, 36 | shardId: ShardIdent, 37 | seqno: number, 38 | vertSeqNo: number, 39 | genUtime: number, 40 | genLt: bigint, 41 | minRefMcSeqno: number, 42 | beforeSplit: boolean, 43 | accounts?: Maybe>, 44 | extras?: Maybe 45 | } 46 | 47 | export function loadShardStateUnsplit(cs: Slice): ShardStateUnsplit { 48 | if (cs.loadUint(32) !== 0x9023afe2) { 49 | throw Error('Invalid data'); 50 | } 51 | let globalId = cs.loadInt(32); 52 | let shardId = loadShardIdent(cs); 53 | let seqno = cs.loadUint(32); 54 | let vertSeqNo = cs.loadUint(32); 55 | let genUtime = cs.loadUint(32); 56 | let genLt = cs.loadUintBig(64); 57 | let minRefMcSeqno = cs.loadUint(32); 58 | 59 | // Skip OutMsgQueueInfo: usually exotic 60 | cs.loadRef(); 61 | 62 | let beforeSplit = cs.loadBit(); 63 | 64 | // Parse accounts 65 | let shardAccountsRef = cs.loadRef(); 66 | let accounts: Dictionary | undefined = undefined; 67 | if (!shardAccountsRef.isExotic) { 68 | accounts = loadShardAccounts(shardAccountsRef.beginParse()); 69 | } 70 | 71 | // Skip (not used by apps) 72 | cs.loadRef(); 73 | 74 | // Parse extras 75 | let mcStateExtra = cs.loadBit(); 76 | let extras: MasterchainStateExtra | null = null; 77 | if (mcStateExtra) { 78 | let cell = cs.loadRef(); 79 | if (!cell.isExotic) { 80 | extras = loadMasterchainStateExtra(cell.beginParse()); 81 | } 82 | }; 83 | 84 | return { 85 | globalId, 86 | shardId, 87 | seqno, 88 | vertSeqNo, 89 | genUtime, 90 | genLt, 91 | minRefMcSeqno, 92 | beforeSplit, 93 | accounts, 94 | extras 95 | } 96 | } -------------------------------------------------------------------------------- /src/types/SimpleLibrary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | import { DictionaryValue } from "../dict/Dictionary"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L145 15 | // simple_lib$_ public:Bool root:^Cell = SimpleLib; 16 | 17 | 18 | export interface SimpleLibrary { 19 | public: boolean; 20 | root: Cell; 21 | } 22 | 23 | export function loadSimpleLibrary(slice: Slice): SimpleLibrary { 24 | return { 25 | public: slice.loadBit(), 26 | root: slice.loadRef() 27 | }; 28 | } 29 | 30 | export function storeSimpleLibrary(src: SimpleLibrary) { 31 | return (builder: Builder) => { 32 | builder.storeBit(src.public); 33 | builder.storeRef(src.root); 34 | } 35 | } 36 | 37 | export const SimpleLibraryValue: DictionaryValue = { 38 | serialize(src, builder) { 39 | storeSimpleLibrary(src)(builder); 40 | }, 41 | parse(src) { 42 | return loadSimpleLibrary(src); 43 | }, 44 | }; -------------------------------------------------------------------------------- /src/types/SplitMergeInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L339 13 | // split_merge_info$_ cur_shard_pfx_len:(## 6) 14 | // acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256 15 | // = SplitMergeInfo; 16 | 17 | export type SplitMergeInfo = { 18 | currentShardPrefixLength: number, 19 | accountSplitDepth: number, 20 | thisAddress: bigint, 21 | siblingAddress: bigint 22 | }; 23 | 24 | export function loadSplitMergeInfo(slice: Slice): SplitMergeInfo { 25 | let currentShardPrefixLength = slice.loadUint(6); 26 | let accountSplitDepth = slice.loadUint(6); 27 | let thisAddress = slice.loadUintBig(256); 28 | let siblingAddress = slice.loadUintBig(256); 29 | return { 30 | currentShardPrefixLength, 31 | accountSplitDepth, 32 | thisAddress, 33 | siblingAddress 34 | }; 35 | } 36 | 37 | export function storeSplitMergeInfo(src: SplitMergeInfo) { 38 | return (builder: Builder) => { 39 | builder.storeUint(src.currentShardPrefixLength, 6); 40 | builder.storeUint(src.accountSplitDepth, 6); 41 | builder.storeUint(src.thisAddress, 256); 42 | builder.storeUint(src.siblingAddress, 256); 43 | }; 44 | } -------------------------------------------------------------------------------- /src/types/StateInit.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { loadStateInit, storeStateInit } from "./StateInit"; 12 | 13 | describe('StateInit', () => { 14 | it('shoild serialize to match golden-1', () => { 15 | 16 | // Serialize 17 | let boc = beginCell() 18 | .store(storeStateInit({ 19 | code: beginCell().storeUint(1, 8).endCell(), 20 | data: beginCell().storeUint(2, 8).endCell() 21 | })) 22 | .endCell() 23 | .toBoc({ idx: false, crc32: true }); 24 | expect(boc.toString('base64')).toEqual('te6cckEBAwEACwACATQBAgACAQACAoN/wQo=') 25 | 26 | // Parse 27 | let parsed = loadStateInit(Cell.fromBoc(boc)[0].beginParse()); 28 | expect(parsed.libraries).toBeUndefined(); 29 | expect(parsed.special).toBeUndefined(); 30 | expect(parsed.splitDepth).toBeUndefined(); 31 | let codeSlice = parsed.code!.beginParse(); 32 | let a = codeSlice.loadUint(8); 33 | expect(a).toBe(1); 34 | codeSlice.endParse(); 35 | let dataSlice = parsed.data!.beginParse(); 36 | let b = dataSlice.loadUint(8); 37 | expect(b).toBe(2); 38 | dataSlice.endParse(); 39 | }); 40 | }); -------------------------------------------------------------------------------- /src/types/StateInit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Slice } from "../boc/Slice"; 12 | import { Dictionary } from "../dict/Dictionary"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { SimpleLibrary, SimpleLibraryValue } from "./SimpleLibrary"; 15 | import { loadTickTock, storeTickTock, TickTock } from "./TickTock"; 16 | 17 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L141 18 | // _ split_depth:(Maybe (## 5)) special:(Maybe TickTock) 19 | // code:(Maybe ^Cell) data:(Maybe ^Cell) 20 | // library:(HashmapE 256 SimpleLib) = StateInit; 21 | 22 | export interface StateInit { 23 | splitDepth?: Maybe; 24 | special?: Maybe; 25 | code?: Maybe; 26 | data?: Maybe; 27 | libraries?: Maybe>; 28 | }; 29 | 30 | export function loadStateInit(slice: Slice): StateInit { 31 | 32 | // Split Depth 33 | let splitDepth: Maybe; 34 | if (slice.loadBit()) { 35 | splitDepth = slice.loadUint(5); 36 | } 37 | 38 | // TickTock 39 | let special: Maybe 40 | if (slice.loadBit()) { 41 | special = loadTickTock(slice); 42 | } 43 | 44 | // Code and Data 45 | let code = slice.loadMaybeRef(); 46 | let data = slice.loadMaybeRef(); 47 | 48 | // Libs 49 | let libraries: Maybe> = slice.loadDict(Dictionary.Keys.BigUint(256), SimpleLibraryValue); 50 | if (libraries.size === 0) { 51 | libraries = undefined; 52 | } 53 | 54 | return { 55 | splitDepth, 56 | special, 57 | code, 58 | data, 59 | libraries 60 | }; 61 | } 62 | 63 | export function storeStateInit(src: StateInit) { 64 | return (builder: Builder) => { 65 | if (src.splitDepth !== null && src.splitDepth !== undefined) { 66 | builder.storeBit(true); 67 | builder.storeUint(src.splitDepth, 5); 68 | } else { 69 | builder.storeBit(false); 70 | } 71 | if (src.special !== null && src.special !== undefined) { 72 | builder.storeBit(true); 73 | builder.store(storeTickTock(src.special)); 74 | } else { 75 | builder.storeBit(false); 76 | } 77 | builder.storeMaybeRef(src.code); 78 | builder.storeMaybeRef(src.data); 79 | builder.storeDict(src.libraries); 80 | } 81 | } -------------------------------------------------------------------------------- /src/types/StorageExtraInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/3fbab2c601380eba5ba68048f45d24a359bd2936/crypto/block/block.tlb#L250 13 | // storage_extra_none$000 = StorageExtraInfo; 14 | // storage_extra_info$001 dict_hash:uint256 = StorageExtraInfo; 15 | 16 | export type StorageExtraInfo = { 17 | dictHash: bigint; 18 | } 19 | 20 | export function loadStorageExtraInfo(slice: Slice): StorageExtraInfo | null { 21 | let header = slice.loadUint(3); 22 | if (header === 0) { 23 | return null; 24 | } 25 | if (header === 1) { 26 | return { 27 | dictHash: slice.loadUintBig(256), 28 | }; 29 | } 30 | throw new Error(`Invalid storage extra info header: ${header}`); 31 | } 32 | 33 | export function storeStorageExtraInfo(src: StorageExtraInfo | null) { 34 | return (builder: Builder) => { 35 | if (src === null) { 36 | builder.storeUint(0, 3); 37 | } else { 38 | builder.storeUint(1, 3); 39 | builder.storeUint(src.dictHash, 256); 40 | } 41 | }; 42 | } -------------------------------------------------------------------------------- /src/types/StorageInfo.spec.ts: -------------------------------------------------------------------------------- 1 | import { Cell } from '../boc/Cell'; 2 | import { loadStorageInfo } from './StorageInfo'; 3 | 4 | 5 | describe('StorageInfo', () => { 6 | it('should load and store storage info (old)', () => { 7 | let oldAccountState = Cell.fromHex('b5ee9c720102160100033d000273c003f73ef51ce3b828b26804e172844051c3cd5c7bef0cd82888bf1aa0f17a9d75a22c85924341e5ef000000d26513758a0d81240a9c513b934001020114ff00f4a413f4bcf2c80b03005100000e3b29a9a3173e8eecf79d2386032eef34836a57a516e67b8613a8d7cae97add903720310401400201200405020148060704f8f28308d71820d31fd31fd31f02f823bbf264ed44d0d31fd31fd3fff404d15143baf2a15151baf2a205f901541064f910f2a3f80024a4c8cb1f5240cb1f5230cbff5210f400c9ed54f80f01d30721c0009f6c519320d74a96d307d402fb00e830e021c001e30021c002e30001c0039130e30d03a4c8cb1f12cb1fcbff1213141502e6d001d0d3032171b0925f04e022d749c120925f04e002d31f218210706c7567bd22821064737472bdb0925f05e003fa403020fa4401c8ca07cbffc9d0ed44d0810140d721f404305c810108f40a6fa131b3925f07e005d33fc8258210706c7567ba923830e30d03821064737472ba925f06e30d08090201200a0b007801fa00f40430f8276f2230500aa121bef2e0508210706c7567831eb17080185004cb0526cf1658fa0219f400cb6917cb1f5260cb3f20c98040fb0006008a5004810108f45930ed44d0810140d720c801cf16f400c9ed540172b08e23821064737472831eb17080185005cb055003cf1623fa0213cb6acb1fcb3fc98040fb00925f03e20201200c0d0059bd242b6f6a2684080a06b90fa0218470d4080847a4937d29910ce6903e9ff9837812801b7810148987159f31840201580e0f0011b8c97ed44d0d70b1f8003db29dfb513420405035c87d010c00b23281f2fff274006040423d029be84c6002012010110019adce76a26840206b90eb85ffc00019af1df6a26840106b90eb858fc0006ed207fa00d4d422f90005c8ca0715cbffc9d077748018c8cb05cb0222cf165005fa0214cb6b12ccccc973fb00c84014810108f451f2a7020070810108d718fa00d33fc8542047810108f451f2a782106e6f746570748018c8cb05cb025006cf165004fa0214cb6a12cb1fcb3fc973fb0002006c810108d718fa00d33f305224810108f459f2a782106473747270748018c8cb05cb025005cf165003fa0213cb6acb1f12cb3fc973fb00000af400c9ed54'); 8 | let cs = oldAccountState.beginParse(); 9 | cs.skip(1); 10 | cs.loadAddress(); 11 | 12 | let storageInfo = loadStorageInfo(cs); 13 | 14 | expect(storageInfo).toEqual({ 15 | used: { cells: 22n, bits: 5705n }, 16 | storageExtra: null, 17 | lastPaid: 1748811232, 18 | duePayment: null 19 | }); 20 | }); 21 | }); -------------------------------------------------------------------------------- /src/types/StorageInfo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { loadStorageExtraInfo, StorageExtraInfo, storeStorageExtraInfo } from './StorageExtraInfo'; 13 | import { loadStorageUsed, StorageUsed, storeStorageUsed } from "./StorageUsed" 14 | 15 | // Source: https://github.com/ton-blockchain/ton/blob/3fbab2c601380eba5ba68048f45d24a359bd2936/crypto/block/block.tlb#L255 16 | // storage_info$_ used:StorageUsed storage_extra:StorageExtraInfo last_paid:uint32 17 | // due_payment:(Maybe Grams) = StorageInfo; 18 | 19 | export type StorageInfo = { 20 | used: StorageUsed; 21 | storageExtra: StorageExtraInfo | null; 22 | lastPaid: number; 23 | duePayment?: Maybe; 24 | } 25 | 26 | export function loadStorageInfo(slice: Slice): StorageInfo { 27 | return { 28 | used: loadStorageUsed(slice), 29 | storageExtra: loadStorageExtraInfo(slice), 30 | lastPaid: slice.loadUint(32), 31 | duePayment: slice.loadMaybeCoins() 32 | }; 33 | } 34 | 35 | export function storeStorageInfo(src: StorageInfo) { 36 | return (builder: Builder) => { 37 | builder.store(storeStorageUsed(src.used)); 38 | builder.store(storeStorageExtraInfo(src.storageExtra)); 39 | builder.storeUint(src.lastPaid, 32); 40 | builder.storeMaybeCoins(src.duePayment); 41 | }; 42 | } -------------------------------------------------------------------------------- /src/types/StorageUsed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/3fbab2c601380eba5ba68048f45d24a359bd2936/crypto/block/block.tlb#L253 13 | // storage_used$_ cells:(VarUInteger 7) bits:(VarUInteger 7) = StorageUsed; 14 | 15 | export type StorageUsed = { 16 | cells: bigint, 17 | bits: bigint, 18 | }; 19 | 20 | export function loadStorageUsed(cs: Slice): StorageUsed { 21 | return { 22 | cells: cs.loadVarUintBig(3), 23 | bits: cs.loadVarUintBig(3), 24 | } 25 | } 26 | 27 | export function storeStorageUsed(src: StorageUsed) { 28 | return (builder: Builder) => { 29 | builder.storeVarUint(src.cells, 3); 30 | builder.storeVarUint(src.bits, 3); 31 | }; 32 | } -------------------------------------------------------------------------------- /src/types/TickTock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | 12 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L139 13 | // tick_tock$_ tick:Bool tock:Bool = TickTock; 14 | 15 | export type TickTock = { 16 | tick: boolean; 17 | tock: boolean; 18 | } 19 | 20 | export function loadTickTock(slice: Slice): TickTock { 21 | return { 22 | tick: slice.loadBit(), 23 | tock: slice.loadBit() 24 | }; 25 | } 26 | 27 | export function storeTickTock(src: TickTock) { 28 | return (builder: Builder) => { 29 | builder.storeBit(src.tick); 30 | builder.storeBit(src.tock); 31 | } 32 | } -------------------------------------------------------------------------------- /src/types/Transaction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Cell } from '../boc/Cell'; 11 | import { Slice } from "../boc/Slice"; 12 | import { Dictionary } from "../dict/Dictionary"; 13 | import { Maybe } from "../utils/maybe"; 14 | import { AccountStatus, loadAccountStatus, storeAccountStatus } from "./AccountStatus"; 15 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 16 | import { HashUpdate, loadHashUpdate, storeHashUpdate } from "./HashUpdate"; 17 | import { loadMessage, Message, MessageValue, storeMessage } from "./Message"; 18 | import { loadTransactionDescription, storeTransactionDescription, TransactionDescription } from "./TransactionDescription"; 19 | 20 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L263 21 | // transaction$0111 account_addr:bits256 lt:uint64 22 | // prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 23 | // outmsg_cnt:uint15 24 | // orig_status:AccountStatus end_status:AccountStatus 25 | // ^[ in_msg:(Maybe ^(Message Any)) out_msgs:(HashmapE 15 ^(Message Any)) ] 26 | // total_fees:CurrencyCollection state_update:^(HASH_UPDATE Account) 27 | // description:^TransactionDescr = Transaction; 28 | 29 | export type Transaction = { 30 | address: bigint, 31 | lt: bigint, 32 | prevTransactionHash: bigint, 33 | prevTransactionLt: bigint, 34 | now: number, 35 | outMessagesCount: number, 36 | oldStatus: AccountStatus, 37 | endStatus: AccountStatus, 38 | inMessage?: Maybe, 39 | outMessages: Dictionary; 40 | totalFees: CurrencyCollection, 41 | stateUpdate: HashUpdate, 42 | description: TransactionDescription, 43 | raw: Cell, 44 | hash: () => Buffer, 45 | }; 46 | 47 | export function loadTransaction(slice: Slice): Transaction { 48 | let raw = slice.asCell(); 49 | 50 | if (slice.loadUint(4) !== 0x07) { 51 | throw Error('Invalid data'); 52 | } 53 | 54 | let address = slice.loadUintBig(256); 55 | let lt = slice.loadUintBig(64); 56 | let prevTransactionHash = slice.loadUintBig(256); 57 | let prevTransactionLt = slice.loadUintBig(64); 58 | let now = slice.loadUint(32); 59 | let outMessagesCount = slice.loadUint(15); 60 | let oldStatus = loadAccountStatus(slice); 61 | let endStatus = loadAccountStatus(slice); 62 | 63 | let msgRef = slice.loadRef(); 64 | let msgSlice = msgRef.beginParse(); 65 | let inMessage = msgSlice.loadBit() ? loadMessage(msgSlice.loadRef().beginParse()) : undefined; 66 | let outMessages = msgSlice.loadDict(Dictionary.Keys.Uint(15), MessageValue); 67 | msgSlice.endParse(); 68 | 69 | let totalFees = loadCurrencyCollection(slice); 70 | let stateUpdate = loadHashUpdate(slice.loadRef().beginParse()); 71 | let description = loadTransactionDescription(slice.loadRef().beginParse()); 72 | return { 73 | address, 74 | lt, 75 | prevTransactionHash, 76 | prevTransactionLt, 77 | now, 78 | outMessagesCount, 79 | oldStatus, 80 | endStatus, 81 | inMessage, 82 | outMessages, 83 | totalFees, 84 | stateUpdate, 85 | description, 86 | raw, 87 | hash: () => raw.hash(), 88 | }; 89 | } 90 | 91 | export function storeTransaction(src: Transaction) { 92 | return (builder: Builder) => { 93 | builder.storeUint(0x07, 4); 94 | builder.storeUint(src.address, 256); 95 | builder.storeUint(src.lt, 64); 96 | builder.storeUint(src.prevTransactionHash, 256); 97 | builder.storeUint(src.prevTransactionLt, 64); 98 | builder.storeUint(src.now, 32); 99 | builder.storeUint(src.outMessagesCount, 15); 100 | builder.store(storeAccountStatus(src.oldStatus)); 101 | builder.store(storeAccountStatus(src.endStatus)); 102 | 103 | let msgBuilder = beginCell(); 104 | if (src.inMessage) { 105 | msgBuilder.storeBit(true); 106 | msgBuilder.storeRef(beginCell().store(storeMessage(src.inMessage))); 107 | } else { 108 | msgBuilder.storeBit(false); 109 | } 110 | msgBuilder.storeDict(src.outMessages); 111 | builder.storeRef(msgBuilder); 112 | 113 | builder.store(storeCurrencyCollection(src.totalFees)); 114 | builder.storeRef(beginCell().store(storeHashUpdate(src.stateUpdate))); 115 | builder.storeRef(beginCell().store(storeTransactionDescription(src.description))); 116 | }; 117 | } -------------------------------------------------------------------------------- /src/types/TransactionActionPhase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { AccountStatusChange, loadAccountStatusChange, storeAccountStatusChange } from "./AccountStatusChange"; 13 | import { loadStorageUsedShort, StorageUsedShort, storeStorageUsedShort } from "./StorageUsedShort"; 14 | 15 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L310 16 | // tr_phase_action$_ success:Bool valid:Bool no_funds:Bool 17 | // status_change:AccStatusChange 18 | // total_fwd_fees:(Maybe Grams) total_action_fees:(Maybe Grams) 19 | // result_code:int32 result_arg:(Maybe int32) tot_actions:uint16 20 | // spec_actions:uint16 skipped_actions:uint16 msgs_created:uint16 21 | // action_list_hash:bits256 tot_msg_size:StorageUsedShort 22 | // = TrActionPhase; 23 | 24 | export type TransactionActionPhase = { 25 | success: boolean; 26 | valid: boolean; 27 | noFunds: boolean; 28 | statusChange: AccountStatusChange; 29 | totalFwdFees?: Maybe; 30 | totalActionFees?: Maybe; 31 | resultCode: number; 32 | resultArg?: Maybe; 33 | totalActions: number; 34 | specActions: number; 35 | skippedActions: number; 36 | messagesCreated: number; 37 | actionListHash: bigint; 38 | totalMessageSize: StorageUsedShort; 39 | } 40 | 41 | export function loadTransactionActionPhase(slice: Slice): TransactionActionPhase { 42 | let success = slice.loadBit(); 43 | let valid = slice.loadBit(); 44 | let noFunds = slice.loadBit(); 45 | let statusChange = loadAccountStatusChange(slice); 46 | let totalFwdFees = slice.loadBit() ? slice.loadCoins() : undefined; 47 | let totalActionFees = slice.loadBit() ? slice.loadCoins() : undefined; 48 | let resultCode = slice.loadInt(32); 49 | let resultArg = slice.loadBit() ? slice.loadInt(32) : undefined; 50 | let totalActions = slice.loadUint(16); 51 | let specActions = slice.loadUint(16); 52 | let skippedActions = slice.loadUint(16); 53 | let messagesCreated = slice.loadUint(16); 54 | let actionListHash = slice.loadUintBig(256); 55 | let totalMessageSize = loadStorageUsedShort(slice); 56 | return { 57 | success, 58 | valid, 59 | noFunds, 60 | statusChange, 61 | totalFwdFees, 62 | totalActionFees, 63 | resultCode, 64 | resultArg, 65 | totalActions, 66 | specActions, 67 | skippedActions, 68 | messagesCreated, 69 | actionListHash, 70 | totalMessageSize 71 | }; 72 | } 73 | 74 | export function storeTransactionActionPhase(src: TransactionActionPhase) { 75 | return (builder: Builder) => { 76 | builder.storeBit(src.success); 77 | builder.storeBit(src.valid); 78 | builder.storeBit(src.noFunds); 79 | builder.store(storeAccountStatusChange(src.statusChange)); 80 | builder.storeMaybeCoins(src.totalFwdFees); 81 | builder.storeMaybeCoins(src.totalActionFees); 82 | builder.storeInt(src.resultCode, 32); 83 | builder.storeMaybeInt(src.resultArg, 32); 84 | builder.storeUint(src.totalActions, 16); 85 | builder.storeUint(src.specActions, 16); 86 | builder.storeUint(src.skippedActions, 16); 87 | builder.storeUint(src.messagesCreated, 16); 88 | builder.storeUint(src.actionListHash, 256); 89 | builder.store(storeStorageUsedShort(src.totalMessageSize)); 90 | }; 91 | } -------------------------------------------------------------------------------- /src/types/TransactionBouncePhase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { loadStorageUsedShort, StorageUsedShort, storeStorageUsedShort } from "./StorageUsedShort"; 12 | 13 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L318 14 | // tr_phase_bounce_negfunds$00 = TrBouncePhase; 15 | // tr_phase_bounce_nofunds$01 msg_size:StorageUsedShort req_fwd_fees:Grams = TrBouncePhase; 16 | // tr_phase_bounce_ok$1 msg_size:StorageUsedShort msg_fees:Grams fwd_fees:Grams = TrBouncePhase; 17 | 18 | export type TransactionBouncePhase = 19 | | TransactionBounceNegativeFunds 20 | | TransactionBounceNoFunds 21 | | TransactionBounceOk; 22 | 23 | export type TransactionBounceNegativeFunds = { 24 | type: "negative-funds"; 25 | } 26 | 27 | export type TransactionBounceNoFunds = { 28 | type: "no-funds"; 29 | messageSize: StorageUsedShort; 30 | requiredForwardFees: bigint; 31 | }; 32 | 33 | export type TransactionBounceOk = { 34 | type: "ok"; 35 | messageSize: StorageUsedShort; 36 | messageFees: bigint; 37 | forwardFees: bigint; 38 | } 39 | 40 | export function loadTransactionBouncePhase(slice: Slice): TransactionBouncePhase { 41 | 42 | // Ok 43 | if (slice.loadBit()) { 44 | let messageSize = loadStorageUsedShort(slice); 45 | let messageFees = slice.loadCoins(); 46 | let forwardFees = slice.loadCoins(); 47 | return { 48 | type: "ok", 49 | messageSize, 50 | messageFees, 51 | forwardFees, 52 | }; 53 | } 54 | 55 | // No funds 56 | if (slice.loadBit()) { 57 | let messageSize = loadStorageUsedShort(slice); 58 | let requiredForwardFees = slice.loadCoins(); 59 | return { 60 | type: "no-funds", 61 | messageSize, 62 | requiredForwardFees, 63 | }; 64 | } 65 | 66 | // Negative funds 67 | return { 68 | type: "negative-funds", 69 | }; 70 | } 71 | 72 | export function storeTransactionBouncePhase(src: TransactionBouncePhase) { 73 | return (builder: Builder) => { 74 | if (src.type === 'ok') { 75 | builder.storeBit(true); 76 | builder.store(storeStorageUsedShort(src.messageSize)); 77 | builder.storeCoins(src.messageFees); 78 | builder.storeCoins(src.forwardFees); 79 | } else if (src.type === 'negative-funds') { 80 | builder.storeBit(false); 81 | builder.storeBit(false); 82 | } else if (src.type === 'no-funds') { 83 | builder.storeBit(false); 84 | builder.storeBit(true); 85 | builder.store(storeStorageUsedShort(src.messageSize)); 86 | builder.storeCoins(src.requiredForwardFees); 87 | } else { 88 | throw new Error("Invalid TransactionBouncePhase type"); 89 | } 90 | }; 91 | } -------------------------------------------------------------------------------- /src/types/TransactionComputePhase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { beginCell, Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { ComputeSkipReason, loadComputeSkipReason, storeComputeSkipReason } from "./ComputeSkipReason"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L296 15 | // tr_phase_compute_skipped$0 reason:ComputeSkipReason 16 | // = TrComputePhase; 17 | // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool 18 | // account_activated:Bool gas_fees:Grams 19 | // ^[ gas_used:(VarUInteger 7) 20 | // gas_limit:(VarUInteger 7) gas_credit:(Maybe (VarUInteger 3)) 21 | // mode:int8 exit_code:int32 exit_arg:(Maybe int32) 22 | // vm_steps:uint32 23 | // vm_init_state_hash:bits256 vm_final_state_hash:bits256 ] 24 | // = TrComputePhase; 25 | 26 | export type TransactionComputePhase = TransactionComputeSkipped | TransactionComputeVm; 27 | export type TransactionComputeSkipped = { type: 'skipped', reason: ComputeSkipReason }; 28 | export type TransactionComputeVm = { 29 | type: 'vm', 30 | success: boolean, 31 | messageStateUsed: boolean, 32 | accountActivated: boolean, 33 | gasFees: bigint, 34 | gasUsed: bigint, 35 | gasLimit: bigint, 36 | gasCredit?: Maybe, 37 | mode: number, 38 | exitCode: number, 39 | exitArg?: Maybe, 40 | vmSteps: number, 41 | vmInitStateHash: bigint, 42 | vmFinalStateHash: bigint 43 | } 44 | 45 | export function loadTransactionComputePhase(slice: Slice): TransactionComputePhase { 46 | 47 | // Skipped 48 | if (!slice.loadBit()) { 49 | let reason = loadComputeSkipReason(slice); 50 | return { 51 | type: 'skipped', 52 | reason 53 | }; 54 | } 55 | 56 | let success = slice.loadBit(); 57 | let messageStateUsed = slice.loadBit(); 58 | let accountActivated = slice.loadBit(); 59 | let gasFees = slice.loadCoins(); 60 | 61 | const vmState = slice.loadRef().beginParse(); 62 | let gasUsed = vmState.loadVarUintBig(3); 63 | let gasLimit = vmState.loadVarUintBig(3); 64 | let gasCredit = vmState.loadBit() ? vmState.loadVarUintBig(2) : undefined; 65 | let mode = vmState.loadUint(8); 66 | let exitCode = vmState.loadInt(32); 67 | let exitArg = vmState.loadBit() ? vmState.loadInt(32) : undefined; 68 | let vmSteps = vmState.loadUint(32); 69 | let vmInitStateHash = vmState.loadUintBig(256); 70 | let vmFinalStateHash = vmState.loadUintBig(256); 71 | return { 72 | type: 'vm', 73 | success, 74 | messageStateUsed, 75 | accountActivated, 76 | gasFees, 77 | gasUsed, 78 | gasLimit, 79 | gasCredit, 80 | mode, 81 | exitCode, 82 | exitArg, 83 | vmSteps, 84 | vmInitStateHash, 85 | vmFinalStateHash 86 | }; 87 | } 88 | 89 | export function storeTransactionComputePhase(src: TransactionComputePhase) { 90 | return (builder: Builder) => { 91 | if (src.type === 'skipped') { 92 | builder.storeBit(0); 93 | builder.store(storeComputeSkipReason(src.reason)); 94 | return; 95 | } 96 | builder.storeBit(1); 97 | builder.storeBit(src.success); 98 | builder.storeBit(src.messageStateUsed); 99 | builder.storeBit(src.accountActivated); 100 | builder.storeCoins(src.gasFees); 101 | builder.storeRef(beginCell() 102 | .storeVarUint(src.gasUsed, 3) 103 | .storeVarUint(src.gasLimit, 3) 104 | .store((b) => (src.gasCredit !== undefined && src.gasCredit !== null) ? b.storeBit(1).storeVarUint(src.gasCredit, 2) : b.storeBit(0)) 105 | .storeUint(src.mode, 8) 106 | .storeInt(src.exitCode, 32) 107 | .store((b) => (src.exitArg !== undefined && src.exitArg !== null) ? b.storeBit(1).storeInt(src.exitArg, 32) : b.storeBit(0)) 108 | .storeUint(src.vmSteps, 32) 109 | .storeUint(src.vmInitStateHash, 256) 110 | .storeUint(src.vmFinalStateHash, 256) 111 | .endCell()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/types/TransactionCreditPhase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { CurrencyCollection, loadCurrencyCollection, storeCurrencyCollection } from "./CurrencyCollection"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L293 15 | // tr_phase_credit$_ due_fees_collected:(Maybe Grams) 16 | // credit:CurrencyCollection = TrCreditPhase; 17 | 18 | export type TransactionCreditPhase = { 19 | dueFeesColelcted?: Maybe, 20 | credit: CurrencyCollection 21 | }; 22 | 23 | export function loadTransactionCreditPhase(slice: Slice): TransactionCreditPhase { 24 | const dueFeesColelcted = slice.loadBit() ? slice.loadCoins() : undefined; 25 | const credit = loadCurrencyCollection(slice); 26 | return { 27 | dueFeesColelcted, 28 | credit 29 | }; 30 | } 31 | 32 | export function storeTransactionCreditPhase(src: TransactionCreditPhase) { 33 | return (builder: Builder) => { 34 | if (src.dueFeesColelcted === null || src.dueFeesColelcted === undefined) { 35 | builder.storeBit(false); 36 | } else { 37 | builder.storeBit(true); 38 | builder.storeCoins(src.dueFeesColelcted); 39 | } 40 | builder.store(storeCurrencyCollection(src.credit)); 41 | }; 42 | } -------------------------------------------------------------------------------- /src/types/TransactionStoragePhase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Builder } from "../boc/Builder"; 10 | import { Slice } from "../boc/Slice"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { AccountStatusChange, loadAccountStatusChange, storeAccountStatusChange } from "./AccountStatusChange"; 13 | 14 | // Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L284 15 | // tr_phase_storage$_ storage_fees_collected:Grams 16 | // storage_fees_due:(Maybe Grams) 17 | // status_change:AccStatusChange 18 | // = TrStoragePhase; 19 | 20 | export type TransactionStoragePhase = { 21 | storageFeesCollected: bigint, 22 | storageFeesDue?: Maybe, 23 | statusChange: AccountStatusChange 24 | }; 25 | 26 | export function loadTransactionStoragePhase(slice: Slice): TransactionStoragePhase { 27 | const storageFeesCollected = slice.loadCoins(); 28 | let storageFeesDue: Maybe = undefined; 29 | if (slice.loadBit()) { 30 | storageFeesDue = slice.loadCoins(); 31 | } 32 | const statusChange = loadAccountStatusChange(slice); 33 | return { 34 | storageFeesCollected, 35 | storageFeesDue, 36 | statusChange 37 | }; 38 | } 39 | 40 | export function storeTransactionsStoragePhase(src: TransactionStoragePhase) { 41 | return (builder: Builder) => { 42 | builder.storeCoins(src.storageFeesCollected); 43 | if (src.storageFeesDue === null || src.storageFeesDue === undefined) { 44 | builder.storeBit(false); 45 | } else { 46 | builder.storeBit(true); 47 | builder.storeCoins(src.storageFeesDue); 48 | } 49 | builder.store(storeAccountStatusChange(src.statusChange)); 50 | }; 51 | } -------------------------------------------------------------------------------- /src/types/_export.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export { 10 | internal, 11 | external, 12 | comment 13 | } from './_helpers'; 14 | export { 15 | Account, 16 | loadAccount, 17 | storeAccount 18 | } from './Account'; 19 | export { 20 | AccountState, 21 | loadAccountState, 22 | storeAccountState 23 | } from './AccountState'; 24 | export { 25 | AccountStatus, 26 | loadAccountStatus, 27 | storeAccountStatus 28 | } from './AccountStatus'; 29 | export { 30 | AccountStatusChange, 31 | loadAccountStatusChange, 32 | storeAccountStatusChange 33 | } from './AccountStatusChange'; 34 | export { 35 | AccountStorage, 36 | loadAccountStorage, 37 | storeAccountStorage 38 | } from './AccountStorage'; 39 | export { 40 | OutActionSendMsg, 41 | OutActionSetCode, 42 | OutActionReserve, 43 | OutActionChangeLibrary, 44 | OutAction, 45 | loadOutAction, 46 | storeOutAction, 47 | loadOutList, 48 | storeOutList 49 | } from './OutList'; 50 | export { 51 | CommonMessageInfo, 52 | CommonMessageInfoInternal, 53 | CommonMessageInfoExternalIn, 54 | CommonMessageInfoExternalOut, 55 | loadCommonMessageInfo, 56 | storeCommonMessageInfo 57 | } from './CommonMessageInfo'; 58 | export { 59 | CommonMessageInfoRelaxed, 60 | CommonMessageInfoRelaxedExternalOut, 61 | CommonMessageInfoRelaxedInternal, 62 | loadCommonMessageInfoRelaxed, 63 | storeCommonMessageInfoRelaxed 64 | } from './CommonMessageInfoRelaxed'; 65 | export { 66 | ComputeSkipReason, 67 | loadComputeSkipReason, 68 | storeComputeSkipReason 69 | } from './ComputeSkipReason'; 70 | export { 71 | CurrencyCollection, 72 | loadCurrencyCollection, 73 | storeCurrencyCollection 74 | } from './CurrencyCollection'; 75 | export { 76 | DepthBalanceInfo, 77 | loadDepthBalanceInfo, 78 | storeDepthBalanceInfo 79 | } from './DepthBalanceInfo'; 80 | export { 81 | ExtraCurrency, 82 | packExtraCurrencyCell, 83 | packExtraCurrencyDict, 84 | loadExtraCurrency, 85 | loadMaybeExtraCurrency, 86 | storeExtraCurrency 87 | } from './ExtraCurrency'; 88 | export { 89 | HashUpdate, 90 | loadHashUpdate, 91 | storeHashUpdate 92 | } from './HashUpdate'; 93 | export { 94 | MasterchainStateExtra, 95 | loadMasterchainStateExtra 96 | } from './MasterchainStateExtra'; 97 | export { 98 | Message, 99 | loadMessage, 100 | storeMessage 101 | } from './Message'; 102 | export { 103 | MessageRelaxed, 104 | loadMessageRelaxed, 105 | storeMessageRelaxed 106 | } from './MessageRelaxed'; 107 | export { 108 | SendMode 109 | } from './SendMode'; 110 | export { 111 | ReserveMode 112 | } from './ReserveMode'; 113 | export { 114 | ShardAccount, 115 | loadShardAccount, 116 | storeShardAccount 117 | } from './ShardAccount'; 118 | export { 119 | ShardAccountRef, 120 | ShardAccountRefValue, 121 | loadShardAccounts, 122 | storeShardAccounts 123 | } from './ShardAccounts'; 124 | export { 125 | ShardIdent, 126 | loadShardIdent, 127 | storeShardIdent 128 | } from './ShardIdent'; 129 | export { 130 | ShardStateUnsplit, 131 | loadShardStateUnsplit 132 | } from './ShardStateUnsplit'; 133 | export { 134 | SimpleLibrary, 135 | loadSimpleLibrary, 136 | storeSimpleLibrary 137 | } from './SimpleLibrary'; 138 | export { 139 | LibRef, 140 | loadLibRef, 141 | storeLibRef 142 | } from './LibRef'; 143 | export { 144 | SplitMergeInfo, 145 | loadSplitMergeInfo, 146 | storeSplitMergeInfo 147 | } from './SplitMergeInfo'; 148 | export { 149 | StateInit, 150 | loadStateInit, 151 | storeStateInit 152 | } from './StateInit'; 153 | export { 154 | StorageInfo, 155 | loadStorageInfo, 156 | storeStorageInfo 157 | } from './StorageInfo'; 158 | export { 159 | StorageUsed, 160 | loadStorageUsed, 161 | storeStorageUsed 162 | } from './StorageUsed'; 163 | export { 164 | TickTock, 165 | loadTickTock, 166 | storeTickTock 167 | } from './TickTock'; 168 | export { 169 | Transaction, 170 | loadTransaction, 171 | storeTransaction 172 | } from './Transaction'; 173 | export { 174 | TransactionActionPhase, 175 | loadTransactionActionPhase, 176 | storeTransactionActionPhase 177 | } from './TransactionActionPhase'; 178 | export { 179 | TransactionBouncePhase, 180 | TransactionBounceNoFunds, 181 | TransactionBounceNegativeFunds, 182 | TransactionBounceOk, 183 | loadTransactionBouncePhase, 184 | storeTransactionBouncePhase 185 | } from './TransactionBouncePhase'; 186 | export { 187 | TransactionComputeVm, 188 | TransactionComputePhase, 189 | TransactionComputeSkipped, 190 | loadTransactionComputePhase, 191 | storeTransactionComputePhase 192 | } from './TransactionComputePhase'; 193 | export { 194 | TransactionCreditPhase, 195 | loadTransactionCreditPhase, 196 | storeTransactionCreditPhase 197 | } from './TransactionCreditPhase'; 198 | export { 199 | TransactionDescription, 200 | TransactionDescriptionGeneric, 201 | TransactionDescriptionMergeInstall, 202 | TransactionDescriptionMergePrepare, 203 | TransactionDescriptionSplitInstall, 204 | TransactionDescriptionSplitPrepare, 205 | TransactionDescriptionStorage, 206 | TransactionDescriptionTickTock, 207 | loadTransactionDescription, 208 | storeTransactionDescription 209 | } from './TransactionDescription'; 210 | export { 211 | TransactionStoragePhase, 212 | loadTransactionStoragePhase, 213 | storeTransactionsStoragePhase 214 | } from './TransactionStoragePhase'; 215 | -------------------------------------------------------------------------------- /src/types/_helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { Address } from "../address/Address"; 10 | import { Cell } from "../boc/Cell"; 11 | import { Maybe } from "../utils/maybe"; 12 | import { beginCell } from "../boc/Builder"; 13 | import { toNano } from "../utils/convert"; 14 | import { MessageRelaxed } from "./MessageRelaxed"; 15 | import { Message } from "./Message"; 16 | import { Dictionary } from "../dict/Dictionary"; 17 | import { StateInit } from "./StateInit"; 18 | import { ExtraCurrency, packExtraCurrencyDict } from "./ExtraCurrency"; 19 | 20 | export function internal(src: { 21 | to: Address | string, 22 | value: bigint | string, 23 | extracurrency?: Maybe, 24 | bounce?: Maybe, 25 | init?: Maybe, 26 | body?: Maybe 27 | }): MessageRelaxed { 28 | 29 | // Resolve bounce 30 | let bounce = true; 31 | if (src.bounce !== null && src.bounce !== undefined) { 32 | bounce = src.bounce; 33 | } 34 | 35 | // Resolve address 36 | let to: Address; 37 | if (typeof src.to === 'string') { 38 | to = Address.parse(src.to); 39 | } else if (Address.isAddress(src.to)) { 40 | to = src.to; 41 | } else { 42 | throw new Error(`Invalid address ${src.to}`); 43 | } 44 | 45 | // Resolve value 46 | let value: bigint; 47 | if (typeof src.value === 'string') { 48 | value = toNano(src.value); 49 | } else { 50 | value = src.value; 51 | } 52 | 53 | let other: Dictionary | undefined; 54 | if(src.extracurrency) { 55 | // Resolve value 56 | other = packExtraCurrencyDict(src.extracurrency); 57 | } 58 | 59 | // Resolve body 60 | let body: Cell = Cell.EMPTY; 61 | if (typeof src.body === 'string') { 62 | body = beginCell().storeUint(0, 32).storeStringTail(src.body).endCell(); 63 | } else if (src.body) { 64 | body = src.body; 65 | } 66 | 67 | // Create message 68 | return { 69 | info: { 70 | type: 'internal', 71 | dest: to, 72 | value: { coins: value, other }, 73 | bounce, 74 | ihrDisabled: true, 75 | bounced: false, 76 | ihrFee: 0n, 77 | forwardFee: 0n, 78 | createdAt: 0, 79 | createdLt: 0n 80 | }, 81 | init: src.init ?? undefined, 82 | body: body 83 | }; 84 | } 85 | 86 | export function external(src: { 87 | to: Address | string, 88 | init?: Maybe, 89 | body?: Maybe 90 | }): Message { 91 | 92 | // Resolve address 93 | let to: Address; 94 | if (typeof src.to === 'string') { 95 | to = Address.parse(src.to); 96 | } else if (Address.isAddress(src.to)) { 97 | to = src.to; 98 | } else { 99 | throw new Error(`Invalid address ${src.to}`); 100 | } 101 | 102 | return { 103 | info: { 104 | type: 'external-in', 105 | dest: to, 106 | importFee: 0n 107 | }, 108 | init: src.init ?? undefined, 109 | body: src.body || Cell.EMPTY 110 | }; 111 | } 112 | 113 | export function comment(src: string) { 114 | return beginCell() 115 | .storeUint(0, 32) 116 | .storeStringTail(src) 117 | .endCell(); 118 | } -------------------------------------------------------------------------------- /src/utils/base32.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { base32Decode, base32Encode } from "./base32"; 10 | 11 | describe('base32', () => { 12 | it('should encode and decode', () => { 13 | expect(base32Decode('fvcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3')) 14 | .toEqual(Buffer.from('2D45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD74BB', 'hex')); 15 | expect(base32Encode(Buffer.from('2D45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD74BB', 'hex'))) 16 | .toEqual('fvcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3'); 17 | }); 18 | }); -------------------------------------------------------------------------------- /src/utils/base32.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | const alphabet = 'abcdefghijklmnopqrstuvwxyz234567'; 10 | 11 | export function base32Encode(buffer: Buffer): string { 12 | const length = buffer.byteLength; 13 | let bits = 0; 14 | let value = 0; 15 | let output = ''; 16 | 17 | for (let i = 0; i < length; i++) { 18 | value = (value << 8) | buffer[i]!; 19 | bits += 8; 20 | 21 | while (bits >= 5) { 22 | output += alphabet[(value >>> (bits - 5)) & 31]; 23 | bits -= 5; 24 | } 25 | } 26 | if (bits > 0) { 27 | output += alphabet[(value << (5 - bits)) & 31]; 28 | } 29 | return output; 30 | } 31 | 32 | function readChar(alphabet: string, char: string): number { 33 | const idx = alphabet.indexOf(char); 34 | 35 | if (idx === -1) { 36 | throw new Error('Invalid character found: ' + char); 37 | } 38 | 39 | return idx; 40 | } 41 | 42 | export function base32Decode(input: string): Buffer { 43 | let cleanedInput: string; 44 | cleanedInput = input.toLowerCase(); 45 | 46 | const { length } = cleanedInput; 47 | 48 | let bits = 0; 49 | let value = 0; 50 | 51 | let index = 0; 52 | const output = Buffer.alloc(((length * 5) / 8) | 0); 53 | 54 | for (let i = 0; i < length; i++) { 55 | value = (value << 5) | readChar(alphabet, cleanedInput[i]!); 56 | bits += 5; 57 | 58 | if (bits >= 8) { 59 | output[index++] = (value >>> (bits - 8)) & 255; 60 | bits -= 8; 61 | } 62 | } 63 | 64 | return output; 65 | } -------------------------------------------------------------------------------- /src/utils/bitsForNumber.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { bitsForNumber } from "./bitsForNumber"; 10 | 11 | describe('bitsForNumber', () => { 12 | it('should work', () => { 13 | expect(bitsForNumber(0, 'int')).toBe(1); 14 | expect(bitsForNumber(1, 'int')).toBe(2); 15 | expect(bitsForNumber(-1, 'int')).toBe(1); 16 | expect(bitsForNumber(-2, 'int')).toBe(3); 17 | }); 18 | }); -------------------------------------------------------------------------------- /src/utils/bitsForNumber.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export function bitsForNumber(src: bigint | number, mode: 'int' | 'uint'): number { 10 | let v = BigInt(src); 11 | 12 | // Handle negative values 13 | if (mode === 'int') { 14 | 15 | // Corner case for zero or -1 value 16 | if (v === 0n || v === -1n) { 17 | return 1; 18 | } 19 | 20 | let v2 = v > 0 ? v : -v; 21 | return (v2.toString(2).length + 1/* Sign bit */); 22 | } else if (mode === 'uint') { 23 | if (v < 0) { 24 | throw Error(`value is negative. Got ${src}`); 25 | } 26 | return (v.toString(2).length); 27 | } else { 28 | throw Error(`invalid mode. Got ${mode}`); 29 | } 30 | } -------------------------------------------------------------------------------- /src/utils/convert.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { fromNano, toNano } from "./convert"; 10 | 11 | const stringCases: { nano: string, real: string }[] = [ 12 | { real: '1', nano: '1000000000' }, 13 | { real: '10', nano: '10000000000' }, 14 | { real: '0.1', nano: '100000000' }, 15 | { real: '0.33', nano: '330000000' }, 16 | { real: '0.000000001', nano: '1' }, 17 | { real: '10.000000001', nano: '10000000001' }, 18 | { real: '1000000.000000001', nano: '1000000000000001' }, 19 | { real: '100000000000', nano: '100000000000000000000' }, 20 | ]; 21 | 22 | const numberCases: { nano: string, real: number }[] = [ 23 | { real: -0, nano: '0' }, 24 | { real: 0, nano: '0' }, 25 | { real: 1e64, nano: '10000000000000000000000000000000000000000000000000000000000000000000000000' }, 26 | { real: 1, nano: '1000000000' }, 27 | { real: 10, nano: '10000000000' }, 28 | { real: 0.1, nano: '100000000' }, 29 | { real: 0.33, nano: '330000000' }, 30 | { real: 0.000000001, nano: '1' }, 31 | { real: 10.000000001, nano: '10000000001' }, 32 | { real: 1000000.000000001, nano: '1000000000000001' }, 33 | { real: 100000000000, nano: '100000000000000000000' }, 34 | ]; 35 | 36 | describe('convert', () => { 37 | it('should throw an error for NaN', () => { 38 | expect(() => toNano(NaN)).toThrow(); 39 | }); 40 | it('should throw an error for Infinity', () => { 41 | expect(() => toNano(Infinity)).toThrow(); 42 | }); 43 | it('should throw an error for -Infinity', () => { 44 | expect(() => toNano(-Infinity)).toThrow(); 45 | }); 46 | it('should throw an error due to insufficient precision of number', () => { 47 | expect(() => toNano(10000000.000000001)).toThrow(); 48 | }); 49 | it('should convert numbers toNano', () => { 50 | for (let r of numberCases) { 51 | let c = toNano(r.real); 52 | expect(c).toBe(BigInt(r.nano)); 53 | } 54 | }); 55 | it('should convert strings toNano', () => { 56 | for (let r of stringCases) { 57 | let c = toNano(r.real); 58 | expect(c).toBe(BigInt(r.nano)); 59 | } 60 | }); 61 | it('should convert fromNano', () => { 62 | for (let r of stringCases) { 63 | let c = fromNano(r.nano); 64 | expect(c).toEqual(r.real); 65 | } 66 | }); 67 | }); -------------------------------------------------------------------------------- /src/utils/convert.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export function toNano(src: number | string | bigint): bigint { 10 | 11 | if (typeof src === 'bigint') { 12 | return src * 1000000000n; 13 | } else { 14 | if (typeof src === 'number') { 15 | if (!Number.isFinite(src)) { 16 | throw Error('Invalid number'); 17 | } 18 | 19 | if (Math.log10(src) <= 6) { 20 | src = src.toLocaleString('en', { minimumFractionDigits: 9, useGrouping: false }); 21 | } else if (src - Math.trunc(src) === 0) { 22 | src = src.toLocaleString('en', { maximumFractionDigits: 0, useGrouping: false }); 23 | } else { 24 | throw Error('Not enough precision for a number value. Use string value instead'); 25 | } 26 | } 27 | 28 | // Check sign 29 | let neg = false; 30 | while (src.startsWith('-')) { 31 | neg = !neg; 32 | src = src.slice(1); 33 | } 34 | 35 | // Split string 36 | if (src === '.') { 37 | throw Error('Invalid number'); 38 | } 39 | let parts = src.split('.'); 40 | if (parts.length > 2) { 41 | throw Error('Invalid number'); 42 | } 43 | 44 | // Prepare parts 45 | let whole = parts[0]; 46 | let frac = parts[1]; 47 | if (!whole) { 48 | whole = '0'; 49 | } 50 | if (!frac) { 51 | frac = '0'; 52 | } 53 | if (frac.length > 9) { 54 | throw Error('Invalid number'); 55 | } 56 | while (frac.length < 9) { 57 | frac += '0'; 58 | } 59 | 60 | // Convert 61 | let r = BigInt(whole) * 1000000000n + BigInt(frac); 62 | if (neg) { 63 | r = -r; 64 | } 65 | return r; 66 | } 67 | } 68 | 69 | export function fromNano(src: bigint | number | string) { 70 | let v = BigInt(src); 71 | let neg = false; 72 | if (v < 0) { 73 | neg = true; 74 | v = -v; 75 | } 76 | 77 | // Convert fraction 78 | let frac = v % 1000000000n; 79 | let facStr = frac.toString(); 80 | while (facStr.length < 9) { 81 | facStr = '0' + facStr; 82 | } 83 | facStr = facStr.match(/^([0-9]*[1-9]|0)(0*)/)![1]; 84 | 85 | // Convert whole 86 | let whole = v / 1000000000n; 87 | let wholeStr = whole.toString(); 88 | 89 | // Value 90 | let value = `${wholeStr}${facStr === '0' ? '' : `.${facStr}`}`; 91 | if (neg) { 92 | value = '-' + value; 93 | } 94 | 95 | return value; 96 | } -------------------------------------------------------------------------------- /src/utils/crc16.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { crc16 } from "./crc16"; 10 | 11 | describe('crc16', () => { 12 | it('should match test vector', () => { 13 | expect(crc16(Buffer.from('123456789'))).toEqual(Buffer.from('31c3', 'hex')); 14 | }); 15 | }); -------------------------------------------------------------------------------- /src/utils/crc16.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export function crc16(data: Buffer) { 10 | const poly = 0x1021; 11 | let reg = 0; 12 | const message = Buffer.alloc(data.length + 2); 13 | message.set(data); 14 | for (let byte of message) { 15 | let mask = 0x80; 16 | while (mask > 0) { 17 | reg <<= 1; 18 | if (byte & mask) { 19 | reg += 1; 20 | } 21 | mask >>= 1 22 | if (reg > 0xffff) { 23 | reg &= 0xffff; 24 | reg ^= poly; 25 | } 26 | } 27 | } 28 | return Buffer.from([Math.floor(reg / 256), reg % 256]); 29 | } -------------------------------------------------------------------------------- /src/utils/crc32c.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import { crc32c } from "./crc32c"; 10 | 11 | describe('src32c', () => { 12 | it('should match test vector', () => { 13 | expect(crc32c(Buffer.from('123456789'))).toEqual(Buffer.from('839206e3', 'hex')); 14 | }); 15 | }); -------------------------------------------------------------------------------- /src/utils/crc32c.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | const POLY = 0x82f63b78; 10 | 11 | export function crc32c(source: Buffer) { 12 | let crc = 0 ^ 0xffffffff; 13 | for (let n = 0; n < source.length; n++) { 14 | crc ^= source[n]; 15 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 16 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 17 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 18 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 19 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 20 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 21 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 22 | crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; 23 | } 24 | crc = crc ^ 0xffffffff; 25 | 26 | // Convert endianness 27 | let res = Buffer.alloc(4); 28 | res.writeInt32LE(crc); 29 | return res; 30 | } -------------------------------------------------------------------------------- /src/utils/getMethodId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | const TABLE = new Int16Array([ 10 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 11 | 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 12 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 13 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 14 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 15 | 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 16 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 17 | 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 18 | 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 19 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 20 | 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 21 | 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 22 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 23 | 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 24 | 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 25 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 26 | 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 27 | 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 28 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 29 | 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 30 | 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 31 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 32 | 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 33 | 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 34 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 35 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 36 | 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 37 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 38 | 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 39 | 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 40 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 41 | 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 42 | ]) 43 | 44 | function crc16(data: string | Buffer) { 45 | if (!(data instanceof Buffer)) { 46 | data = Buffer.from(data) 47 | } 48 | 49 | let crc = 0 50 | 51 | for (let index = 0; index < data.length; index++) { 52 | const byte = data[index] 53 | crc = (TABLE[((crc >> 8) ^ byte) & 0xff] ^ (crc << 8)) & 0xffff 54 | } 55 | 56 | return crc 57 | } 58 | 59 | export function getMethodId(name: string) { 60 | return (crc16(name) & 0xffff) | 0x10000; 61 | } -------------------------------------------------------------------------------- /src/utils/maybe.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | export type Maybe = T | null | undefined; -------------------------------------------------------------------------------- /src/utils/testAddress.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Whales Corp. 3 | * All Rights Reserved. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | import Prando from "prando"; 10 | import { Address } from "../address/Address"; 11 | import { ExternalAddress } from "../address/ExternalAddress"; 12 | import { bitsForNumber } from "./bitsForNumber"; 13 | 14 | export function testAddress(workchain: number, seed: string) { 15 | const random = new Prando(seed); 16 | const hash = Buffer.alloc(32); 17 | for (let i = 0; i < hash.length; i++) { 18 | hash[i] = random.nextInt(0, 255); 19 | } 20 | return new Address(workchain, hash); 21 | } 22 | 23 | 24 | export function testExternalAddress(seed: string) { 25 | const random = new Prando(seed); 26 | const hash = Buffer.alloc(32); 27 | for (let i = 0; i < hash.length; i++) { 28 | hash[i] = random.nextInt(0, 255); 29 | } 30 | let v = BigInt('0x' + hash.toString('hex')); 31 | return new ExternalAddress(v, bitsForNumber(v, 'uint')); 32 | } 33 | --------------------------------------------------------------------------------