--- a/tests/package.json +++ b/tests/package.json @@ -26,7 +26,8 @@ "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts", "testApprove": "mocha --timeout 9999999 -r ts-node/register ./**/approve.test.ts", "testTransferFrom": "mocha --timeout 9999999 -r ts-node/register ./**/transferFrom.test.ts", - "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts" + "testCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts", + "testToggleContractWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/toggleContractWhiteList.test.ts" }, "author": "", "license": "Apache 2.0", --- a/tests/src/contracts.test.ts +++ b/tests/src/contracts.test.ts @@ -2,95 +2,20 @@ import chaiAsPromised from 'chai-as-promised'; import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; import fs from "fs"; -import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract"; -import { IKeyringPair } from "@polkadot/types/types"; -import { ApiPromise, Keyring } from "@polkadot/api"; -import { ApiTypes, SubmittableExtrinsic } from "@polkadot/api/types"; +import { Abi, ContractPromise as Contract } from "@polkadot/api-contract"; import privateKey from "./substrate/privateKey"; +import { + deployFlipper, + getFlipValue +} from "./util/contracthelpers"; chai.use(chaiAsPromised); const expect = chai.expect; -import { BigNumber } from 'bignumber.js'; -import { findUnusedAddress } from './util/helpers'; const value = 0; const gasLimit = 3000n * 1000000n; -const endowment = `1000000000000000`; const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6'; - -function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise { - return new Promise(async (resolve, reject) => { - const unsub = await code - .createBlueprint() - .signAndSend(alice, (result) => { - if (result.status.isInBlock || result.status.isFinalized) { - // here we have an additional field in the result, containing the blueprint - resolve(result.blueprint); - unsub(); - } - }) - }); -} - -function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise { - return new Promise(async (resolve, reject) => { - const endowment = 1000000000000000n; - const initValue = true; - - const unsub = await blueprint.tx - .new(endowment, gasLimit, initValue) - .signAndSend(alice, (result) => { - if (result.status.isInBlock || result.status.isFinalized) { - unsub(); - resolve(result); - } - }); - }); -} -async function prepareDeployer(api: ApiPromise) { - // Find unused address - const deployer = await findUnusedAddress(api); - - // Transfer balance to it - const keyring = new Keyring({ type: 'sr25519' }); - const alice = keyring.addFromUri(`//Alice`); - let amount = new BigNumber(endowment); - amount = amount.plus(1e15); - const tx = api.tx.balances.transfer(deployer.address, amount.toFixed()); - await submitTransactionAsync(alice, tx); - - return deployer; -} - -export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> { - const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8')); - const abi = new Abi(metadata); - - const deployer = await prepareDeployer(api); - - const wasm = fs.readFileSync('./src/flipper/flipper.wasm'); - - const code = new CodePromise(api, abi, wasm); - - const blueprint = await deployBlueprint(deployer, code); - const contract = (await deployContract(deployer, blueprint))['contract'] as Contract; - - const initialGetResponse = await getFlipValue(contract, deployer); - expect(initialGetResponse).to.be.true; - - return [contract, deployer]; -} - -async function getFlipValue(contract: Contract, deployer: IKeyringPair) { - const result = await contract.query.get(deployer.address, value, gasLimit); - - if(!result.result.isSuccess) { - throw `Failed to get flipper value`; - } - return (result.result.asSuccess.data[0] == 0x00) ? false : true; -} - describe('Contracts', () => { it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => { await usingApi(async api => { @@ -103,68 +28,6 @@ const afterFlipGetResponse = await getFlipValue(contract, deployer); expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.'); - }); - }); - - it(`Whitelisted account can call contract.`, async () => { - await usingApi(async api => { - const bob = privateKey("//Bob"); - - const [contract, deployer] = await deployFlipper(api); - - let expectedFlipValue = await getFlipValue(contract, deployer); - - const flip = contract.exec('flip', value, gasLimit); - await submitTransactionAsync(bob, flip); - expectedFlipValue = !expectedFlipValue; - const afterFlip = await getFlipValue(contract,deployer); - expect(afterFlip).to.be.eq(expectedFlipValue, `Anyone can call new contract.`); - - const deployerCanFlip = async () => { - expectedFlipValue = !expectedFlipValue; - const deployerFlip = contract.exec('flip', value, gasLimit); - await submitTransactionAsync(deployer, deployerFlip); - const aliceFlip1Response = await getFlipValue(contract, deployer); - expect(aliceFlip1Response).to.be.eq(expectedFlipValue, `Deployer always can flip.`); - }; - await deployerCanFlip(); - - const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true); - const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx); - const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit); - await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected; - const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer); - expect(flipValueAfterEnableWhiteList).to.be.eq(expectedFlipValue, `Enabling whitelist doesn't make it possible to call contract for everyone.`); - - await deployerCanFlip(); - - const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address); - const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx); - const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit); - await submitTransactionAsync(bob, flipWithWhitelistedBob); - expectedFlipValue = !expectedFlipValue; - const flipAfterWhiteListed = await getFlipValue(contract,deployer); - expect(flipAfterWhiteListed).to.be.eq(expectedFlipValue, `Bob was whitelisted, now he can flip.`); - - await deployerCanFlip(); - - const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address); - const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx); - const bobRemoved = contract.exec('flip', value, gasLimit); - await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected; - const afterBobRemoved = await getFlipValue(contract, deployer); - expect(afterBobRemoved).to.be.eq(expectedFlipValue, `Bob can't call contract, now when he is removeed from white list.`); - - await deployerCanFlip(); - - const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false); - const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx); - const whiteListDisabledFlip = contract.exec('flip', value, gasLimit); - await submitTransactionAsync(bob, whiteListDisabledFlip); - expectedFlipValue = !expectedFlipValue; - const afterWhiteListDisabled = await getFlipValue(contract,deployer); - expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`); - }); }); --- /dev/null +++ b/tests/src/toggleContractWhiteList.test.ts @@ -0,0 +1,151 @@ +import chai from "chai"; +import chaiAsPromised from 'chai-as-promised'; +import usingApi, { submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api"; +import privateKey from "./substrate/privateKey"; +import { + deployFlipper, + getFlipValue +} from "./util/contracthelpers"; +import { + getGenericResult +} from "./util/helpers" + +chai.use(chaiAsPromised); +const expect = chai.expect; + +const value = 0; +const gasLimit = 3000n * 1000000n; + +describe('Integration Test toggleContractWhiteList', () => { + + it(`Enable white list contract mode`, async () => { + await usingApi(async api => { + const [contract, deployer] = await deployFlipper(api); + + const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true); + const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx); + const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + + expect(getGenericResult(enableEvents).success).to.be.true; + expect(enabledBefore).to.be.false; + expect(enabled).to.be.true; + }); + }); + + it(`Only whitelisted account can call contract`, async () => { + await usingApi(async api => { + const bob = privateKey("//Bob"); + + const [contract, deployer] = await deployFlipper(api); + + let expectedFlipValue = await getFlipValue(contract, deployer); + + const flip = contract.exec('flip', value, gasLimit); + await submitTransactionAsync(bob, flip); + expectedFlipValue = !expectedFlipValue; + const afterFlip = await getFlipValue(contract,deployer); + expect(afterFlip).to.be.eq(expectedFlipValue, `Anyone can call new contract.`); + + const deployerCanFlip = async () => { + expectedFlipValue = !expectedFlipValue; + const deployerFlip = contract.exec('flip', value, gasLimit); + await submitTransactionAsync(deployer, deployerFlip); + const aliceFlip1Response = await getFlipValue(contract, deployer); + expect(aliceFlip1Response).to.be.eq(expectedFlipValue, `Deployer always can flip.`); + }; + await deployerCanFlip(); + + const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true); + const enableResult = await submitTransactionAsync(deployer, enableWhiteListTx); + const flipWithEnabledWhiteList = contract.exec('flip', value, gasLimit); + await expect(submitTransactionExpectFailAsync(bob, flipWithEnabledWhiteList)).to.be.rejected; + const flipValueAfterEnableWhiteList = await getFlipValue(contract, deployer); + expect(flipValueAfterEnableWhiteList).to.be.eq(expectedFlipValue, `Enabling whitelist doesn't make it possible to call contract for everyone.`); + + await deployerCanFlip(); + + const addBobToWhiteListTx = api.tx.nft.addToContractWhiteList(contract.address, bob.address); + const addBobResult = await submitTransactionAsync(deployer, addBobToWhiteListTx); + const flipWithWhitelistedBob = contract.exec('flip', value, gasLimit); + await submitTransactionAsync(bob, flipWithWhitelistedBob); + expectedFlipValue = !expectedFlipValue; + const flipAfterWhiteListed = await getFlipValue(contract,deployer); + expect(flipAfterWhiteListed).to.be.eq(expectedFlipValue, `Bob was whitelisted, now he can flip.`); + + await deployerCanFlip(); + + const removeBobFromWhiteListTx = api.tx.nft.removeFromContractWhiteList(contract.address, bob.address); + const removeBobResult = await submitTransactionAsync(deployer, removeBobFromWhiteListTx); + const bobRemoved = contract.exec('flip', value, gasLimit); + await expect(submitTransactionExpectFailAsync(bob, bobRemoved)).to.be.rejected; + const afterBobRemoved = await getFlipValue(contract, deployer); + expect(afterBobRemoved).to.be.eq(expectedFlipValue, `Bob can't call contract, now when he is removeed from white list.`); + + await deployerCanFlip(); + + const disableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, false); + const disableWhiteListResult = await submitTransactionAsync(deployer, disableWhiteListTx); + const whiteListDisabledFlip = contract.exec('flip', value, gasLimit); + await submitTransactionAsync(bob, whiteListDisabledFlip); + expectedFlipValue = !expectedFlipValue; + const afterWhiteListDisabled = await getFlipValue(contract,deployer); + expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`); + + }); + }); + + it(`Enabling white list repeatedly should not produce errors`, async () => { + await usingApi(async api => { + const [contract, deployer] = await deployFlipper(api); + + const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true); + const enableEvents = await submitTransactionAsync(deployer, enableWhiteListTx); + const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + const enableAgainEvents = await submitTransactionAsync(deployer, enableWhiteListTx); + const enabledAgain = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + + expect(getGenericResult(enableEvents).success).to.be.true; + expect(enabledBefore).to.be.false; + expect(enabled).to.be.true; + expect(getGenericResult(enableAgainEvents).success).to.be.true; + expect(enabledAgain).to.be.true; + }); + }); + +}); + +describe('Negative Integration Test toggleContractWhiteList', () => { + + it(`Enable white list for a non-contract`, async () => { + await usingApi(async api => { + const alice = privateKey("//Alice"); + const bobGuineaPig = privateKey("//Bob"); + + const enabledBefore = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON(); + const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(bobGuineaPig.address, true); + await expect(submitTransactionExpectFailAsync(alice, enableWhiteListTx)).to.be.rejected; + const enabled = (await api.query.nft.contractWhiteListEnabled(bobGuineaPig.address)).toJSON(); + + expect(enabledBefore).to.be.false; + expect(enabled).to.be.false; + }); + }); + + it(`Enable white list using a non-owner address`, async () => { + await usingApi(async api => { + const bob = privateKey("//Bob"); + const [contract, deployer] = await deployFlipper(api); + + const enabledBefore = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + const enableWhiteListTx = api.tx.nft.toggleContractWhiteList(contract.address, true); + await expect(submitTransactionExpectFailAsync(bob, enableWhiteListTx)).to.be.rejected; + const enabled = (await api.query.nft.contractWhiteListEnabled(contract.address)).toJSON(); + + expect(enabledBefore).to.be.false; + expect(enabled).to.be.false; + }); + }); + +}); --- /dev/null +++ b/tests/src/util/contracthelpers.ts @@ -0,0 +1,94 @@ +// +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. +// + +import chai from "chai"; +import chaiAsPromised from 'chai-as-promised'; +import { submitTransactionAsync } from "../substrate/substrate-api"; +import fs from "fs"; +import { Abi, BlueprintPromise as Blueprint, CodePromise, ContractPromise as Contract } from "@polkadot/api-contract"; +import { IKeyringPair } from "@polkadot/types/types"; +import { ApiPromise, Keyring } from "@polkadot/api"; + +chai.use(chaiAsPromised); +const expect = chai.expect; +import { BigNumber } from 'bignumber.js'; +import { findUnusedAddress } from '../util/helpers'; + +const value = 0; +const gasLimit = 3000n * 1000000n; +const endowment = `1000000000000000`; + +function deployBlueprint(alice: IKeyringPair, code: CodePromise): Promise { + return new Promise(async (resolve, reject) => { + const unsub = await code + .createBlueprint() + .signAndSend(alice, (result) => { + if (result.status.isInBlock || result.status.isFinalized) { + // here we have an additional field in the result, containing the blueprint + resolve(result.blueprint); + unsub(); + } + }) + }); +} + +function deployContract(alice: IKeyringPair, blueprint: Blueprint) : Promise { + return new Promise(async (resolve, reject) => { + const endowment = 1000000000000000n; + const initValue = true; + + const unsub = await blueprint.tx + .new(endowment, gasLimit, initValue) + .signAndSend(alice, (result) => { + if (result.status.isInBlock || result.status.isFinalized) { + unsub(); + resolve(result); + } + }); + }); +} + +async function prepareDeployer(api: ApiPromise) { + // Find unused address + const deployer = await findUnusedAddress(api); + + // Transfer balance to it + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri(`//Alice`); + let amount = new BigNumber(endowment); + amount = amount.plus(1e15); + const tx = api.tx.balances.transfer(deployer.address, amount.toFixed()); + await submitTransactionAsync(alice, tx); + + return deployer; +} + +export async function deployFlipper(api: ApiPromise): Promise<[Contract, IKeyringPair]> { + const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8')); + const abi = new Abi(metadata); + + const deployer = await prepareDeployer(api); + + const wasm = fs.readFileSync('./src/flipper/flipper.wasm'); + + const code = new CodePromise(api, abi, wasm); + + const blueprint = await deployBlueprint(deployer, code); + const contract = (await deployContract(deployer, blueprint))['contract'] as Contract; + + const initialGetResponse = await getFlipValue(contract, deployer); + expect(initialGetResponse).to.be.true; + + return [contract, deployer]; +} + +export async function getFlipValue(contract: Contract, deployer: IKeyringPair) { + const result = await contract.query.get(deployer.address, value, gasLimit); + + if(!result.result.isSuccess) { + throw `Failed to get flipper value`; + } + return (result.result.asSuccess.data[0] == 0x00) ? false : true; +}