Browse Source

Pricing

master
Lev 2 years ago
parent
commit
b35dbb9283
  1. 31
      contracts/imports/dns-utils.fc
  2. 131
      contracts/main.ts
  3. 10
      contracts/nft-collection.fc
  4. 14
      test/creation.spec.ts

31
contracts/imports/dns-utils.fc

@ -172,6 +172,37 @@ int check_domain_string(slice domain) {
return (10, 1);
}
int price_function(int length, int multiplierx10, int steepness) {
;; length is the length of the domain, the price depends on it. The rest is the price config
;; multiplierx10 is the price multiplier, the default price is multiplied by multiplierx10 / 10
;; steepness (from 0 to 10) is the steepness of the price function,
;; 10 means the distribution is as in the function from `get_min_price_config` (the second number), 0 means the price is always the same, 50
int price = 50;
if (length == 3) {
price = 200;
}
if (length == 4) {
price = 100;
}
if (length == 5) {
price = 50;
}
if (length == 6) {
price = 40;
}
if (length == 7) {
price = 30;
}
if (length == 8) {
price = 20;
}
if (length >= 9) {
price = 10;
}
price = (steepness * price + (10 - steepness) * 50) / 10;
return price * one_ton * multiplierx10 / 10;
}
int get_min_price(int domain_bits_length, int now_time) {
(int start_min_price, int end_min_price) = get_min_price_config(domain_bits_length / 8);
start_min_price *= one_ton;

131
contracts/main.ts

@ -1,16 +1,16 @@
import BN from "bn.js";
import { Cell, beginCell, Address } from "ton";
import { C7Config, SmartContract } from "ton-contract-executor";
import {Cell, beginCell, Address} from "ton";
import {C7Config, SmartContract} from "ton-contract-executor";
import {encodeOffChainContent, makeSnakeCell} from "./utils";
import { randomBytes } from "crypto";
import { keyPairFromSeed, KeyPair, sign } from "ton-crypto";
import { ExpansionPanelActions } from "@material-ui/core";
import {randomBytes} from "crypto";
import {keyPairFromSeed, KeyPair, sign} from "ton-crypto";
import {ExpansionPanelActions} from "@material-ui/core";
// encode contract storage according to save_data() contract method
export function genKeyPair(): KeyPair {
let seed = randomBytes(32);
return keyPairFromSeed(seed);
let seed = randomBytes(32);
return keyPairFromSeed(seed);
}
// nft_item_code:^Cell
@ -22,89 +22,100 @@ export function genKeyPair(): KeyPair {
// cell auction - auction info
// int64 last_fill_up_time
export function data(params: { ownerAddress: Address; collectionAddress: Address, code: Cell, domain: String, publicKey: Buffer }): Cell {
let data_cell = beginCell()
// For code: https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-auction/build.sh
.storeRef(params.code)
.storeUint(0, 256)
.storeAddress(params.collectionAddress)
.storeAddress(params.ownerAddress)
.storeRef(encodeOffChainContent("https://agorata.io/collection.json")) // https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md
.storeRef(makeSnakeCell(Buffer.from(params.domain)))
.storeDict(null)
.storeUint(0, 64).endCell();
let data_cell = beginCell()
// For code: https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-auction/build.sh
.storeRef(params.code)
.storeUint(0, 256)
.storeAddress(params.collectionAddress)
.storeAddress(params.ownerAddress)
.storeRef(encodeOffChainContent("https://agorata.io/collection.json")) // https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md
.storeRef(makeSnakeCell(Buffer.from(params.domain)))
.storeDict(null)
.storeUint(0, 64).endCell();
return beginCell().storeRef(data_cell).storeBuffer(params.publicKey).endCell();
return beginCell().storeRef(data_cell).storeBuffer(params.publicKey).endCell();
}
export function collectionData(params: { code: Cell, ownerAddress: Address, ownerKey: number }): Cell {
export function collectionData(params: {
code: Cell, ownerAddress: Address, ownerKey: number,
price_multiplier?: number, price_steepness?: number
}): Cell {
if (params.price_multiplier == undefined) {
params.price_multiplier = 10;
}
if (params.price_steepness == undefined) {
params.price_steepness = 5;
}
return beginCell()
.storeRef(encodeOffChainContent("https://agorata.io/collection.json"))
.storeRef(params.code)
.storeRef(beginCell().endCell())
.storeRef(beginCell().storeUint(params.price_multiplier, 8).storeUint(params.price_steepness, 4).endCell())
.storeUint(params.ownerKey, 256)
.storeAddress(params.ownerAddress)
.endCell();
.endCell();
}
export function auctionWithWinner(winnerAddress: Address) {
return beginCell().storeAddress(winnerAddress).storeCoins(0).storeUint(0, 64)
return beginCell().storeAddress(winnerAddress).storeCoins(0).storeUint(0, 64)
}
export function setContractBalance(contract: SmartContract, balance: number) {
contract.setC7Config({balance: balance});
contract.setC7Config({balance: balance});
}
export function TON(): number { return 1000000000; }
export function TON(): number {
return 1000000000;
}
// message encoders for all ops (see contracts/imports/constants.fc for consts)
export function transferOwnership(params: { newOwnerAddress: Address }): Cell {
return beginCell().storeUint(0x5fcc3d14, 32).storeUint(0, 64).storeAddress(params.newOwnerAddress).storeAddress(null).storeInt(0, 1).storeCoins(1 * TON()).endCell();
return beginCell().storeUint(0x5fcc3d14, 32).storeUint(0, 64).storeAddress(params.newOwnerAddress).storeAddress(null).storeInt(0, 1).storeCoins(1 * TON()).endCell();
}
export function createItem(params: { domain: String }): Cell {
let signature = '000';
return beginCell()
.storeUint(0, 32)
.storeRef(makeSnakeCell(Buffer.from(params.domain + ';' + signature)))
.endCell();
let signature = '000';
return beginCell()
.storeUint(0, 32)
.storeRef(makeSnakeCell(Buffer.from(params.domain + ';' + signature)))
.endCell();
}
export function instantBuySignature(receiverAddress: Address, issuedCollectionAddr: Address, amount: number, domain: Cell, privateKey: Buffer): Buffer {
let messageToSign = beginCell().storeAddress(receiverAddress).storeAddress(issuedCollectionAddr).storeUint(amount, 256).storeRef(domain).endCell();
let hash = messageToSign.hash();
// console.log(hash.toString("hex"));
// console.log((new BN(hash)).toString(10))
// return sign(hash, privateKey);
let althash = new BN("FF11841721E4DAD5AE679A1A338B2EBFC5AAF7529C200B4EF9D71831B1DCB969", "hex");
return sign(althash.toBuffer(), privateKey);
let messageToSign = beginCell().storeAddress(receiverAddress).storeAddress(issuedCollectionAddr).storeUint(amount, 256).storeRef(domain).endCell();
let hash = messageToSign.hash();
// console.log(hash.toString("hex"));
// console.log((new BN(hash)).toString(10))
// return sign(hash, privateKey);
let althash = new BN("FF11841721E4DAD5AE679A1A338B2EBFC5AAF7529C200B4EF9D71831B1DCB969", "hex");
return sign(althash.toBuffer(), privateKey);
}
export function instantBuyMessage(params: { receiverAddress: Address, issuedCollectionAddr: Address, price: number, domain: String, privateKey: Buffer}): Cell {
let domainSnakeCell = makeSnakeCell(Buffer.from(params.domain));
let signature = instantBuySignature(params.receiverAddress, params.issuedCollectionAddr, params.price, domainSnakeCell, params.privateKey);
console.log(signature.toString("hex").toUpperCase());
let cell = beginCell()
.storeUint(0x16c7d435, 32) // opcode
.storeUint(0, 64) // query id
.storeBuffer(signature) // body
.storeUint(params.price, 256)
.storeRef(domainSnakeCell).endCell();
return cell;
export function instantBuyMessage(params: { receiverAddress: Address, issuedCollectionAddr: Address, price: number, domain: String, privateKey: Buffer }): Cell {
let domainSnakeCell = makeSnakeCell(Buffer.from(params.domain));
let signature = instantBuySignature(params.receiverAddress, params.issuedCollectionAddr, params.price, domainSnakeCell, params.privateKey);
console.log(signature.toString("hex").toUpperCase());
let cell = beginCell()
.storeUint(0x16c7d435, 32) // opcode
.storeUint(0, 64) // query id
.storeBuffer(signature) // body
.storeUint(params.price, 256)
.storeRef(domainSnakeCell).endCell();
return cell;
}
export function currentState(contract: SmartContract) {
let c4 = contract.dataCell.beginParse();
let reader = c4.readRef();
let c4 = contract.dataCell.beginParse();
let reader = c4.readRef();
return {
nft_item_code: reader.readRef(),
index: reader.readUint(256),
collectionAddress: reader.readAddress(),
ownerAddress: reader.readAddress(),
collectionContent: reader.readRef(),
domain: reader.readRef(),
// auction: reader.readCell(), – TODO: still havent's figured out to load auction here
// lastFillUpTime: reader.readInt(64)
}
return {
nft_item_code: reader.readRef(),
index: reader.readUint(256),
collectionAddress: reader.readAddress(),
ownerAddress: reader.readAddress(),
collectionContent: reader.readRef(),
domain: reader.readRef(),
// auction: reader.readCell(), – TODO: still havent's figured out to load auction here
// lastFillUpTime: reader.readInt(64)
}
}

10
contracts/nft-collection.fc

@ -41,7 +41,10 @@ int calcprice(slice domain, cell pricing) inline_ref {
throw_unless(201, len <= 126 * 8); ;; maxmimum 126 characters
throw_unless(202, mod(len, 8) == 0);
throw_unless(203, check_domain_string(domain));
return 100000; ;; todo
slice pr = pricing.begin_parse();
int multiplier = pr~load_uint(8);
int steepness = pr~load_uint(4);
return price_function(len / 8, multiplier, steepness);
}
cell calculate_nft_item_state_init(int item_index, cell nft_item_code) {
@ -138,6 +141,11 @@ cell get_nft_content(int index, cell individual_nft_content) method_id {
return individual_nft_content;
}
int get_price(slice domain) method_id {
var (content, nft_item_code, pricing, key, addr) = load_data();
return calcprice(domain, pricing);
}
(int, cell) dnsresolve(slice subdomain, int category) method_id {
throw_unless(70, mod(slice_bits(subdomain), 8) == 0);

14
test/creation.spec.ts

@ -33,13 +33,23 @@ describe("Creating items tests", () => {
const sendToSelfMessage = internalMessage({
from: ownerAddr,
body: main.createItem({ domain: "test" }),
value: new BN(10 * main.TON()),
value: new BN(100 * main.TON()),
});
const res = await contract.sendInternalMessage(sendToSelfMessage);
console.log(res);
expect(res.type).to.equal("success");
expect(res.exit_code).to.equal(0);
});
it("does not allow to buy an item if the price is too low", async () => {
main.setContractBalance(contract, 10 * main.TON());
let ownerAddr = randomAddress("owner");
const sendToSelfMessage = internalMessage({
from: ownerAddr,
body: main.createItem({ domain: "test" }),
value: new BN(10 * main.TON()),
});
const res = await contract.sendInternalMessage(sendToSelfMessage);
expect(res.type).to.equal("failed");
});
});

Loading…
Cancel
Save