tonweb
Version:
TonWeb - JavaScript API for TON blockchain
201 lines (175 loc) • 8.67 kB
JavaScript
const {Contract} = require("../../index.js");
const {Cell} = require("../../../boc");
const {Address, bytesToBase64, BN} = require("../../../utils");
const {parseAddress} = require('./NftUtils.js');
const {createOffchainUriCell, serializeUri, parseOffchainUriCell, getRoyaltyParams} = require("./NftUtils");
class NftCollection extends Contract {
/**
* @param provider
* @param options {{ownerAddress: Address, collectionContentUri: string, nftItemContentBaseUri: string, nftItemCodeHex: string, royalty: number, royaltyAddress: Address, address?: Address | string, code?: Cell}}
*/
constructor(provider, options) {
options.wc = 0;
// https://github.com/ton-blockchain/token-contract/blob/1ad314a98d20b41241d5329e1786fc894ad811de/nft/nft-collection-editable.fc
options.code = options.code || Cell.oneFromBoc('B5EE9C724102140100021F000114FF00F4A413F4BCF2C80B0102016202030202CD04050201200E0F04E7D10638048ADF000E8698180B8D848ADF07D201800E98FE99FF6A2687D20699FEA6A6A184108349E9CA829405D47141BAF8280E8410854658056B84008646582A802E78B127D010A65B509E58FE59F80E78B64C0207D80701B28B9E382F970C892E000F18112E001718112E001F181181981E0024060708090201200A0B00603502D33F5313BBF2E1925313BA01FA00D43028103459F0068E1201A44343C85005CF1613CB3FCCCCCCC9ED54925F05E200A6357003D4308E378040F4966FA5208E2906A4208100FABE93F2C18FDE81019321A05325BBF2F402FA00D43022544B30F00623BA9302A402DE04926C21E2B3E6303250444313C85005CF1613CB3FCCCCCCC9ED54002C323401FA40304144C85005CF1613CB3FCCCCCCC9ED54003C8E15D4D43010344130C85005CF1613CB3FCCCCCCC9ED54E05F04840FF2F00201200C0D003D45AF0047021F005778018C8CB0558CF165004FA0213CB6B12CCCCC971FB008002D007232CFFE0A33C5B25C083232C044FD003D0032C03260001B3E401D3232C084B281F2FFF2742002012010110025BC82DF6A2687D20699FEA6A6A182DE86A182C40043B8B5D31ED44D0FA40D33FD4D4D43010245F04D0D431D430D071C8CB0701CF16CCC980201201213002FB5DAFDA89A1F481A67FA9A9A860D883A1A61FA61FF480610002DB4F47DA89A1F481A67FA9A9A86028BE09E008E003E00B01A500C6E');
if (options.royalty > 1) throw new Error('royalty > 1');
options.royaltyBase = 1000;
options.royaltyFactor = Math.floor(options.royalty * options.royaltyBase);
super(provider, options);
this.methods.getCollectionData = this.getCollectionData.bind(this);
this.methods.getNftItemAddressByIndex = this.getNftItemAddressByIndex.bind(this);
this.methods.getNftItemContent = this.getNftItemContent.bind(this);
this.methods.getRoyaltyParams = this.getRoyaltyParams.bind(this);
}
/**
* @private
* @param params {{collectionContentUri: string, nftItemContentBaseUri: string}}
* @return {Cell}
*/
createContentCell(params) {
const collectionContentCell = createOffchainUriCell(params.collectionContentUri);
const commonContentCell = new Cell();
commonContentCell.bits.writeBytes(serializeUri(params.nftItemContentBaseUri));
const contentCell = new Cell();
contentCell.refs[0] = collectionContentCell;
contentCell.refs[1] = commonContentCell;
return contentCell;
}
/**
* @private
* @param params {{royaltyFactor: number, royaltyBase: number, royaltyAddress: Address}}
* @return {Cell}
*/
createRoyaltyCell(params) {
const royaltyCell = new Cell();
royaltyCell.bits.writeUint(params.royaltyFactor, 16);
royaltyCell.bits.writeUint(params.royaltyBase, 16);
royaltyCell.bits.writeAddress(params.royaltyAddress);
return royaltyCell;
}
/**
* @override
* @private
* @return {Cell} cell contains nft collection data
*/
createDataCell() {
const cell = new Cell();
cell.bits.writeAddress(this.options.ownerAddress);
cell.bits.writeUint(0, 64); // next_item_index
cell.refs[0] = this.createContentCell(this.options);
cell.refs[1] = Cell.oneFromBoc(this.options.nftItemCodeHex);
cell.refs[2] = this.createRoyaltyCell(this.options);
return cell;
}
/**
* params {{itemIndex: BN|number, amount: BN, itemOwnerAddress: Address, itemContentUri: string, queryId?: number}}
* @return {Cell}
*/
createMintBody(params) {
const body = new Cell();
body.bits.writeUint(1, 32); // OP deploy new nft
body.bits.writeUint(params.queryId || 0, 64); // query_id
body.bits.writeUint(params.itemIndex, 64);
body.bits.writeCoins(params.amount);
const nftItemContent = new Cell();
nftItemContent.bits.writeAddress(params.itemOwnerAddress);
const uriContent = new Cell();
uriContent.bits.writeBytes(serializeUri(params.itemContentUri));
nftItemContent.refs[0] = uriContent;
body.refs[0] = nftItemContent;
return body;
}
/**
* params {{queryId?: number}}
* @return {Cell}
*/
createGetRoyaltyParamsBody(params) {
const body = new Cell();
body.bits.writeUint(0x693d3950, 32); // OP
body.bits.writeUint(params.queryId || 0, 64); // query_id
return body;
}
/**
* params {{queryId?: number, newOwnerAddress: Address}}
* @return {Cell}
*/
createChangeOwnerBody(params) {
if (params.newOwnerAddress === undefined) throw new Error('Specify newOwnerAddress');
const body = new Cell();
body.bits.writeUint(3, 32); // OP
body.bits.writeUint(params.queryId || 0, 64); // query_id
body.bits.writeAddress(params.newOwnerAddress);
return body;
}
/**
* @param params {{collectionContentUri: string, nftItemContentBaseUri: string, royalty: number, royaltyAddress: Address, queryId?: number}}
* @return {Cell}
*/
createEditContentBody(params) {
if (params.royalty > 1) throw new Error('royalty > 1');
params.royaltyBase = 1000;
params.royaltyFactor = Math.floor(params.royalty * params.royaltyBase);
const body = new Cell();
body.bits.writeUint(4, 32); // OP
body.bits.writeUint(params.queryId || 0, 64); // query_id
body.refs[0] = this.createContentCell(params);
body.refs[1] = this.createRoyaltyCell(params);
return body;
}
/**
* @return {Promise<{nextItemIndex: number, itemsCount: BN, ownerAddress: Address, collectionContentCell: Cell, collectionContentUri: string|null}>}
*/
async getCollectionData() {
const myAddress = await this.getAddress();
const result = await this.provider.call2(myAddress.toString(), 'get_collection_data');
const itemsCount = result[0];
let nextItemIndex = NaN;
try {
nextItemIndex = itemsCount.toNumber();
} catch (e) {
}
const collectionContentCell = result[1];
let collectionContentUri = null;
try {
collectionContentUri = parseOffchainUriCell(collectionContentCell);
} catch (e) {
}
const ownerAddress = parseAddress(result[2]);
return {nextItemIndex, itemsCount, ownerAddress, collectionContentCell, collectionContentUri};
}
/**
* @param nftItem {NftItem}
* @return {Promise<{isInitialized: boolean, index: number, itemIndex: BN, collectionAddress: Address, ownerAddress: Address|null, contentCell: Cell, contentUri: string|null}>}
*/
async getNftItemContent(nftItem) {
const myAddress = await this.getAddress();
const nftData = await nftItem.getData();
if (nftData.isInitialized) {
const result = await this.provider.call2(myAddress.toString(), 'get_nft_content', [['num', nftData.itemIndex.toString(10)], ['tvm.Cell', bytesToBase64(await nftData.contentCell.toBoc(false))]]);
nftData.contentUri = null;
try {
nftData.contentUri = parseOffchainUriCell(result);
} catch (e) {
}
}
return nftData;
}
/**
* @param index {BN|number}
* @return {Promise<Address>}
*/
async getNftItemAddressByIndex(index) {
index = new BN(index);
const myAddress = await this.getAddress();
const result = await this.provider.call2(myAddress.toString(), 'get_nft_address_by_index', [['num', index.toString(10)]]);
return parseAddress(result);
}
/**
* @return {Promise<{royalty: number, royaltyFactor: number, royaltyBase: number, royaltyAddress: Address}>}
*/
async getRoyaltyParams() {
const myAddress = await this.getAddress();
return getRoyaltyParams(this.provider, myAddress.toString());
}
}
module.exports = {NftCollection};