Browse Source

We can now deploy items and have a cli

master
Lev 2 years ago
parent
commit
a028b5c7d3
  1. 63
      bin/cli.js
  2. 8
      build/_build.ts
  3. 266
      build/_deploy.ts
  4. 7
      build/nft-collection.deploy.ts
  5. 2
      contracts/imports/dns-utils.fc
  6. 32
      contracts/main.ts
  7. 27
      package-lock.json
  8. 1
      package.json
  9. 3
      test/creation.spec.ts
  10. 3
      test/signing.spec.ts

63
bin/cli.js

@ -1,13 +1,15 @@
const main = require('../contracts/main'); const main = require('../contracts/main');
const subcommand = require('subcommand'); const subcommand = require('subcommand');
const {Address} = require("ton"); const {Address} = require("ton");
const {keyPairFromSeed, getSecureRandomBytes} = require("ton-crypto"); const {keyPairFromSeed, getSecureRandomBytes, keyPairFromSecretKey} = require("ton-crypto");
const {Base64} = require('@tonconnect/protocol');
const BN = require("bn.js");
const argv = process.argv.slice(2); const argv = process.argv.slice(2);
let commands = [ let commands = [
{ {
name: 'sign-message', name: 'buy-message',
options: [ // cliclopts options options: [ // cliclopts options
// { // {
// name: 'loud', // name: 'loud',
@ -31,12 +33,18 @@ let commands = [
help: 'Secret key, hex-encoded' help: 'Secret key, hex-encoded'
} }
], ],
command: function sign_message(args) { command: function message(args) {
let key = Buffer.from(args.key, 'hex'); let collection = Address.parse(args._[1]);
let collection = Address.parse(args.collection); if (args._[2] !== '') {
let buyer = Address.parse(args.buyer); let buyer = Address.parse(args._[2]);
let signature = main.signBuy(args.domain, collection, buyer, key); let key = Buffer.from(args._[3], 'hex');
console.log(signature); let signature = main.signBuy(args._[0], collection, buyer, key);
let msg = main.createItem({domain: args._[0], signature})
console.log(Base64.encode(msg.toBoc()) + ' ' + collection.toString());
} else {
let msg = main.createItem({domain: args._[0], signature: '0'})
console.log(Base64.encode(msg.toBoc()) + ' ' + collection.toString());
}
} }
}, },
{ {
@ -45,6 +53,45 @@ let commands = [
let keypair = keyPairFromSeed(await getSecureRandomBytes(32)); let keypair = keyPairFromSeed(await getSecureRandomBytes(32));
console.log(keypair.secretKey.toString('hex')); console.log(keypair.secretKey.toString('hex'));
} }
},
{
name: 'getpub', // get public key as BN from b64 secret key
options: [
{
name: 'key',
help: 'Secret key, hex-encoded'
}
],
command: function getpub(args) {
let keypair = keyPairFromSecretKey(Buffer.from(args._[0], 'base64'));
console.log(new BN(keypair.publicKey))
}
},
{
name: 'content-message',
options: [
{
name: 'domain',
help: 'domain name to set content for (test for test.example.ton)'
},
{
name: 'zone',
help: 'zone name to set content for (example.ton for test.example.ton)'
},
{
name: 'address',
help: 'address of the collection contract'
},
{
name: 'workchain',
help: '0 for mainnet, 1 for testnet'
}
],
command: function set_content(args) {
let msg = main.setContent({domain: args._[0], zone: args._[1]});
let addr = main.getItemAddr(Address.parse(args._[2]), args._[0], args._[3] === '1' ? -1 : 0);
console.log(Base64.encode(msg.toBoc()) + ' ' + addr.toString());
}
} }
] ]

8
build/_build.ts

