Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
temp
build

.env
4 changes: 3 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"dotenv": "^16.4.5"
}
}
13 changes: 7 additions & 6 deletions scripts/deployNftCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Address, toNano } from '@ton/core';
import { NftCollection } from '../wrappers/NftCollection';
import { compile, NetworkProvider } from '@ton/blueprint';
import { buildCollectionContentCell, setItemContentCell } from './nftContent/onChain';
import * as dotenv from 'dotenv';
dotenv.config();

const randomSeed= Math.floor(Math.random() * 10000);

Expand All @@ -11,21 +13,20 @@ export async function run(provider: NetworkProvider) {
ownerAddress: provider.sender().address!!,
nextItemIndex: 0,
collectionContent: buildCollectionContentCell({
name: "OnChain collection",
description: "Collection of items with onChain metadata",
image: "https://raw.githubusercontent.com/Cosmodude/Nexton/main/Nexton_Logo.jpg"
name: process.env.COLLECTION_NAME!,
description: process.env.COLLECTION_DESCRIPTION!,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need these params from .env?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't hardcoded data more demonstrative?

image: process.env.COLLECTION_IMAGE!
}),
nftItemCode: await compile("NftItem"),
royaltyParams: {
royaltyFactor: Math.floor(Math.random() * 500),
royaltyBase: 1000,
royaltyFactor: parseInt(process.env.COLLECTION_ROYALTY_PERCENT!),
royaltyBase: 100,
royaltyAddress: provider.sender().address as Address
}
}, await compile('NftCollection')));

console.log(provider.sender().address as Address)
await nftCollection.sendDeploy(provider.sender(), toNano('0.05'));
console.log()
await provider.waitForDeploy(nftCollection.address);

