@openproduct/web-sdk
Version:
OpenMask Web - TypeScript Browser SDK for TON blockchain
180 lines (157 loc) • 4.72 kB
text/typescript
import BN from "bn.js";
import { Buffer } from "buffer";
import { BitString } from "../../../boc/bitString";
import { Cell } from "../../../boc/cell";
import TonHttpProvider from "../../../providers/httpProvider";
import Address from "../../../utils/address";
export const SNAKE_DATA_PREFIX = 0x00;
export const CHUNK_DATA_PREFIX = 0x01;
export const ONCHAIN_CONTENT_PREFIX = 0x00;
export const OFFCHAIN_CONTENT_PREFIX = 0x01;
/**
* @param uri {string}
* @returns {Uint8Array}
*/
export const serializeUri = (uri: string) => {
return new TextEncoder().encode(encodeURI(uri));
};
/**
* @param bytes {Uint8Array}
* @return {string}
*/
export const parseUri = (bytes: Uint8Array) => {
return new TextDecoder().decode(bytes);
};
/**
* @param uri {string}
* @return {Cell}
*/
export const createOffchainUriCell = (uri: string) => {
const cell = new Cell();
cell.bits.writeUint(OFFCHAIN_CONTENT_PREFIX, 8);
cell.bits.writeBytes(serializeUri(uri));
return cell;
};
/**
* @param cell {Cell}
* @returns {string}
*/
export const parseOffchainUriCell = (cell: Cell) => {
if (cell.bits.array[0] !== OFFCHAIN_CONTENT_PREFIX) {
throw new Error("no OFFCHAIN_CONTENT_PREFIX");
}
let length = 0;
let c = cell;
while (c) {
length += c.bits.array.length;
c = c.refs[0];
}
const bytes = new Uint8Array(length);
length = 0;
c = cell;
while (c) {
bytes.set(c.bits.array, length);
length += c.bits.array.length;
c = c.refs[0];
}
return parseUri(bytes.slice(1)); // slice OFFCHAIN_CONTENT_PREFIX
};
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;
}
export function makeSnakeCell(data: Buffer) {
let chunks = bufferToChunks(data, 127);
let rootCell = new Cell();
let curCell = rootCell;
for (let i = 0; i < chunks.length; i++) {
let chunk = chunks[i];
curCell.bits.writeBuffer(chunk);
if (chunks[i + 1]) {
let nextCell = new Cell();
curCell.refs.push(nextCell);
curCell = nextCell;
}
}
return rootCell;
}
export const createOffChainContent = (content: string) => {
let data = Buffer.from(content);
let offChainPrefix = Buffer.from([OFFCHAIN_CONTENT_PREFIX]);
data = Buffer.concat([offChainPrefix, data]);
return makeSnakeCell(data);
};
/**
* @param bs {BitString}
* @param cursor {number}
* @param bits {number}
* @return {BigInt}
*/
const readIntFromBitString = (bs: BitString, cursor: number, bits: number) => {
let n = BigInt(0);
for (let i = 0; i < bits; i++) {
n *= BigInt(2);
n += BigInt(bs.get(cursor + i));
}
return n;
};
/**
* @param cell {Cell}
* @return {Address|null}
*/
export const parseAddress = (cell: Cell) => {
let n = readIntFromBitString(cell.bits, 3, 8);
if (n > BigInt(127)) {
n = n - BigInt(256);
}
const hashPart = readIntFromBitString(cell.bits, 3 + 8, 256);
if (n.toString(10) + ":" + hashPart.toString(16) === "0:0") return null;
const s = n.toString(10) + ":" + hashPart.toString(16).padStart(64, "0");
return new Address(s);
};
/**
* @param provider {TonHttpProvider}
* @param address {string}
* @return {Promise<{royalty: number, royaltyFactor: number, royaltyBase: number, royaltyAddress: Address}>}
*/
export const getRoyaltyParams = async (
provider: TonHttpProvider,
address: string
) => {
const result = await provider.call2(address, "royalty_params");
const royaltyFactor = result[0].toNumber();
const royaltyBase = result[1].toNumber();
const royalty = royaltyFactor / royaltyBase;
const royaltyAddress = parseAddress(result[2]);
return { royalty, royaltyBase, royaltyFactor, royaltyAddress };
};
export const nftTransferBody = (params: {
queryId?: number;
newOwnerAddress: Address;
forwardAmount?: BN;
forwardPayload?: Uint8Array;
responseAddress: Address;
}) => {
const cell = new Cell();
cell.bits.writeUint(0x5fcc3d14, 32); // transfer op
cell.bits.writeUint(params.queryId || 0, 64);
cell.bits.writeAddress(params.newOwnerAddress);
cell.bits.writeAddress(params.responseAddress);
cell.bits.writeBit(false); // null custom_payload
cell.bits.writeCoins(params.forwardAmount || new BN(0));
cell.bits.writeBit(false); // forward_payload in this slice, not separate cell
if (params.forwardPayload) {
cell.bits.writeBytes(params.forwardPayload);
}
return cell;
};
export const nftGetStaticDataBody = (params: { queryId?: number }) => {
const body = new Cell();
body.bits.writeUint(0x2fcb26a2, 32); // OP
body.bits.writeUint(params.queryId || 0, 64); // query_id
return body;
};