@ -19,10 +19,10 @@ async function main() {
console.log("Build script running, let's find some FunC contracts to compile.."); console.log("Build script running, let's find some FunC contracts to compile..");
// if we have an explicit bin directory, use the executables there (needed for glitch.com) // if we have an explicit bin directory, use the executables there (needed for glitch.com)
if (fs.existsSync("bin")) { // if (fs.existsSync("bin")) {
process.env.PATH = path.join(__dirname, "..", "bin") + path.delimiter + process.env.PATH; // process.env.PATH = path.join(__dirname, "..", "bin") + path.delimiter + process.env.PATH;
process.env.FIFTPATH = path.join(__dirname, "..", "bin", "fiftlib"); // process.env.FIFTPATH = path.join(__dirname, "..", "bin", "fiftlib");
} // }
// make sure func compiler is available // make sure func compiler is available
const minSupportFunc = "0.2.0"; const minSupportFunc = "0.2.0";

266
build/_deploy.ts

@ -6,144 +6,152 @@
import axios from "axios"; import axios from "axios";
import axiosThrottle from "axios-request-throttle"; import axiosThrottle from "axios-request-throttle";
axiosThrottle.use(axios, { requestsPerSecond: 0.5 }); // required since toncenter jsonRPC limits to 1 req/sec without API key
axiosThrottle.use(axios, {requestsPerSecond: 0.5}); // required since toncenter jsonRPC limits to 1 req/sec without API key
import dotenv from "dotenv"; import dotenv from "dotenv";
dotenv.config(); dotenv.config();
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import glob from "fast-glob"; import glob from "fast-glob";
import { Address, Cell, CellMessage, CommonMessageInfo, fromNano, InternalMessage, StateInit, toNano } from "ton"; import {Address, Cell, CellMessage, CommonMessageInfo, fromNano, InternalMessage, StateInit, toNano} from "ton";
import { TonClient, WalletContract, WalletV3R2Source, contractAddress, SendMode } from "ton"; import {TonClient, WalletContract, WalletV3R2Source, contractAddress, SendMode} from "ton";
import { mnemonicNew, mnemonicToWalletKey } from "ton-crypto"; import {mnemonicNew, mnemonicToWalletKey} from "ton-crypto";
async function main() { async function main() {
console.log(`=================================================================`); console.log(`=================================================================`);
console.log(`Deploy script running, let's find some contracts to deploy..`); console.log(`Deploy script running, let's find some contracts to deploy..`);
const isTestnet = process.env.TESTNET || process.env.npm_lifecycle_event == "deploy:testnet"; const isTestnet = process.env.TESTNET || process.env.npm_lifecycle_event == "deploy:testnet";
// check input arguments (given through environment variables) // check input arguments (given through environment variables)
if (isTestnet) { if (isTestnet) {
console.log(`\n* We are working with 'testnet' (https://t.me/testgiver_ton_bot will give you test TON)`); console.log(`\n* We are working with 'testnet' (https://t.me/testgiver_ton_bot will give you test TON)`);
} else { } else {
console.log(`\n* We are working with 'mainnet'`); console.log(`\n* We are working with 'mainnet'`);
}
// initialize globals
const client = new TonClient({ endpoint: `https://${isTestnet ? "testnet." : ""}toncenter.com/api/v2/jsonRPC` });
const deployerWalletType = "org.ton.wallets.v3.r2"; // also see WalletV3R2Source class used below
const newContractFunding = toNano(0.02); // this will be (almost in full) the balance of a new deployed contract and allow it to pay rent
const workchain = 0; // normally 0, only special contracts should be deployed to masterchain (-1)
// make sure we have a wallet mnemonic to deploy from (or create one if not found)
const deployConfigEnv = ".env";
let deployerMnemonic;
if (!fs.existsSync(deployConfigEnv) || !process.env.DEPLOYER_MNEMONIC) {
console.log(`\n* Config file '${deployConfigEnv}' not found, creating a new wallet for deploy..`);
deployerMnemonic = (await mnemonicNew(24)).join(" ");
const deployWalletEnvContent = `DEPLOYER_WALLET=${deployerWalletType}\nDEPLOYER_MNEMONIC="${deployerMnemonic}"\n`;
fs.writeFileSync(deployConfigEnv, deployWalletEnvContent);
console.log(` - Created new wallet in '${deployConfigEnv}' - keep this file secret!`);
} else {
console.log(`\n* Config file '${deployConfigEnv}' found and will be used for deployment!`);
deployerMnemonic = process.env.DEPLOYER_MNEMONIC;
}
// open the wallet and make sure it has enough TON
const walletKey = await mnemonicToWalletKey(deployerMnemonic.split(" "));
const walletContract = WalletContract.create(client, WalletV3R2Source.create({ publicKey: walletKey.publicKey, workchain }));
console.log(` - Wallet address used to deploy from is: ${walletContract.address.toFriendly()}`);
const walletBalance = await client.getBalance(walletContract.address);
if (walletBalance.lt(toNano(0.2))) {
console.log(` - ERROR: Wallet has less than 0.2 TON for gas (${fromNano(walletBalance)} TON), please send some TON for gas first`);
process.exit(1);
} else {
console.log(` - Wallet balance is ${fromNano(walletBalance)} TON, which will be used for gas`);
}
// go over all the contracts we have deploy scripts for
const rootContracts = glob.sync(["build/*.deploy.ts"]);
for (const rootContract of rootContracts) {
// deploy a new root contract
console.log(`\n* Found root contract '${rootContract} - let's deploy it':`);
const contractName = path.parse(path.parse(rootContract).name).name;
// prepare the init data cell
const deployInitScript = require(__dirname + "/../" + rootContract);
if (typeof deployInitScript.initData !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initData()' function`);
process.exit(1);
}
console.log(1);
const initDataCell = deployInitScript.initData() as Cell;
console.log(2);
// prepare the init message
if (typeof deployInitScript.initMessage !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initMessage()' function`);
process.exit(1);
}
console.log(1);
const initMessageCell = deployInitScript.initMessage() as Cell | null;
// prepare the init code cell
const hexArtifact = `build/${contractName}.compiled.json`;
if (!fs.existsSync(hexArtifact)) {
console.log(` - ERROR: '${hexArtifact}' not found, did you build?`);
process.exit(1);
}
const initCodeCell = Cell.fromBoc(JSON.parse(fs.readFileSync(hexArtifact).toString()).hex)[0];
// make sure the contract was not already deployed
const newContractAddress = contractAddress({ workchain, initialData: initDataCell, initialCode: initCodeCell });
console.log(` - Based on your init code+data, your new contract address is: ${newContractAddress.toFriendly()}`);
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - Looks like the contract is already deployed in this address, skipping deployment`);
await performPostDeploymentTest(rootContract, deployInitScript, walletContract, walletKey.secretKey, newContractAddress);
continue;
} }
// deploy by sending an internal message to the deploying wallet // initialize globals
console.log(` - Let's deploy the contract on-chain..`); const client = new TonClient({
const seqno = await walletContract.getSeqNo(); endpoint: `https://${isTestnet ? "testnet." : ""}toncenter.com/api/v2/jsonRPC`,
const transfer = walletContract.createTransfer({ apiKey: isTestnet ? 'bed2b4589201ff3c575be653593f912a337c08eed416b60b02345763b9ee9c36' : 'a1e8a1055a387515158589dc7e9bad3035e7db2b9f9ea5cdad6b727f71e328db'
secretKey: walletKey.secretKey,
seqno: seqno,
sendMode: SendMode.PAY_GAS_SEPARATLY + SendMode.IGNORE_ERRORS,
order: new InternalMessage({
to: newContractAddress,
value: newContractFunding,
bounce: false,
body: new CommonMessageInfo({
stateInit: new StateInit({ data: initDataCell, code: initCodeCell }),
body: initMessageCell !== null ? new CellMessage(initMessageCell) : null,
}),
}),
}); });
await client.sendExternalMessage(walletContract, transfer); const deployerWalletType = "org.ton.wallets.v3.r2"; // also see WalletV3R2Source class used below
console.log(` - Deploy transaction sent successfully`); const newContractFunding = toNano(0.02); // this will be (almost in full) the balance of a new deployed contract and allow it to pay rent
const workchain = 0; // normally 0, only special contracts should be deployed to masterchain (-1)
// make sure that the contract was deployed
console.log(` - Block explorer link: https://${process.env.TESTNET ? "test." : ""}tonwhales.com/explorer/address/${newContractAddress.toFriendly()}`); // make sure we have a wallet mnemonic to deploy from (or create one if not found)
console.log(` - Waiting up to 20 seconds to check if the contract was actually deployed..`); const deployConfigEnv = ".env";
for (let attempt = 0; attempt < 10; attempt++) { let deployerMnemonic;
await sleep(2000); if (!fs.existsSync(deployConfigEnv) || !process.env.DEPLOYER_MNEMONIC) {
const seqnoAfter = await walletContract.getSeqNo(); console.log(`\n* Config file '${deployConfigEnv}' not found, creating a new wallet for deploy..`);
if (seqnoAfter > seqno) break; deployerMnemonic = (await mnemonicNew(24)).join(" ");
const deployWalletEnvContent = `DEPLOYER_WALLET=${deployerWalletType}\nDEPLOYER_MNEMONIC="${deployerMnemonic}"\n`;
fs.writeFileSync(deployConfigEnv, deployWalletEnvContent);
console.log(` - Created new wallet in '${deployConfigEnv}' - keep this file secret!`);
} else {
console.log(`\n* Config file '${deployConfigEnv}' found and will be used for deployment!`);
deployerMnemonic = process.env.DEPLOYER_MNEMONIC;
} }
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - SUCCESS! Contract deployed successfully to address: ${newContractAddress.toFriendly()}`); // open the wallet and make sure it has enough TON
const contractBalance = await client.getBalance(newContractAddress); const walletKey = await mnemonicToWalletKey(deployerMnemonic.split(" "));
console.log(` - New contract balance is now ${fromNano(contractBalance)} TON, make sure it has enough to pay rent`); const walletContract = WalletContract.create(client, WalletV3R2Source.create({
await performPostDeploymentTest(rootContract, deployInitScript, walletContract, walletKey.secretKey, newContractAddress); publicKey: walletKey.publicKey,
workchain
}));
console.log(` - Wallet address used to deploy from is: ${walletContract.address.toFriendly()}`);
const walletBalance = await client.getBalance(walletContract.address);
if (walletBalance.lt(toNano(0.2))) {
console.log(` - ERROR: Wallet has less than 0.2 TON for gas (${fromNano(walletBalance)} TON), please send some TON for gas first`);
process.exit(1);
} else { } else {
console.log(` - FAILURE! Contract address still looks uninitialized: ${newContractAddress.toFriendly()}`); console.log(` - Wallet balance is ${fromNano(walletBalance)} TON, which will be used for gas`);
} }
}
console.log(``); // go over all the contracts we have deploy scripts for
const rootContracts = glob.sync(["build/*.deploy.ts"]);
for (const rootContract of rootContracts) {
// deploy a new root contract
console.log(`\n* Found root contract '${rootContract} - let's deploy it':`);
const contractName = path.parse(path.parse(rootContract).name).name;
// prepare the init data cell
const deployInitScript = require(__dirname + "/../" + rootContract);
if (typeof deployInitScript.initData !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initData()' function`);
process.exit(1);
}
console.log(1);
const initDataCell = deployInitScript.initData() as Cell;
console.log(2);
// prepare the init message
if (typeof deployInitScript.initMessage !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initMessage()' function`);
process.exit(1);
}
console.log(1);
const initMessageCell = deployInitScript.initMessage() as Cell | null;
// prepare the init code cell
const hexArtifact = `build/${contractName}.compiled.json`;
if (!fs.existsSync(hexArtifact)) {
console.log(` - ERROR: '${hexArtifact}' not found, did you build?`);
process.exit(1);
}
const initCodeCell = Cell.fromBoc(JSON.parse(fs.readFileSync(hexArtifact).toString()).hex)[0];
// make sure the contract was not already deployed
const newContractAddress = contractAddress({workchain, initialData: initDataCell, initialCode: initCodeCell});
console.log(` - Based on your init code+data, your new contract address is: ${newContractAddress.toFriendly()}`);
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - Looks like the contract is already deployed in this address, skipping deployment`);
await performPostDeploymentTest(rootContract, deployInitScript, walletContract, walletKey.secretKey, newContractAddress);
continue;
}
// deploy by sending an internal message to the deploying wallet
console.log(` - Let's deploy the contract on-chain..`);
const seqno = await walletContract.getSeqNo();
const transfer = walletContract.createTransfer({
secretKey: walletKey.secretKey,
seqno: seqno,
sendMode: SendMode.PAY_GAS_SEPARATLY + SendMode.IGNORE_ERRORS,
order: new InternalMessage({
to: newContractAddress,
value: newContractFunding,
bounce: false,
body: new CommonMessageInfo({
stateInit: new StateInit({data: initDataCell, code: initCodeCell}),
body: initMessageCell !== null ? new CellMessage(initMessageCell) : null,
}),
}),
});
await client.sendExternalMessage(walletContract, transfer);
console.log(` - Deploy transaction sent successfully`);
// make sure that the contract was deployed
console.log(` - Block explorer link: https://${process.env.TESTNET ? "test." : ""}tonwhales.com/explorer/address/${newContractAddress.toFriendly()}`);
console.log(` - Waiting up to 20 seconds to check if the contract was actually deployed..`);
for (let attempt = 0; attempt < 10; attempt++) {
await sleep(2000);
const seqnoAfter = await walletContract.getSeqNo();
if (seqnoAfter > seqno) break;
}
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - SUCCESS! Contract deployed successfully to address: ${newContractAddress.toFriendly()}`);
const contractBalance = await client.getBalance(newContractAddress);
console.log(` - New contract balance is now ${fromNano(contractBalance)} TON, make sure it has enough to pay rent`);
await performPostDeploymentTest(rootContract, deployInitScript, walletContract, walletKey.secretKey, newContractAddress);
} else {
console.log(` - FAILURE! Contract address still looks uninitialized: ${newContractAddress.toFriendly()}`);
}
}
console.log(``);
} }
main(); main();
@ -151,14 +159,14 @@ main();
// helpers // helpers
function sleep(ms: number) { function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }
async function performPostDeploymentTest(rootContract: string, deployInitScript: any, walletContract: WalletContract, secretKey: Buffer, newContractAddress: Address) { async function performPostDeploymentTest(rootContract: string, deployInitScript: any, walletContract: WalletContract, secretKey: Buffer, newContractAddress: Address) {
if (typeof deployInitScript.postDeployTest !== "function") { if (typeof deployInitScript.postDeployTest !== "function") {
console.log(` - Not running a post deployment test, '${rootContract}' does not have 'postDeployTest()' function`); console.log(` - Not running a post deployment test, '${rootContract}' does not have 'postDeployTest()' function`);
return; return;
} }
console.log(` - Running a post deployment test:`); console.log(` - Running a post deployment test:`);
await deployInitScript.postDeployTest(walletContract, secretKey, newContractAddress); await deployInitScript.postDeployTest(walletContract, secretKey, newContractAddress);
} }

7
build/nft-collection.deploy.ts

@ -1,15 +1,16 @@
import * as main from "../contracts/main"; import * as main from "../contracts/main";
import { Address, toNano, TupleSlice, WalletContract } from "ton"; import { Address, toNano, TupleSlice, WalletContract } from "ton";
// import { sendInternalMessageWithWallet } from "../test/helpers"; // import { sendInternalMessageWithWallet } from "../test/helpers";
import { hex } from "../build/nft-item.compiled.json"; import {hex as item_code} from "../build/nft-item.compiled.json";
import { Builder, Cell, Slice } from "ton"; import { Builder, Cell, Slice } from "ton";
import BN from "bn.js";
// return the init Cell of the contract storage (according to load_data() contract method) // return the init Cell of the contract storage (according to load_data() contract method)
export function initData() { export function initData() {
return main.collectionData({ return main.collectionData({
ownerAddress: Address.parseFriendly("kQBw4_jZTQVbOSDbUjAMibTHWbstrCqjOnzvUTCphGpTFDrK").address, ownerAddress: Address.parseFriendly("kQBw4_jZTQVbOSDbUjAMibTHWbstrCqjOnzvUTCphGpTFDrK").address,
code: Cell.fromBoc(hex)[0], code: Cell.fromBoc(item_code)[0],
ownerKey: 0 // 63181357919630091755807889549433422416741950993093777020964723182484811889834 ownerKey: new BN(0) // 63181357919630091755807889549433422416741950993093777020964723182484811889834
}); });
} }

2
contracts/imports/dns-utils.fc

@ -200,7 +200,7 @@ int price_function(int length, int multiplierx10, int steepness) {
if (length >= 9) { if (length >= 9) {
price = 10; price = 10;
} }
price = (steepness * price + (10 - steepness) * 50) / 10; price = (steepness * price + (10 - steepness) * 50) / 50;
return price * one_ton * multiplierx10 / 10; return price * one_ton * multiplierx10 / 10;
} }

32
contracts/main.ts

@ -4,6 +4,7 @@ import {SmartContract} from "ton-contract-executor";
import {encodeOffChainContent, makeSnakeCell} from "./utils"; import {encodeOffChainContent, makeSnakeCell} from "./utils";
import {randomBytes} from "crypto"; import {randomBytes} from "crypto";
import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey} from "ton-crypto"; import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey} from "ton-crypto";
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";
@ -29,7 +30,7 @@ export function data(params: { ownerAddress: Address; collectionAddress: Address
.storeUint(0, 256) .storeUint(0, 256)
.storeAddress(params.collectionAddress) .storeAddress(params.collectionAddress)
.storeAddress(params.ownerAddress) .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(encodeOffChainContent(`https://api.agorata.io/collection/${params.domain}.json`)) // https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md
.storeRef(makeSnakeCell(Buffer.from(params.domain))) .storeRef(makeSnakeCell(Buffer.from(params.domain)))
.storeDict(null) .storeDict(null)
.storeUint(0, 64).endCell(); .storeUint(0, 64).endCell();
@ -39,16 +40,19 @@ export function data(params: { ownerAddress: Address; collectionAddress: Address
export function collectionData(params: { export function collectionData(params: {
code: Cell, ownerAddress: Address, ownerKey: BN, code: Cell, ownerAddress: Address, ownerKey: BN,
price_multiplier?: number, price_steepness?: number price_multiplier?: number, price_steepness?: number, zone?: string
}): Cell { }): Cell {
if (params.price_multiplier == undefined) { if (params.price_multiplier == undefined) {
params.price_multiplier = 10; params.price_multiplier = 1;
} }
if (params.price_steepness == undefined) { if (params.price_steepness == undefined) {
params.price_steepness = 5; params.price_steepness = 1;
}
if (params.zone == undefined) {
params.zone = "example";
} }
return beginCell() return beginCell()
.storeRef(encodeOffChainContent("https://agorata.io/collection.json")) .storeRef(encodeOffChainContent(`https://api.agorata.io/data/${params.zone}.json`)) // https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md
.storeRef(params.code) .storeRef(params.code)
.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)
@ -99,6 +103,17 @@ export function signBuy(domain: string, collectionAddress: Address, buyerAddress
return asciiEncode(signature); return asciiEncode(signature);
} }
export function getItemAddr(collectionAddress: Address, domain: string, workchain: number): Address {
let item_data_init = beginCell()
.storeUint(new BN(hashCell(beginCell().storeBuffer(Buffer.from(domain)).endCell())), 256)
.storeAddress(collectionAddress).endCell();
let item_state_init = beginCell().storeUint(0, 2)
.storeDict(Cell.fromBoc(item_code)[0])
.storeDict(item_data_init)
.storeUint(0, 1).endCell();
return new Address(workchain, hashCell(item_state_init));
}
export function TON(): number { export function TON(): number {
return 1000000000; return 1000000000;
} }
@ -119,6 +134,13 @@ export function createItem(params: { domain: String, signature?: String }): Cell
.endCell(); .endCell();
} }
export function setContent(params: { domain: string, zone: string }) {
return beginCell()
.storeUint(0x1a0b9d51 , 32)
.storeRef(encodeOffChainContent(`https://api.agorata.io/data/${params.zone}/${params.domain}.json`))
.endCell();
}
export function instantBuySignature(receiverAddress: Address, issuedCollectionAddr: Address, amount: number, domain: Cell, privateKey: Buffer): Buffer { 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 messageToSign = beginCell().storeAddress(receiverAddress).storeAddress(issuedCollectionAddr).storeUint(amount, 256).storeRef(domain).endCell();
let hash = messageToSign.hash(); let hash = messageToSign.hash();

27
package-lock.json generated

@ -10,6 +10,7 @@
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tonconnect/protocol": "^2.0.1",
"semver": "^7.3.7", "semver": "^7.3.7",
"subcommand": "^2.1.1", "subcommand": "^2.1.1",
"ton-compiler": "^2.0.0" "ton-compiler": "^2.0.0"
@ -303,6 +304,14 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/@tonconnect/protocol": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.0.1.tgz",
"integrity": "sha512-jkSj6EKjIlHnJxrtxdlO7KqVJe41yrIgqamGZiqziKH6iwx0m9YyKvuIREd6CmWY2jbsev3BvBWqPp9KH6HrRw==",
"dependencies": {
"tweetnacl-util": "^0.15.1"
}
},
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -1962,6 +1971,11 @@
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
"dev": true "dev": true
}, },
"node_modules/tweetnacl-util": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
"integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw=="
},
"node_modules/type-detect": { "node_modules/type-detect": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@ -2263,6 +2277,14 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@tonconnect/protocol": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.0.1.tgz",
"integrity": "sha512-jkSj6EKjIlHnJxrtxdlO7KqVJe41yrIgqamGZiqziKH6iwx0m9YyKvuIREd6CmWY2jbsev3BvBWqPp9KH6HrRw==",
"requires": {
"tweetnacl-util": "^0.15.1"
}
},
"@tsconfig/node10": { "@tsconfig/node10": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@ -3497,6 +3519,11 @@
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
"dev": true "dev": true
}, },
"tweetnacl-util": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
"integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw=="
},
"type-detect": { "type-detect": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",

1
package.json

@ -47,6 +47,7 @@
"node": ">=16.15.0" "node": ">=16.15.0"
}, },
"dependencies": { "dependencies": {
"@tonconnect/protocol": "^2.0.1",
"semver": "^7.3.7", "semver": "^7.3.7",
"subcommand": "^2.1.1", "subcommand": "^2.1.1",
"ton-compiler": "^2.0.0" "ton-compiler": "^2.0.0"

3
test/creation.spec.ts

@ -9,6 +9,7 @@ import * as main from "../contracts/main";
import { internalMessage, randomAddress } from "./helpers"; import { internalMessage, randomAddress } from "./helpers";
import { hex } from "../build/nft-collection.compiled.json"; import { hex } from "../build/nft-collection.compiled.json";
import {hex as item_code} from "../build/nft-item.compiled.json";
import {makeSnakeCell} from "../contracts/utils"; import {makeSnakeCell} from "../contracts/utils";
describe("Creating items tests", () => { describe("Creating items tests", () => {
@ -20,7 +21,7 @@ describe("Creating items tests", () => {
Cell.fromBoc(hex)[0], Cell.fromBoc(hex)[0],
main.collectionData({ main.collectionData({
ownerAddress: randomAddress("owner"), ownerAddress: randomAddress("owner"),
code: Cell.fromBoc(hex)[0], code: Cell.fromBoc(item_code)[0],
ownerKey: 0, ownerKey: 0,
}), }),
{ debug: debug } { debug: debug }

3
test/signing.spec.ts

@ -10,6 +10,7 @@ import * as main from "../contracts/main";
import {internalMessage, randomAddress} from "./helpers"; import {internalMessage, randomAddress} from "./helpers";
import {hex} from "../build/nft-collection.compiled.json"; import {hex} from "../build/nft-collection.compiled.json";
import {hex as item_code} from "../build/nft-item.compiled.json";
import {makeSnakeCell} from "../contracts/utils"; import {makeSnakeCell} from "../contracts/utils";
import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey} from "ton-crypto"; import {keyPairFromSeed, KeyPair, sign, keyPairFromSecretKey} from "ton-crypto";
import {signBuy} from "../contracts/main"; import {signBuy} from "../contracts/main";
@ -18,7 +19,7 @@ let ownerKeys = keyPairFromSeed(Buffer.from("00000000000000000000000000000000000
let ownerPubNum = new BN(ownerKeys.publicKey); let ownerPubNum = new BN(ownerKeys.publicKey);
let data = main.collectionData({ let data = main.collectionData({
ownerAddress: randomAddress("owner"), ownerAddress: randomAddress("owner"),
code: Cell.fromBoc(hex)[0], code: Cell.fromBoc(item_code)[0],
ownerKey: ownerPubNum, ownerKey: ownerPubNum,
}); });

Loading…
Cancel
Save