const mint = await nftCollection.sendMintNft(provider.sender(),{
Expand Down
115 changes: 107 additions & 8 deletions tests/NftCollection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,53 @@
import { Blockchain, SandboxContract } from '@ton/sandbox';
import { Cell, toNano } from '@ton/core';
import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
import { Address, Cell, toNano } from '@ton/core';
import { NftCollection } from '../wrappers/NftCollection';
import { buildCollectionContentCell, setItemContentCell } from '../scripts/nftContent/onChain';
import '@ton/test-utils';
import { compile } from '@ton/blueprint';
import { flattenTransaction } from '@ton/test-utils';
import * as dotenv from 'dotenv';
dotenv.config();

describe('NftCollection', () => {
let code: Cell;
let collectionCode: Cell;
let item: Cell;
let collectionContent: Cell;
let nftItemContent: Cell;

beforeAll(async () => {
code = await compile('NftCollection');
collectionCode = await compile('NftCollection');
item = await compile('NftItem');
});

let blockchain: Blockchain;
let nftCollection: SandboxContract<NftCollection>;
let collectionOwner: SandboxContract<TreasuryContract>;

beforeEach(async () => {
blockchain = await Blockchain.create();
collectionOwner = await blockchain.treasury("ownerWallet");
collectionContent = buildCollectionContentCell({
name: process.env.COLLECTION_NAME!,
description: process.env.COLLECTION_DESCRIPTION!,
image: process.env.COLLECTION_IMAGE!
});
nftItemContent = setItemContentCell({
name: "OnChain",
description: "Holds onchain metadata",
image: "https://raw.githubusercontent.com/Cosmodude/Nexton/main/Nexton_Logo.jpg",
});

nftCollection = blockchain.openContract(NftCollection.createFromConfig({}, code));
nftCollection = blockchain.openContract(NftCollection.createFromConfig({
ownerAddress: collectionOwner.address,
nextItemIndex: 0,
collectionContent: collectionContent,
nftItemCode: item,
royaltyParams: {
royaltyFactor: parseInt(process.env.COLLECTION_ROYALTY_PERCENT!), //Math.floor(Math.random() * 500),
royaltyBase: 100, //1000,
royaltyAddress: collectionOwner.getSender().address as Address
}
},collectionCode));

const deployer = await blockchain.treasury('deployer');

Expand All @@ -31,8 +61,77 @@ describe('NftCollection', () => {
});
});

it('should deploy', async () => {
// the check is done inside beforeEach
// blockchain and nftCollection are ready to use
it('should get collection data after collection has been deployed', async () => {
const collection_data = await nftCollection.getCollectionData();
// check next_item_index
expect(collection_data).toHaveProperty("nextItemId", BigInt(0));
// check collection content
expect(collection_data.collectionContent).toEqualCell(collectionContent);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to check all the content fields one by one (name, description and image).

You can refer to these tests

https://github.com/Cosmodude/Nexton/blob/main/tests/NexTon.spec.ts#L111C9-L111C15

// check owner address
expect(collection_data.ownerAddress.toString()).toBe(collectionOwner.address.toString());
});

it('should get roylty params after collection has been deployed', async () => {
const royalty_params = await nftCollection.getRoyaltyParams();
expect(royalty_params.royaltyFactor).toBe(BigInt(parseInt(process.env.COLLECTION_ROYALTY_PERCENT!)));
expect(royalty_params.royaltyBase).toBe(BigInt(100));
expect(royalty_params.royaltyAddress.toString()).toBe(collectionOwner.address.toString());
});

it ('should mint NFT item if requested by collection owner', async () => {

const nftOwner = await blockchain.treasury('NewNFTOwner');
const mintResult = await nftCollection.sendMintNft(collectionOwner.getSender(), {
value: toNano("0.02"),
queryId: Math.floor(Math.random() * 10000),
amount: toNano("0.014"),
itemIndex: 0,
itemOwnerAddress: nftOwner.getSender().address!!,
itemContent: nftItemContent
});
// const arr = mintResult.transactions.map(tx => flattenTransaction(tx));
// console.log(arr);
// check that tx to the collection address is successful
expect(mintResult.transactions).toHaveTransaction({
to: nftCollection.address,
op: 1,
value: toNano("0.02"),
success: true
})
// check that getItemAddressByIndex() returns nft item address
const nftItemAddr = await nftCollection.getItemAddressByIndex({ type: 'int', value: BigInt(0) });
// check that tx to the nft item address is successful
expect(mintResult.transactions).toHaveTransaction({
from: nftCollection.address,
to: nftItemAddr,
value: toNano("0.014"),
success: true
})
// check that next item index has been incremented
const collection_data = await nftCollection.getCollectionData();
expect(collection_data.nextItemId).toBe(BigInt(1));
});

it ('should return 401 error if mint item was requested by non-owner', async () => {
const nonOwnerWallet = await blockchain.treasury("non-owner");
const nftOwner = await blockchain.treasury('NewNFTOwner');

const result = await nftCollection.sendMintNft(nonOwnerWallet.getSender(), {
value: toNano("0.02"),
queryId: Math.floor(Math.random() * 10000),
amount: toNano("0.014"),
itemIndex: 0,
itemOwnerAddress: nftOwner.getSender().address!!,
itemContent: nftItemContent
});
// check that tx failed with 401 exit code
expect(result.transactions).toHaveTransaction({
to: nftCollection.address,
value: toNano("0.02"),
exitCode: 401,
op: 1,
success: false
});
})

});
14 changes: 13 additions & 1 deletion wrappers/NftCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class NftCollection implements Contract {
.endCell()
})
}

// GETTERS
async getCollectionData(provider: ContractProvider): Promise<{
nextItemId: bigint,
ownerAddress: Address,
Expand All @@ -130,4 +130,16 @@ export class NftCollection implements Contract {
return itemAddress;
}

async getRoyaltyParams(provider: ContractProvider) {
const royaltyParams = await provider.get("royalty_params", []);
const stack = royaltyParams.stack;
let numerator: bigint = stack.readBigNumber();
let denominator: bigint = stack.readBigNumber();
let destination: Address = stack.readAddress();
return {
royaltyFactor: numerator,
royaltyBase: denominator,
royaltyAddress: destination,
}
}
}