UNPKG

@broxus/tip4

Version:

Set of ready-to-use tvm nft token contracts following tip4 standard

373 lines (311 loc) 12 kB
import { Contract, Address, toNano, zeroAddress } from 'locklift'; import { expect } from 'chai'; import { BigNumber } from 'bignumber.js'; import JsonTemplate from '../metadata-template.json'; import { CollectionAbi, NftAbi } from '../build/factorySource'; describe('Collection', () => { let owner: Address; let user: Address; let collection: Contract<CollectionAbi>; let nft: Contract<NftAbi>; before('deploy contracts', async () => { await locklift.deployments.fixture({ exclude: ['nft', 'callbacks'] }); owner = locklift.deployments.getAccount('OwnerWallet').account.address; user = locklift.deployments.getAccount('UserWallet').account.address; collection = locklift.deployments.getContract<CollectionAbi>('Collection'); }); describe('ownable', () => { it('should return valid owner', async () => { const currentOwner = await collection.methods .owner() .call() .then((r) => r.value0); return expect(currentOwner.toString()).to.be.equal(owner.toString()); }); it('should throw 100 error code for ownership transfer called from user', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .transferOwnership({ newOwner: user }) .send({ from: user, amount: toNano(0.5), bounce: true }), { allowedCodes: { compute: [100] } }, ); return expect(traceTree) .to.call('transferOwnership') .count(1) .and.to.have.error(100); }); it('should transfer ownership to user', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .transferOwnership({ newOwner: user }) .send({ from: owner, amount: toNano(0.5) }), ); return expect(traceTree) .to.call('transferOwnership') .count(1) .and.to.emit('OwnershipTransferred') .count(1) .withNamedArgs({ newOwner: user, oldOwner: owner }); }); it('should transfer ownership back to owner', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .transferOwnership({ newOwner: owner }) .send({ from: user, amount: toNano(0.5) }), ); return expect(traceTree) .to.call('transferOwnership') .count(1) .and.to.emit('OwnershipTransferred') .count(1) .withNamedArgs({ newOwner: owner, oldOwner: user }); }); }); describe('tip-4.1', () => { it('should return total supply 0', async () => { const totalSupply = await collection.methods .totalSupply({ answerId: 0 }) .call() .then((r) => r.count); return expect(totalSupply).to.be.equal('0'); }); it('should return right salted Nft code and its hash', async () => { const collectionNftCode = await collection.methods .nftCode({ answerId: 0 }) .call() .then((r) => r.code); const collectionNftCodeHash = await collection.methods .nftCodeHash({ answerId: 0 }) .call() .then((r) => new BigNumber(r.codeHash)); const NftCode = locklift.factory.getContractArtifacts('Nft').code; const NftCodeWithSalt = await locklift.provider .setCodeSalt({ code: NftCode, salt: { structure: [{ 'name': 'collection', 'type': 'address' }] as const, data: { collection: collection.address }, }, }); expect(collectionNftCodeHash.isEqualTo(NftCodeWithSalt.hash, 16)).to.be.true; return expect(collectionNftCode).to.be.equal(NftCodeWithSalt.code); }); it('should return valid address for minted NFT', async () => { nft = await collection.methods .nftAddress({ answerId: 0, id: 0 }) .call() .then((r) => locklift.factory.getDeployedContract('Nft', r.nft)); const { traceTree } = await locklift.tracing.trace( collection.methods .mintNft({ _owner: user, _json: JSON.stringify(JsonTemplate.nfts[0]) }) .send({ from: owner, amount: toNano(5) }) ); return expect(traceTree) .to.call('mintNft') .count(1) .and.to.emit('NftCreated') .count(1) .withNamedArgs({ id: '0', nft: nft.address, owner: user, manager: user, creator: owner }); }); it('should return total supply 1', async () => { const totalSupply = await collection.methods .totalSupply({ answerId: 0 }) .call() .then((r) => r.count); return expect(totalSupply).to.be.equal('1'); }); it('should burn NFT with ID 0', async () => { const { traceTree } = await locklift.tracing.trace( nft.methods .burn({ sendGasTo: user, callbackTo: zeroAddress, callbackPayload: '' }) .send({ from: user, amount: toNano(3) }) ); return expect(traceTree) .to.call('acceptNftBurn') .count(1) .and.to.emit('NftBurned') .count(1) .withNamedArgs({ id: '0', nft: nft.address, owner: user, manager: user }); }); it('should return total supply 0', async () => { const totalSupply = await collection.methods .totalSupply({ answerId: 0 }) .call() .then((r) => r.count); return expect(totalSupply).to.be.equal('0'); }); }); describe('tip-4.2', () => { it('should return JSON of collection', async () => { const collectionJson = await collection.methods .getJson({ answerId: 0 }) .call() .then((r) => r.json); return expect(collectionJson).to.be.equal(JSON.stringify(JsonTemplate.collection)); }); }); describe('tip-4.3', () => { it('should return IndexBasis code and its hash', async () => { const collectionIndexBasisCode = await collection.methods .indexBasisCode({ answerId: 0 }) .call() .then((r) => r.code); const collectionIndexBasisCodeHash = await collection.methods .indexBasisCodeHash({ answerId: 0 }) .call() .then((r) => new BigNumber(r.hash)); const IndexBasisArtifacts = locklift.factory.getContractArtifacts('IndexBasis'); expect(collectionIndexBasisCodeHash.isEqualTo(IndexBasisArtifacts.codeHash, 16)).to.be.true; return expect(collectionIndexBasisCode).to.be.equal(IndexBasisArtifacts.code); }); it('should return Index code and its hash', async () => { const collectionIndexCode = await collection.methods .indexCode({ answerId: 0 }) .call() .then((r) => r.code); const collectionIndexCodeHash = await collection.methods .indexCodeHash({ answerId: 0 }) .call() .then((r) => new BigNumber(r.hash)); const IndexArtifacts = locklift.factory.getContractArtifacts('Index'); expect(collectionIndexCodeHash.isEqualTo(IndexArtifacts.codeHash, 16)).to.be.true; return expect(collectionIndexCode).to.be.equal(IndexArtifacts.code); }); it('should return valid address for deployed IndexBasis', async () => { const index = await collection.methods .resolveIndexBasis({ answerId: 0 }) .call() .then((r) => locklift.factory.getDeployedContract('IndexBasis', r.indexBasis)); const indexCollection = await index.methods .getInfo({ answerId: 0 }) .call() .then((r) => r.collection); return expect(indexCollection.toString()).to.be.equal(collection.address.toString()); }); }); describe('tip-6', () => { it('should return true for tip-4.1 interface', async () => { const isSupported = await collection.methods .supportsInterface({ answerId: 0, interfaceID: new BigNumber('0x1217aaab').toString(10) }) .call() .then((r) => r.value0); return expect(isSupported).to.be.true; }); it('should return true for tip-4.2 interface', async () => { const isSupported = await collection.methods .supportsInterface({ answerId: 0, interfaceID: new BigNumber('0x24d7d5f5').toString(10) }) .call() .then((r) => r.value0); return expect(isSupported).to.be.true; }); it('should return true for tip-4.3 interface', async () => { const isSupported = await collection.methods .supportsInterface({ answerId: 0, interfaceID: new BigNumber('0x4387bbfb').toString(10) }) .call() .then((r) => r.value0); return expect(isSupported).to.be.true; }); it('should return true for tip-6 interface', async () => { const isSupported = await collection.methods .supportsInterface({ answerId: 0, interfaceID: new BigNumber('0x3204ec29').toString(10) }) .call() .then((r) => r.value0); return expect(isSupported).to.be.true; }); it('should return false for unknown interface', async () => { const isSupported = await collection.methods .supportsInterface({ answerId: 0, interfaceID: new BigNumber('0x3204ec25').toString(10) }) .call() .then((r) => r.value0); return expect(isSupported).to.be.false; }); }); describe('mint and batch mint', () => { it('should 100 error code for mint called from user', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .mintNft({ _owner: user, _json: JSON.stringify(JsonTemplate.nfts[0]) }) .send({ from: user, amount: toNano(3), bounce: true }), { allowedCodes: { compute: [100] } }, ); return expect(traceTree).to.have.error(100); }); it('should 100 error code for batch mint called from user', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .batchMintNft({ _owner: user, _jsons: [JSON.stringify(JsonTemplate.nfts[0])] }) .send({ from: user, amount: toNano(3), bounce: true }), { allowedCodes: { compute: [100] } }, ); return expect(traceTree).to.have.error(100); }); it('should throw VALUE_IS_LESS_THAN_REQUIRED for mint', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .mintNft({ _owner: user, _json: JSON.stringify(JsonTemplate.nfts[0]) }) .send({ from: owner, amount: toNano(0.5), bounce: true }), { allowedCodes: { compute: [104] } }, ); return expect(traceTree).to.have.error(104); }); it('should throw VALUE_IS_LESS_THAN_REQUIRED for batch mint', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .batchMintNft({ _owner: user, _jsons: [JSON.stringify(JsonTemplate.nfts[0])] }) .send({ from: owner, amount: toNano(3), bounce: true }), { allowedCodes: { compute: [104] } }, ); return expect(traceTree).to.have.error(104); }); it('should mint 3 NFTs by batchMint', async () => { const { traceTree } = await locklift.tracing.trace( collection.methods .batchMintNft({ _owner: user, _jsons: [ JSON.stringify(JsonTemplate.nfts[0]), JSON.stringify(JsonTemplate.nfts[0]), JSON.stringify(JsonTemplate.nfts[0]) ] }) .send({ from: owner, amount: toNano(11) }) ); const totalSupply = await collection.methods .totalSupply({ answerId: 0 }) .call() .then((r) => r.count); const events = traceTree?.findEventsForContract({ contract: collection, name: 'NftCreated' as const }); expect(totalSupply).to.be.equal('3'); return expect(events?.length).to.be.equal(3); }); }); });