Browse Source

Initializing the nft content in the same content; working with records

master
Lev 2 years ago
parent
commit
443321588e
  1. 8
      bin/api.js
  2. 37
      contracts/main.ts
  3. 33
      contracts/nft-collection.fc
  4. 9
      contracts/nft-item.fc
  5. 2
      test/item.spec.ts

8
bin/api.js

@ -1,11 +1,9 @@
const main = require('../contracts/main'); const main = require('../contracts/main');
const {Address, Cell, Slice} = require("ton"); const {Address, Cell, Slice} = require("ton");
const {Base64} = require('@tonconnect/protocol'); const {Base64} = require('@tonconnect/protocol');
const BN = require("bn.js");
const express = require('express') const express = require('express')
const {get_tonclient, AdnlAddress} = require("../contracts/utils"); const {get_tonclient, AdnlAddress} = require("../contracts/utils");
const {sha256} = require("ton-crypto");
const {getRecords} = require("../contracts/main"); const {getRecords} = require("../contracts/main");
const app = express() const app = express()
const port = 5171 const port = 5171
@ -17,7 +15,7 @@ app.use(function(err, req, res, next) {
console.log("error"); console.log("error");
res.send("error"); res.send("error");
}); });
app.get('/', (req, res) => { app.get('/', async (req, res) => {
res.send('Agorata microservice for dealing with TON') res.send('Agorata microservice for dealing with TON')
}) })
@ -58,13 +56,13 @@ app.get('/get-records/:address', async (req, res) => {
app.get('/set-record/site/:site', async (req, res) => { app.get('/set-record/site/:site', async (req, res) => {
let site = new AdnlAddress(req.params.site); let site = new AdnlAddress(req.params.site);
let msg = main.changeRecordMsg("site", await main.AdnlRecord(site)); let msg = await main.changeRecordMsg("site", await main.AdnlRecord(site));
res.send(JSON.stringify(Base64.encode(msg.toBoc()))); res.send(JSON.stringify(Base64.encode(msg.toBoc())));
}) })
app.get('/set-record/wallet/:wallet', async (req, res) => { app.get('/set-record/wallet/:wallet', async (req, res) => {
let wallet = Address.parse(req.params.wallet); let wallet = Address.parse(req.params.wallet);
let msg = main.changeRecordMsg("wallet", await main.WalletRecord(wallet)); let msg = await main.changeRecordMsg("wallet", await main.WalletRecord(wallet));
res.send(JSON.stringify(Base64.encode(msg.toBoc()))); res.send(JSON.stringify(Base64.encode(msg.toBoc())));
}) })

37
contracts/main.ts

@ -11,9 +11,10 @@ import {
} from "./utils"; } from "./utils";
import {randomBytes} from "crypto"; import {randomBytes} from "crypto";
import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey, sha256} from "ton-crypto"; import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey, sha256} from "ton-crypto";
import { hex as item_code } from "../build/nft-item.compiled.json"; import {hex as item_code} from "../build/nft-item.compiled.json";
import {randomAddress} from "../test/helpers"; import {randomAddress} from "../test/helpers";
import {hashCell} from "ton/dist/boc/boc"; import {hashCell} from "ton/dist/boc/boc";
import {Base64} from "@tonconnect/protocol";
// encode contract storage according to save_data() contract method // encode contract storage according to save_data() contract method
@ -64,6 +65,11 @@ export function collectionData(params: {
.storeRef(beginCell().storeUint(params.price_multiplier, 8).storeUint(params.price_steepness, 4).endCell()) .storeRef(beginCell().storeUint(params.price_multiplier, 8).storeUint(params.price_steepness, 4).endCell())
.storeUint(params.ownerKey, 256) .storeUint(params.ownerKey, 256)
.storeAddress(params.ownerAddress) .storeAddress(params.ownerAddress)
.storeRef(
beginCell()
.storeRef(beginCell().storeBuffer(Buffer.from(`https://api.agorata.io/data/${params.zone}/`)).endCell())
.storeRef(beginCell().storeBuffer(Buffer.from(`.json`)).endCell())
.endCell())
.endCell(); .endCell();
} }
@ -75,9 +81,10 @@ export function itemDataUninit(params: { domain: String, collectionAddress: Addr
.storeAddress(params.collectionAddress).endCell(); .storeAddress(params.collectionAddress).endCell();
} }
export function itemData(params: { export async function itemData(params: {
domain: String, collectionAddress: Address, domain: String, collectionAddress: Address,
ownerAddress: Address }): Cell { ownerAddress: Address
}): Promise<Cell> {
let domain = params.domain.split('.')[0]; let domain = params.domain.split('.')[0];
let zone = params.domain.split('.').slice(1).join('.'); let zone = params.domain.split('.').slice(1).join('.');
let index = new BN(hashCell(makeSnakeCell(Buffer.from(domain)))); let index = new BN(hashCell(makeSnakeCell(Buffer.from(domain))));
@ -85,7 +92,7 @@ export function itemData(params: {
.storeUint(index, 256) .storeUint(index, 256)
.storeAddress(params.collectionAddress) .storeAddress(params.collectionAddress)
.storeAddress(params.ownerAddress) .storeAddress(params.ownerAddress)
.storeRef(itemContent(domain, zone)) .storeRef(await itemContent(domain, zone))
.storeRef(makeSnakeCell(Buffer.from(domain))) .storeRef(makeSnakeCell(Buffer.from(domain)))
.storeUint(0, 64).endCell(); .storeUint(0, 64).endCell();
} }
@ -97,9 +104,12 @@ export async function getRecords(tonclient: TonClient, address: Address) {
[["tvm.Slice", "te6cckEBAQEAAwAAAgDTZ9xB"], [["tvm.Slice", "te6cckEBAQEAAwAAAgDTZ9xB"],
["num", new BN(await sha256('wallet')).toString()]])).stack[1]; ["num", new BN(await sha256('wallet')).toString()]])).stack[1];
if (ans_wallet[1].bytes !== undefined) { if (ans_wallet[1].bytes !== undefined) {
wallet = Cell.fromBoc(Buffer.from(ans_wallet[1].bytes, 'base64'))[0].beginParse().readAddress(); let reader = Cell.fromBoc(Buffer.from(ans_wallet[1].bytes, 'base64'))[0].beginParse()
reader.skip(16);
wallet = reader.readAddress()?.toFriendly();
}
} catch (e) {
} }
} catch (e) {}
let site = null; let site = null;
try { try {
let ans_site = (await tonclient.callGetMethod(address, 'dnsresolve', let ans_site = (await tonclient.callGetMethod(address, 'dnsresolve',
@ -108,16 +118,18 @@ export async function getRecords(tonclient: TonClient, address: Address) {
if (ans_site[1].bytes !== undefined) { if (ans_site[1].bytes !== undefined) {
let site_data = ans_site[1].bytes; // object.data.b64; let site_data = ans_site[1].bytes; // object.data.b64;
// print site_data converted from base64 // print site_data converted from base64
let c = Cell.fromBoc(Buffer.from(site_data, 'base64'))[0]; let c = Cell.fromBoc(Buffer.from(site_data, 'base64'))[0].beginParse();
site = new AdnlAddress(c.bits.buffer.subarray(2, 2 + 32)).toHex(); c.skip(16);
site = new AdnlAddress(c.readRemainingBytes().subarray(2, 2 + 32)).toHex();
}
} catch (e) {
} }
} catch (e) {}
let uri = null; let uri = null;
try { try {
let ans_uri = (await tonclient.callGetMethod(address, 'dnsresolve', let ans_uri = (await tonclient.callGetMethod(address, 'dnsresolve',
[["tvm.Slice", "te6cckEBAQEAAwAAAgDTZ9xB"], [["tvm.Slice", "te6cckEBAQEAAwAAAgDTZ9xB"],
["num", new BN(await sha256('uri')).toString()]])).stack[1]; ["num", new BN(await sha256('uri')).toString()]])).stack[1];
uri = Buffer.from(ans_uri[1].bytes, 'base64').toString(); uri = Cell.fromBoc(Buffer.from(ans_uri[1].bytes, "base64"))[0].beginParse().readRemainingBytes().toString();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
console.log((await tonclient.callGetMethod(address, 'get_nft_data')).stack[4]); console.log((await tonclient.callGetMethod(address, 'get_nft_data')).stack[4]);
@ -221,10 +233,11 @@ export function createItem(params: { domain: String, signature?: String }): Cell
.endCell(); .endCell();
} }
export function initializeItemMsg(params: { domain: String, ownerAddr: Address }): Cell { export function initializeItemMsg(params: { domain: String, ownerAddr: Address, zone: String }): Cell {
return beginCell() return beginCell()
.storeAddress(params.ownerAddr) .storeAddress(params.ownerAddr)
.storeRef(makeSnakeCell(Buffer.from(params.domain))) .storeRef(beginCell().storeBuffer(Buffer.from(params.domain)).endCell())
.storeRef(beginCell().storeBuffer(Buffer.from(`https://api.agorata.io/data/${params.zone}/${params.domain}.json`)).endCell())
.endCell(); .endCell();
} }

33
contracts/nft-collection.fc

@ -10,25 +10,28 @@
;; cell pricing ;; cell pricing
;; uint256(key) owner_key ;; uint256(key) owner_key
;; address owner_address ;; address owner_address
;; cell uri_scheme: prefix slice as a cell, then postfix ('.json') slice as a cell
(cell, cell, cell, int, slice) load_data() inline { (cell, cell, cell, int, slice, cell) load_data() inline {
var ds = get_data().begin_parse(); var ds = get_data().begin_parse();
return ( return (
ds~load_ref(), ;; content ds~load_ref(), ;; content
ds~load_ref(), ;; nft_item_code ds~load_ref(), ;; nft_item_code
ds~load_ref(), ;; pricing ds~load_ref(), ;; pricing
ds~load_uint(256), ;; owner key ds~load_uint(256), ;; owner key
ds~load_msg_addr() ;; owner address ds~load_msg_addr(), ;; owner address
ds~load_ref() ;; uri_scheme
); );
} }
() save_data(cell content, cell nft_item_code, cell pricing, int owner_key, slice owner_addr) impure inline { () save_data(cell content, cell nft_item_code, cell pricing, int owner_key, slice owner_addr, cell uri_scheme) impure inline {
set_data(begin_cell() set_data(begin_cell()
.store_ref(content) .store_ref(content)
.store_ref(nft_item_code) .store_ref(nft_item_code)
.store_ref(pricing) .store_ref(pricing)
.store_uint(owner_key, 256) .store_uint(owner_key, 256)
.store_slice(owner_addr) .store_slice(owner_addr)
.store_ref(uri_scheme)
.end_cell()); .end_cell());
} }
@ -50,6 +53,15 @@ cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell(); return begin_cell().store_uint(0, 2).store_dict(nft_item_code).store_dict(data).store_uint(0, 1).end_cell();
} }
cell get_uri(slice domain, cell uri_scheme) {
;; parse the prefix and the postfix out of uri_scheme
slice cs = uri_scheme.begin_parse();
slice prefix = cs~load_ref().begin_parse();
slice postfix = cs~load_ref().begin_parse();
;; create the slice with the uri: prefix + domain + postfix
return begin_cell().store_slice(prefix).store_slice(domain).store_slice(postfix).end_cell();
}
slice calculate_nft_item_address(int wc, cell state_init) { slice calculate_nft_item_address(int wc, cell state_init) {
return begin_cell() return begin_cell()
.store_uint(4, 3) .store_uint(4, 3)
@ -75,7 +87,7 @@ slice calculate_nft_item_address(int wc, cell state_init) {
send_raw_message(msg.end_cell(), send_mode); send_raw_message(msg.end_cell(), send_mode);
} }
() deploy_nft_item(int item_index, cell nft_item_code, cell nft_content) impure { () deploy_nft_item(int item_index, cell nft_item_code, cell nft_payload) impure {
cell state_init = calculate_nft_item_state_init(item_index, nft_item_code); cell state_init = calculate_nft_item_state_init(item_index, nft_item_code);
slice nft_address = calculate_nft_item_address(workchain(), state_init); slice nft_address = calculate_nft_item_address(workchain(), state_init);
var msg = begin_cell() var msg = begin_cell()
@ -84,7 +96,7 @@ slice calculate_nft_item_address(int wc, cell state_init) {
.store_coins(0) .store_coins(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) .store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init) .store_ref(state_init)
.store_ref(nft_content); .store_ref(nft_payload);
send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message, fee deducted from amount send_raw_message(msg.end_cell(), 64); ;; carry all the remaining value of the inbound message, fee deducted from amount
} }
@ -107,7 +119,7 @@ int verify_signature(slice signature, slice sender_address, slice domain, int ow
int op = in_msg_body~load_uint(32); int op = in_msg_body~load_uint(32);
var (content, nft_item_code, pricing, key, addr) = load_data(); var (content, nft_item_code, pricing, key, addr, uri_scheme) = load_data();
if (op == 0) { ;; deploy new nft if (op == 0) { ;; deploy new nft
int now_time = now(); int now_time = now();
@ -122,14 +134,13 @@ int verify_signature(slice signature, slice sender_address, slice domain, int ow
slice sender_address = cs~load_msg_addr(); slice sender_address = cs~load_msg_addr();
if (key != 0) { if (key != 0) {
slice signature = decode_asciicode(signature_encoded); slice signature = decode_asciicode(signature_encoded);
int bbb = signature.preload_uint(8);
;; throw(300 + slice_bits(signature));
throw_unless(205, verify_signature(signature, sender_address, domain, key)); throw_unless(205, verify_signature(signature, sender_address, domain, key));
} }
cell nft_content = begin_cell() cell nft_content = begin_cell()
.store_slice(sender_address) .store_slice(sender_address)
.store_ref(begin_cell().store_slice(domain).end_cell()) .store_ref(begin_cell().store_slice(domain).end_cell())
.store_ref(get_uri(domain, uri_scheme))
.end_cell(); .end_cell();
deploy_nft_item(item_index, nft_item_code, nft_content); deploy_nft_item(item_index, nft_item_code, nft_content);
return (); return ();
@ -151,12 +162,12 @@ int verify_signature(slice signature, slice sender_address, slice domain, int ow
;; Get methods ;; Get methods
(int, cell, slice) get_collection_data() method_id { (int, cell, slice) get_collection_data() method_id {
var (content, nft_item_code, pricing, key, addr) = load_data(); var (content, nft_item_code, pricing, key, addr, uri_scheme) = load_data();
return (-1, content, zero_address()); return (-1, content, zero_address());
} }
slice get_nft_address_by_index(int index) method_id { slice get_nft_address_by_index(int index) method_id {
var (content, nft_item_code, pricing, key, addr) = load_data(); var (content, nft_item_code, pricing, key, addr, uri_scheme) = load_data();
cell state_init = calculate_nft_item_state_init(index, nft_item_code); cell state_init = calculate_nft_item_state_init(index, nft_item_code);
return calculate_nft_item_address(workchain(), state_init); return calculate_nft_item_address(workchain(), state_init);
} }
@ -166,7 +177,7 @@ cell get_nft_content(int index, cell individual_nft_content) method_id {
} }
int get_price(slice domain) method_id { int get_price(slice domain) method_id {
var (content, nft_item_code, pricing, key, addr) = load_data(); var (content, nft_item_code, pricing, key, addr, uri_scheme) = load_data();
return calcprice(domain, pricing); return calcprice(domain, pricing);
} }

9
contracts/nft-item.fc

@ -5,6 +5,9 @@
int min_tons_for_storage() asm "1000000000 PUSHINT"; ;; 1 TON int min_tons_for_storage() asm "1000000000 PUSHINT"; ;; 1 TON
;; sha256('uri')
const uri_key = 51065135818459385347574250312853146822620586594996463797054414300406918686668;
;; ;;
;; === Storage === ;; === Storage ===
;; ;;
@ -104,8 +107,10 @@ int min_tons_for_storage() asm "1000000000 PUSHINT"; ;; 1 TON
throw_unless(405, equal_slices(collection_address, sender_address)); throw_unless(405, equal_slices(collection_address, sender_address));
slice from_address = in_msg_body~load_msg_addr(); slice from_address = in_msg_body~load_msg_addr();
cell domain = in_msg_body~load_ref(); cell domain = in_msg_body~load_ref();
cell uri = in_msg_body~load_ref();
cell content = begin_cell().store_uint(0, 8).store_dict(new_dict()).end_cell(); cell content_dict = new_dict();
content_dict~udict_set_ref(256, uri_key, uri);
cell content = begin_cell().store_uint(0, 8).store_dict(content_dict).end_cell();
store_data(index, collection_address, from_address, content, domain, now()); store_data(index, collection_address, from_address, content, domain, now());
return (); return ();
} }

2
test/item.spec.ts

@ -36,7 +36,7 @@ describe("Creating items tests", () => {
let ownerAddr = randomAddress("dude"); let ownerAddr = randomAddress("dude");
const initializeMsg = internalMessage({ const initializeMsg = internalMessage({
from: randomAddress("collection"), from: randomAddress("collection"),
body: main.initializeItemMsg({domain: "levcccc", ownerAddr}), body: main.initializeItemMsg({domain: "levcccc", ownerAddr, zone: "example.ton"}),
value: new BN(0), value: new BN(0),
}); });
const res = await contract.sendInternalMessage(initializeMsg); const res = await contract.sendInternalMessage(initializeMsg);

Loading…
Cancel
Save