Browse Source

on-chain-writing-fix

master
AlexG 2 years ago
parent
commit
2b1e13c859
  1. 1
      package.json
  2. 131
      sources/utils/jetton-helpers.ts

1
package.json

@ -12,6 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@types/jest": "^29.2.4", "@types/jest": "^29.2.4",
"@aws-crypto/sha256-js": "^3.0.0",
"@types/node": "^18.11.14", "@types/node": "^18.11.14",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"jest": "^29.3.1", "jest": "^29.3.1",

131
sources/utils/jetton-helpers.ts

@ -1,99 +1,62 @@
import {Address, Cell, Builder, beginCell} from "ton"; import { Sha256 } from "@aws-crypto/sha256-js";
import { beginCell, Cell } from "ton";
import { Dictionary } from "ton-core";
function bufferToChunks(buff: Buffer, chunkSize: number) {
let chunks: Buffer[] = [];
while (buff.byteLength > 0) {
chunks.push(buff.slice(0, chunkSize));
buff = buff.slice(chunkSize);
}
return chunks;
}
const CELL_MAX_SIZE_BYTES = Math.floor((1023 - 8) / 8);
export function makeSnakeCell(data: Buffer) {
let chunks = bufferToChunks(data, CELL_MAX_SIZE_BYTES);
const b = chunks.reduceRight((curCell, chunk, index) => {
if (index === 0) {
curCell.storeInt(SNAKE_PREFIX, 8);
}
curCell.storeBuffer(chunk);
if (index > 0) {
const cell = curCell.endCell();
return beginCell().storeRef(cell);
} else {
return curCell;
}
}, beginCell());
return b.endCell();
}
const ONCHAIN_CONTENT_PREFIX = 0x00; const ONCHAIN_CONTENT_PREFIX = 0x00;
const SNAKE_PREFIX = 0x00; const SNAKE_PREFIX = 0x00;
export type JettonMetaDataKeys = "name" | "description" | "image" | "symbol";
const jettonOnChainMetadataSpec: {
[key in JettonMetaDataKeys]: "utf8" | "ascii" | undefined;
} = {
name: "utf8",
description: "utf8",
image: "ascii",
symbol: "utf8",
};
const sha256 = (str: string) => { const sha256 = (str: string) => {
const sha = new Sha256(); const sha = new Sha256();
sha.update(str); sha.update(str);
return Buffer.from(sha.digestSync()); return Buffer.from(sha.digestSync());
}; };
export function buildTokenMetadataCell(data: { [s: string]: string | undefined }): Cell { const toKey = (key: string) => {
const KEYLEN = 256; return BigInt(`0x${sha256(key).toString("hex")}`);
let rootCell = beginCell();
let dict = new Cell();
Object.entries(data).forEach(([k, v]: [string, string | undefined]) => {
if (!jettonOnChainMetadataSpec[k as JettonMetaDataKeys])
throw new Error(`Unsupported onchain key: ${k}`);
if (v === undefined || v === "") return;
let bufferToStore = Buffer.from(v, jettonOnChainMetadataSpec[k as JettonMetaDataKeys]);
const CELL_MAX_SIZE_BYTES = Math.floor((1023 - 8) / 8);
rootCell.storeUint(SNAKE_PREFIX, 16);
let currentCell = rootCell;
//TODO need fix dictionary writing
while (bufferToStore.length > 0) {
currentCell.storeBuffer(bufferToStore.subarray(0, CELL_MAX_SIZE_BYTES)) // how to read from Buffer???
//currentCell.bits.writeBuffer(bufferToStore.slice(0, CELL_MAX_SIZE_BYTES));
//bufferToStore = bufferToStore.slice(CELL_MAX_SIZE_BYTES);
if (bufferToStore.length > 0) {
const newCell = new Builder();
newCell.storeRef(currentCell);
currentCell = newCell;
}
}
let dict = currentCell.endCell();
});
return beginCell().storeInt(ONCHAIN_CONTENT_PREFIX, 8).storeDict(dict).endCell();
}
export function parseTokenMetadataCell(contentCell: Cell): {
[s in JettonMetaDataKeys]?: string;
} {
// Note that this relies on what is (perhaps) an internal implementation detail:
// "ton" library dict parser converts: key (provided as buffer) => BN(base10)
// and upon parsing, it reads it back to a BN(base10)
// tl;dr if we want to read the map back to a JSON with string keys, we have to convert BN(10) back to hex
const toKey = (str: string) => new BN(str, "hex").toString(10);
const KEYLEN = 256;
const contentSlice = contentCell.beginParse();
if (contentSlice.readUint(8).toNumber() !== ONCHAIN_CONTENT_PREFIX)
throw new Error("Expected onchain content marker");
const dict = contentSlice.readDict(KEYLEN, (s) => {
const buffer = Buffer.from("");
const sliceToVal = (s: Slice, v: Buffer, isFirst: boolean) => {
s.toCell().beginParse();
if (isFirst && s.readUint(8).toNumber() !== SNAKE_PREFIX)
throw new Error("Only snake format is supported");
v = Buffer.concat([v, s.readRemainingBytes()]);
if (s.remainingRefs === 1) {
v = sliceToVal(s.readRef(), v, false);
}
return v;
}; };
return sliceToVal(s.readRef(), buffer, true); export function buildOnchainMetadata(data: {
}); name: string;
description: string;
const res: { [s in JettonMetaDataKeys]?: string } = {}; image: string;
}): Cell {
Object.keys(jettonOnChainMetadataSpec).forEach((k) => { let dict = Dictionary.empty(
const val = dict Dictionary.Keys.BigUint(256),
.get(toKey(sha256(k).toString("hex"))) Dictionary.Values.Cell()
?.toString(jettonOnChainMetadataSpec[k as JettonMetaDataKeys]); );
if (val) res[k as JettonMetaDataKeys] = val; Object.entries(data).forEach(([key, value]) => {
dict.set(toKey(key), makeSnakeCell(Buffer.from(value, "utf8")));
}); });
return res; return beginCell()
.storeInt(ONCHAIN_CONTENT_PREFIX, 8)
.storeDict(dict)
.endCell();
} }
Loading…
Cancel
Save