From 1b35173f0ded2a0193733a5f5c91ab0b7e7898a3 Mon Sep 17 00:00:00 2001 From: lukozill Date: Mon, 15 Apr 2024 15:44:09 +0200 Subject: [PATCH] tests for burnable zrc2 added, tests refactored --- tests/globalConfig.ts | 22 + tests/testutils.ts | 126 ++ tests/zrc2/fungible-token-burnable/config.ts | 28 + .../fungibleBurnableToken.test.ts | 201 +++ tests/zrc6/config.ts | 14 +- tests/zrc6/testutils.ts | 30 - tests/zrc6/zrc6.approval.test.ts | 1243 ++++++++--------- tests/zrc6/zrc6.mint.test.ts | 1001 ++++++------- tests/zrc6/zrc6.token.test.ts | 199 +-- 9 files changed, 1494 insertions(+), 1370 deletions(-) create mode 100644 tests/globalConfig.ts create mode 100644 tests/testutils.ts create mode 100644 tests/zrc2/fungible-token-burnable/config.ts create mode 100644 tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts delete mode 100644 tests/zrc6/testutils.ts diff --git a/tests/globalConfig.ts b/tests/globalConfig.ts new file mode 100644 index 000000000..1ef2fd58b --- /dev/null +++ b/tests/globalConfig.ts @@ -0,0 +1,22 @@ +import { BN, Zilliqa, bytes, units } from "@zilliqa-js/zilliqa"; +import Long from "long"; + +export const API = `http://localhost:${process.env["PORT"]}`; // Zilliqa Isolated Server +export const CHAIN_ID = 222; +export const MSG_VERSION = 1; +export const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); + +export const JEST_WORKER_ID = Number(process.env["JEST_WORKER_ID"]); +export const GENESIS_PRIVATE_KEY = global.GENESIS_PRIVATE_KEYS[JEST_WORKER_ID - 1]; + +export const zilliqa = new Zilliqa(API); +zilliqa.wallet.addByPrivateKey(GENESIS_PRIVATE_KEY); + +export const GAS_PRICE = units.toQa("2000", units.Units.Li); + +export const FAUCET_PARAMS = { + version: VERSION, + amount: new BN(units.toQa("100000000", units.Units.Zil)), + gasPrice: GAS_PRICE, + gasLimit: Long.fromNumber(50), +}; diff --git a/tests/testutils.ts b/tests/testutils.ts new file mode 100644 index 000000000..578abe582 --- /dev/null +++ b/tests/testutils.ts @@ -0,0 +1,126 @@ +import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; +import { getAddressFromPrivateKey, schnorr } from "@zilliqa-js/zilliqa"; +import { FAUCET_PARAMS, zilliqa } from "./globalConfig"; + +export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; + +export async function getAccounts(numberOfAccounts: number) { + const accounts = Array.from({ length: numberOfAccounts }, schnorr.generatePrivateKey).map( + (privateKey) => ({ + privateKey, + address: getAddressFromPrivateKey(privateKey), + }) + ); + + for (const { privateKey, address } of accounts) { + zilliqa.wallet.addByPrivateKey(privateKey); + const tx = await zilliqa.blockchain.createTransaction( + zilliqa.transactions.new( + { + ...FAUCET_PARAMS, + toAddr: address, + }, + false + ) + ); + if (!tx.getReceipt()?.success) { + throw new Error(); + } + } + + return accounts; +} + +export type ContractTestCaseDefinition = { + name: string, + transition: string, + getSender: () => string, + getParams: () => Record>, + error: number | undefined, + want: { + expectState: (state: any) => void, + events: Array<{ + name: string, + getParams: () => Record>, + }>, + transitions: Array<{ + tag: string, + getParams: () => Record>, + }>, + } | undefined, +} + +export const expectEvents = (events, want) => { + if (events === undefined) { + expect(undefined).toBe(want); + } + + for (const [index, event] of events.entries()) { + expect(event._eventname).toBe(want[index].name); + const wantParams = scillaJSONParams(want[index].getParams()); + expect(JSON.stringify(event.params)).toBe(JSON.stringify(wantParams)); + } +}; + +export const expectTransitions = ( + receiptTransitions: Array<{ msg: { params: any } }> | undefined, + expectedTransitions: Array<{ + tag: string, + getParams: () => Record>, + }> +) => { + if (!receiptTransitions && expectedTransitions.length > 0) { + fail("Expected transitions but got none"); + return; + } + + expect(receiptTransitions!.length).toBe(expectedTransitions.length); + + for (const [index, transition] of receiptTransitions!.entries()) { + const { msg } = transition; + expect(expectedTransitions[index]!.tag).toBe(expectedTransitions[index]!.tag); + const wantParams = scillaJSONParams(expectedTransitions[index]!.getParams()); + expect(JSON.stringify(msg.params)).toBe(JSON.stringify(wantParams)); + } +}; + +export const getErrorMsg = (code) => + `Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 ${code}))])`; + +export function runAllTestCases( + testCases: Array, + testedContractAddress: () => string, + txParams: any +) { + for (const testCase of testCases) { + it(`${testCase.transition}: ${testCase.name}`, async () => { + zilliqa.wallet.setDefault(testCase.getSender()); + const tx: any = await zilliqa.contracts + .at(testedContractAddress()) + .call( + testCase.transition, + scillaJSONParams(testCase.getParams()), + txParams + ); + + if (testCase.want === undefined) { + // Negative Cases + expect(tx.receipt.success).toBe(false); + expect(tx.receipt.exceptions[0].message).toBe( + getErrorMsg(testCase.error) + ); + } else { + // Positive Cases + expect(tx.receipt.success).toBe(true); + expectEvents(tx.receipt.event_logs, testCase.want.events); + expectTransitions(tx.receipt.transitions, testCase.want.transitions); + + const state = await zilliqa.contracts + .at(testedContractAddress()) + .getState(); + + testCase.want.expectState(state); + } + }); + } +} \ No newline at end of file diff --git a/tests/zrc2/fungible-token-burnable/config.ts b/tests/zrc2/fungible-token-burnable/config.ts new file mode 100644 index 000000000..f6a345eb1 --- /dev/null +++ b/tests/zrc2/fungible-token-burnable/config.ts @@ -0,0 +1,28 @@ +import fs from "fs"; +import { Long, BN } from "@zilliqa-js/util"; +import { GAS_PRICE, VERSION } from "../../globalConfig"; + + +const CODE_PATH = "reference-contracts/FungibleToken-Burnable.scilla"; +export const CODE = fs.readFileSync(CODE_PATH).toString(); + +export const FungibleBurnableToken_ERROR = { + CodeIsSender: -1, + CodeInsufficientFunds: -2, + CodeInsufficientAllowance: -3, + CodeNotOwner: -4 +}; + +export const TOKEN_NAME = "TEST"; +export const TOKEN_SYMBOL = "T"; +export const TOKEN_DECIMALS = 6; +export const TOKEN_INIT_SUPPLY = 1000000000; + +export const GAS_LIMIT = Long.fromNumber(100000); + +export const TX_PARAMS = { + version: VERSION, + amount: new BN(0), + gasPrice: GAS_PRICE, + gasLimit: GAS_LIMIT, +}; diff --git a/tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts b/tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts new file mode 100644 index 000000000..3f31a3202 --- /dev/null +++ b/tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts @@ -0,0 +1,201 @@ +import { expect } from "@jest/globals"; +import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; + +import { + getAccounts, + runAllTestCases, +} from "../../testutils"; + +import { + TX_PARAMS, + CODE, + FungibleBurnableToken_ERROR, + TOKEN_NAME, + TOKEN_SYMBOL, + TOKEN_DECIMALS, + TOKEN_INIT_SUPPLY, +} from "./config"; +import { GENESIS_PRIVATE_KEY, JEST_WORKER_ID, zilliqa } from "../../globalConfig"; + +let globalContractAddress: string | undefined; + +let globalTestAccounts: Array<{ + privateKey: string; + address: string; +}> = []; +const CONTRACT_OWNER = 0; +const TOKEN_OWNER = 0; +const STRANGER_1 = 1; +const STRANGER_2 = 2; +const STRANGER_1_INITIAL_TOKENS = 1000; +const getTestAddr = (index) => globalTestAccounts[index]?.address as string; + +beforeAll(async () => { + globalTestAccounts = await getAccounts(4); + + console.table({ + JEST_WORKER_ID, + GENESIS_PRIVATE_KEY, + CONTRACT_OWNER: getTestAddr(CONTRACT_OWNER), + TOKEN_OWNER: getTestAddr(TOKEN_OWNER), + STRANGER_1: getTestAddr(STRANGER_1), + STRANGER_2: getTestAddr(STRANGER_2), + }); +}); + +beforeEach(async () => { + zilliqa.wallet.setDefault(getTestAddr(CONTRACT_OWNER)); + const init = scillaJSONParams({ + _scilla_version: ["Uint32", 0], + contract_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + name: ["String", TOKEN_NAME], + symbol: ["String", TOKEN_SYMBOL], + decimals: ["Uint32", TOKEN_DECIMALS], + init_supply: ["Uint128", TOKEN_INIT_SUPPLY], + }); + const [contractDeploymentTransaction, contract] = await zilliqa.contracts + .new(CODE, init) + .deploy(TX_PARAMS, 33, 1000, true); + globalContractAddress = contract.address; + + if (globalContractAddress === undefined) { + throw new Error(JSON.stringify({ + message: "Failed to deploy FungibleToken-Burnable contract", + receipt: contractDeploymentTransaction.getReceipt() + })); + } + + let stranger1InitialTokenTransferTx: any = await zilliqa.contracts.at(globalContractAddress).call( + "Transfer", + scillaJSONParams({ + to: ["ByStr20", getTestAddr(STRANGER_1)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + TX_PARAMS + ); + + if (!stranger1InitialTokenTransferTx.receipt.success) { + throw new Error("Initial transfer fund to STRANGER_1 failed"); + } +}); + +describe("Burn", () => { + runAllTestCases( + [ + { + name: "throws CodeInsufficientFunds if not enough funds", + transition: "Burn", + getSender: () => getTestAddr(STRANGER_1), + getParams: () => ({ + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS * 2], + }), + error: FungibleBurnableToken_ERROR.CodeInsufficientFunds, + want: undefined, + }, + { + name: "successfuly burns tokens", + transition: "Burn", + getSender: () => getTestAddr(STRANGER_1), + getParams: () => ({ + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.total_supply + ).toEqual(`${TOKEN_INIT_SUPPLY - STRANGER_1_INITIAL_TOKENS}`); + }, + events: [ + { + name: "Burnt", + getParams: () => ({ + burner: ["ByStr20", getTestAddr(STRANGER_1)], + burn_account: ["ByStr20", getTestAddr(STRANGER_1)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + }, + ], + transitions: [ + { + tag: "BurnSuccessCallBack", + getParams: () => ({ + burner: ["ByStr20", getTestAddr(STRANGER_1)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + }, + ], + }, + }, + ], + () => globalContractAddress!, + TX_PARAMS + ) +}); + +describe("Transfer", () => { + runAllTestCases( + [ + { + name: "throws CodeInsufficientFunds if not enough funds", + transition: "Transfer", + getSender: () => getTestAddr(STRANGER_1), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_2)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS * 2], + }), + error: FungibleBurnableToken_ERROR.CodeInsufficientFunds, + want: undefined, + }, + { + name: "successfuly transfer tokens", + transition: "Transfer", + getSender: () => getTestAddr(STRANGER_1), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_2)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.total_supply + ).toEqual(`${TOKEN_INIT_SUPPLY}`); + + console.log("balanceeees", state.balances); + }, + events: [ + { + name: "TransferSuccess", + getParams: () => ({ + sender: ["ByStr20", getTestAddr(STRANGER_1)], + recipient: ["ByStr20", getTestAddr(STRANGER_2)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + }, + ], + transitions: [ + { + tag: "RecipientAcceptTransfer", + getParams: () => ({ + sender: ["ByStr20", getTestAddr(STRANGER_1)], + recipient: ["ByStr20", getTestAddr(STRANGER_2)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + }, + { + tag: "TransferSuccessCallBack", + getParams: () => ({ + sender: ["ByStr20", getTestAddr(STRANGER_1)], + recipient: ["ByStr20", getTestAddr(STRANGER_2)], + amount: ["Uint128", STRANGER_1_INITIAL_TOKENS], + }), + }, + ], + }, + }, + ], + () => globalContractAddress!, + TX_PARAMS + ) +}); diff --git a/tests/zrc6/config.ts b/tests/zrc6/config.ts index 67fbf123c..1433b702a 100644 --- a/tests/zrc6/config.ts +++ b/tests/zrc6/config.ts @@ -1,11 +1,7 @@ -import { bytes, units } from "@zilliqa-js/util"; import fs from "fs"; import { Long, BN } from "@zilliqa-js/util"; +import { GAS_PRICE, VERSION } from "../globalConfig"; -export const API = `http://localhost:${process.env["PORT"]}`; // Zilliqa Isolated Server -export const CHAIN_ID = 222; -export const MSG_VERSION = 1; -export const VERSION = bytes.pack(CHAIN_ID, MSG_VERSION); const CODE_PATH = "reference-contracts/zrc6.scilla"; export const CODE = fs.readFileSync(CODE_PATH).toString(); @@ -36,7 +32,6 @@ export const TOKEN_SYMBOL = "T"; export const BASE_URI = "https://creatures-api.zilliqa.com/api/creature/"; export const GAS_LIMIT = Long.fromNumber(100000); -export const GAS_PRICE = units.toQa("2000", units.Units.Li); export const TX_PARAMS = { version: VERSION, @@ -44,10 +39,3 @@ export const TX_PARAMS = { gasPrice: GAS_PRICE, gasLimit: GAS_LIMIT, }; - -export const FAUCET_PARAMS = { - version: VERSION, - amount: new BN(units.toQa("100000000", units.Units.Zil)), - gasPrice: GAS_PRICE, - gasLimit: Long.fromNumber(50), -}; diff --git a/tests/zrc6/testutils.ts b/tests/zrc6/testutils.ts deleted file mode 100644 index 7d8bfaeeb..000000000 --- a/tests/zrc6/testutils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; - -export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; - -export const expectEvents = (events, want) => { - if (events === undefined) { - expect(undefined).toBe(want); - } - - for (const [index, event] of events.entries()) { - expect(event._eventname).toBe(want[index].name); - const wantParams = scillaJSONParams(want[index].getParams()); - expect(JSON.stringify(event.params)).toBe(JSON.stringify(wantParams)); - } -}; - -export const expectTransitions = (transitions, want) => { - if (transitions === undefined) { - expect(undefined).toBe(want); - } - for (const [index, transition] of transitions.entries()) { - const { msg } = transition; - expect(want[index].tag).toBe(want[index].tag); - const wantParams = scillaJSONParams(want[index].getParams()); - expect(JSON.stringify(msg.params)).toBe(JSON.stringify(wantParams)); - } -}; - -export const getErrorMsg = (code) => - `Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 ${code}))])`; diff --git a/tests/zrc6/zrc6.approval.test.ts b/tests/zrc6/zrc6.approval.test.ts index b967078f7..913fec218 100644 --- a/tests/zrc6/zrc6.approval.test.ts +++ b/tests/zrc6/zrc6.approval.test.ts @@ -1,33 +1,23 @@ -import { Zilliqa } from "@zilliqa-js/zilliqa"; + import { expect } from "@jest/globals"; -import { getAddressFromPrivateKey, schnorr } from "@zilliqa-js/crypto"; import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; - +import { GENESIS_PRIVATE_KEY, JEST_WORKER_ID, zilliqa } from "../globalConfig"; import { - getErrorMsg, - expectTransitions, - expectEvents, ZERO_ADDRESS, -} from "./testutils"; - + getAccounts, + runAllTestCases, +} from "./../testutils"; import { - API, TX_PARAMS, CODE, ZRC6_ERROR, TOKEN_NAME, TOKEN_SYMBOL, - FAUCET_PARAMS, BASE_URI, } from "./config"; -const JEST_WORKER_ID = Number(process.env["JEST_WORKER_ID"]); -const GENESIS_PRIVATE_KEY = global.GENESIS_PRIVATE_KEYS[JEST_WORKER_ID - 1]; - -const zilliqa = new Zilliqa(API); -zilliqa.wallet.addByPrivateKey(GENESIS_PRIVATE_KEY); -let globalContractAddress; +let globalContractAddress: string | undefined; let globalTestAccounts: Array<{ privateKey: string; @@ -43,28 +33,7 @@ const STRANGER_B = 5; const getTestAddr = (index) => globalTestAccounts[index]?.address as string; beforeAll(async () => { - const accounts = Array.from({ length: 6 }, schnorr.generatePrivateKey).map( - (privateKey) => ({ - privateKey, - address: getAddressFromPrivateKey(privateKey), - }) - ); - for (const { privateKey, address } of accounts) { - zilliqa.wallet.addByPrivateKey(privateKey); - const tx = await zilliqa.blockchain.createTransaction( - zilliqa.transactions.new( - { - ...FAUCET_PARAMS, - toAddr: address, - }, - false - ) - ); - if (!tx.getReceipt()?.success) { - throw new Error(); - } - } - globalTestAccounts = accounts; + globalTestAccounts = await getAccounts(6); console.table({ JEST_WORKER_ID, @@ -143,641 +112,613 @@ beforeEach(async () => { }); describe("Approval", () => { - const testCases = [ - { - name: "throws TokenNotFoundError", - transition: "SetSpender", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - spender: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 999], // Non-existing token - }), - error: ZRC6_ERROR.TokenNotFoundError, - want: undefined, - }, - { - name: "throws NotOwnerOrOperatorError", - transition: "SetSpender", - getSender: () => getTestAddr(STRANGER_A), // Not a token owner - getParams: () => ({ - spender: ["ByStr20", getTestAddr(STRANGER_B)], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.NotOwnerOrOperatorError, - want: undefined, - }, - { - name: "throws SelfError", - transition: "SetSpender", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - spender: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.SelfError, - want: undefined, - }, - { - name: "throws SpenderFoundError", - transition: "SetSpender", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - spender: ["ByStr20", getTestAddr(SPENDER)], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.SpenderFoundError, - want: undefined, - }, - { - name: "sets stranger as spender for token #1", - transition: "SetSpender", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - spender: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.spenders["1"]).toBe( - getTestAddr(STRANGER_A).toLowerCase() - ); - }, - events: [ - { - name: "SetSpender", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - spender: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_SetSpenderCallback", - getParams: () => ({ - spender: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], + runAllTestCases( + [ + { + name: "throws TokenNotFoundError", + transition: "SetSpender", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + spender: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 999], // Non-existing token + }), + error: ZRC6_ERROR.TokenNotFoundError, + want: undefined, }, - }, - { - name: "sets zero address as spender for token #1", - transition: "SetSpender", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - spender: ["ByStr20", ZERO_ADDRESS], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.spenders["1"]).toBe(ZERO_ADDRESS); - }, - events: [ - { - name: "SetSpender", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - spender: ["ByStr20", ZERO_ADDRESS], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_SetSpenderCallback", - getParams: () => ({ - spender: ["ByStr20", ZERO_ADDRESS], - token_id: ["Uint256", 1], - }), - }, - ], + { + name: "throws NotOwnerOrOperatorError", + transition: "SetSpender", + getSender: () => getTestAddr(STRANGER_A), // Not a token owner + getParams: () => ({ + spender: ["ByStr20", getTestAddr(STRANGER_B)], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.NotOwnerOrOperatorError, + want: undefined, }, - }, - { - name: "throws NotTokenOwnerError by stranger)", - transition: "AddOperator", - getSender: () => getTestAddr(STRANGER_B), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - error: ZRC6_ERROR.NotTokenOwnerError, - want: undefined, - }, - { - name: "throws SelfError", - transition: "AddOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], // Self - }), - error: ZRC6_ERROR.SelfError, - want: undefined, - }, - { - name: "throws OperatorFoundError for operator by token owner A", - transition: "AddOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(OPERATOR)], - }), - error: ZRC6_ERROR.OperatorFoundError, - want: undefined, - }, - { - name: "adds stranger A as operator by token owner A", - transition: "AddOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - Object.keys( - state.operators[getTestAddr(TOKEN_OWNER_A).toLowerCase()] - ).includes(getTestAddr(STRANGER_A).toLowerCase()) - ).toBe(true); - }, - events: [ - { - name: "AddOperator", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_AddOperatorCallback", - getParams: () => ({ - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - }, - ], + { + name: "throws SelfError", + transition: "SetSpender", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + spender: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.SelfError, + want: undefined, }, - }, - { - name: "throws OperatorNotFoundError", - transition: "RemoveOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - error: ZRC6_ERROR.OperatorNotFoundError, - want: undefined, - }, - { - name: "throws OperatorNotFoundError for stranger by token owner A", - transition: "RemoveOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(STRANGER_A)], - }), - error: ZRC6_ERROR.OperatorNotFoundError, - want: undefined, - }, - { - name: "removes operator by token owner A", - transition: "RemoveOperator", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - operator: ["ByStr20", getTestAddr(OPERATOR)], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - Object.keys( - state.operators[getTestAddr(TOKEN_OWNER_A).toLowerCase()] - ).length - ).toBe(0); - }, - events: [ - { - name: "RemoveOperator", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - operator: ["ByStr20", getTestAddr(OPERATOR)], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RemoveOperatorCallback", - getParams: () => ({ - operator: ["ByStr20", getTestAddr(OPERATOR)], - }), - }, - ], + { + name: "throws SpenderFoundError", + transition: "SetSpender", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + spender: ["ByStr20", getTestAddr(SPENDER)], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.SpenderFoundError, + want: undefined, }, - }, - { - name: "throws SelfError by self giving", - transition: "TransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - to: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], // Self - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.SelfError, - want: undefined, - }, - { - name: "throws NotAllowedToTransferError by stranger", - transition: "TransferFrom", - getSender: () => getTestAddr(STRANGER_A), // Not Owner - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.NotAllowedToTransferError, - want: undefined, - }, - { - name: "throws ZeroAddressDestinationError", - transition: "TransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - to: ["ByStr20", ZERO_ADDRESS], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.ZeroAddressDestinationError, - want: undefined, - }, - { - name: "throws ThisAddressDestinationError", - transition: "TransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - to: ["ByStr20", globalContractAddress], - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.ThisAddressDestinationError, - want: undefined, - }, - { - name: "token owner -> stranger by token owner", - transition: "TransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_A), - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( - "1" - ); - expect(state.token_owners["1"]).toBe( - getTestAddr(STRANGER_A).toLowerCase() - ); - }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptTransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - { - tag: "ZRC6_TransferFromCallback", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), + { + name: "sets stranger as spender for token #1", + transition: "SetSpender", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + spender: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.spenders["1"]).toBe( + getTestAddr(STRANGER_A).toLowerCase() + ); }, - ], - }, - }, - { - name: "token owner -> stranger by spender", - transition: "TransferFrom", - getSender: () => getTestAddr(SPENDER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( - "1" - ); - expect(state.token_owners["1"]).toBe( - getTestAddr(STRANGER_A).toLowerCase() - ); + events: [ + { + name: "SetSpender", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + spender: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_SetSpenderCallback", + getParams: () => ({ + spender: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptTransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - { - tag: "ZRC6_TransferFromCallback", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], }, - }, - { - name: "token owner -> stranger by operator", - transition: "TransferFrom", - getSender: () => getTestAddr(OPERATOR), - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( - "1" - ); - expect(state.token_owners["1"]).toBe( - getTestAddr(STRANGER_A).toLowerCase() - ); - }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptTransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), + { + name: "sets zero address as spender for token #1", + transition: "SetSpender", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + spender: ["ByStr20", ZERO_ADDRESS], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.spenders["1"]).toBe(ZERO_ADDRESS); }, - { - tag: "ZRC6_TransferFromCallback", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 1], - }), - }, - ], - }, - }, - { - name: "token owner -> spender by spender", - transition: "TransferFrom", - getSender: () => getTestAddr(SPENDER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(SPENDER)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.balances[getTestAddr(SPENDER).toLowerCase()]).toBe("1"); - expect(state.token_owners["1"]).toBe( - getTestAddr(SPENDER).toLowerCase() - ); + events: [ + { + name: "SetSpender", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + spender: ["ByStr20", ZERO_ADDRESS], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_SetSpenderCallback", + getParams: () => ({ + spender: ["ByStr20", ZERO_ADDRESS], + token_id: ["Uint256", 1], + }), + }, + ], }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(SPENDER)], - token_id: ["Uint256", 1], - }), + }, + { + name: "throws NotTokenOwnerError by stranger)", + transition: "AddOperator", + getSender: () => getTestAddr(STRANGER_B), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + error: ZRC6_ERROR.NotTokenOwnerError, + want: undefined, + }, + { + name: "throws SelfError", + transition: "AddOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], // Self + }), + error: ZRC6_ERROR.SelfError, + want: undefined, + }, + { + name: "throws OperatorFoundError for operator by token owner A", + transition: "AddOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(OPERATOR)], + }), + error: ZRC6_ERROR.OperatorFoundError, + want: undefined, + }, + { + name: "adds stranger A as operator by token owner A", + transition: "AddOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + Object.keys( + state.operators[getTestAddr(TOKEN_OWNER_A).toLowerCase()] + ).includes(getTestAddr(STRANGER_A).toLowerCase()) + ).toBe(true); }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptTransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(SPENDER)], - token_id: ["Uint256", 1], - }), + events: [ + { + name: "AddOperator", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_AddOperatorCallback", + getParams: () => ({ + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + }, + ], + }, + }, + { + name: "throws OperatorNotFoundError", + transition: "RemoveOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + error: ZRC6_ERROR.OperatorNotFoundError, + want: undefined, + }, + { + name: "throws OperatorNotFoundError for stranger by token owner A", + transition: "RemoveOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(STRANGER_A)], + }), + error: ZRC6_ERROR.OperatorNotFoundError, + want: undefined, + }, + { + name: "removes operator by token owner A", + transition: "RemoveOperator", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + operator: ["ByStr20", getTestAddr(OPERATOR)], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + Object.keys( + state.operators[getTestAddr(TOKEN_OWNER_A).toLowerCase()] + ).length + ).toBe(0); }, - { - tag: "ZRC6_TransferFromCallback", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(SPENDER)], - token_id: ["Uint256", 1], - }), + events: [ + { + name: "RemoveOperator", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + operator: ["ByStr20", getTestAddr(OPERATOR)], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RemoveOperatorCallback", + getParams: () => ({ + operator: ["ByStr20", getTestAddr(OPERATOR)], + }), + }, + ], + }, + }, + { + name: "throws SelfError by self giving", + transition: "TransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + to: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], // Self + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.SelfError, + want: undefined, + }, + { + name: "throws NotAllowedToTransferError by stranger", + transition: "TransferFrom", + getSender: () => getTestAddr(STRANGER_A), // Not Owner + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.NotAllowedToTransferError, + want: undefined, + }, + { + name: "throws ZeroAddressDestinationError", + transition: "TransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + to: ["ByStr20", ZERO_ADDRESS], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.ZeroAddressDestinationError, + want: undefined, + }, + { + name: "throws ThisAddressDestinationError", + transition: "TransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + to: ["ByStr20", globalContractAddress], + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.ThisAddressDestinationError, + want: undefined, + }, + { + name: "token owner -> stranger by token owner", + transition: "TransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_A), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( + "1" + ); + expect(state.token_owners["1"]).toBe( + getTestAddr(STRANGER_A).toLowerCase() + ); }, - ], + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptTransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + { + tag: "ZRC6_TransferFromCallback", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + }, }, - }, - { - name: "token owner -> operator by operator", - transition: "TransferFrom", - getSender: () => getTestAddr(OPERATOR), - getParams: () => ({ - to: ["ByStr20", getTestAddr(OPERATOR)], - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.balances[getTestAddr(OPERATOR).toLowerCase()]).toBe("1"); - expect(state.token_owners["1"]).toBe( - getTestAddr(OPERATOR).toLowerCase() - ); + { + name: "token owner -> stranger by spender", + transition: "TransferFrom", + getSender: () => getTestAddr(SPENDER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( + "1" + ); + expect(state.token_owners["1"]).toBe( + getTestAddr(STRANGER_A).toLowerCase() + ); + }, + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptTransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + { + tag: "ZRC6_TransferFromCallback", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(OPERATOR)], - token_id: ["Uint256", 1], - }), + }, + { + name: "token owner -> stranger by operator", + transition: "TransferFrom", + getSender: () => getTestAddr(OPERATOR), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.balances[getTestAddr(STRANGER_A).toLowerCase()]).toBe( + "1" + ); + expect(state.token_owners["1"]).toBe( + getTestAddr(STRANGER_A).toLowerCase() + ); }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptTransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(OPERATOR)], - token_id: ["Uint256", 1], - }), + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptTransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + { + tag: "ZRC6_TransferFromCallback", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 1], + }), + }, + ], + }, + }, + { + name: "token owner -> spender by spender", + transition: "TransferFrom", + getSender: () => getTestAddr(SPENDER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(SPENDER)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.balances[getTestAddr(SPENDER).toLowerCase()]).toBe("1"); + expect(state.token_owners["1"]).toBe( + getTestAddr(SPENDER).toLowerCase() + ); }, - { - tag: "ZRC6_TransferFromCallback", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - to: ["ByStr20", getTestAddr(OPERATOR)], - token_id: ["Uint256", 1], - }), + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(SPENDER)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptTransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(SPENDER)], + token_id: ["Uint256", 1], + }), + }, + { + tag: "ZRC6_TransferFromCallback", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(SPENDER)], + token_id: ["Uint256", 1], + }), + }, + ], + }, + }, + { + name: "token owner -> operator by operator", + transition: "TransferFrom", + getSender: () => getTestAddr(OPERATOR), + getParams: () => ({ + to: ["ByStr20", getTestAddr(OPERATOR)], + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.balances[getTestAddr(OPERATOR).toLowerCase()]).toBe("1"); + expect(state.token_owners["1"]).toBe( + getTestAddr(OPERATOR).toLowerCase() + ); }, - ], + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(OPERATOR)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptTransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(OPERATOR)], + token_id: ["Uint256", 1], + }), + }, + { + tag: "ZRC6_TransferFromCallback", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + to: ["ByStr20", getTestAddr(OPERATOR)], + token_id: ["Uint256", 1], + }), + }, + ], + }, }, - }, - { - name: "throws SelfError", - transition: "BatchTransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_B), - getParams: () => ({ - to_token_id_pair_list: [ - "List (Pair (ByStr20) (Uint256))", - [ - [getTestAddr(TOKEN_OWNER_A), 2], - [getTestAddr(TOKEN_OWNER_B), 3], - [getTestAddr(STRANGER_A), 4], + { + name: "throws SelfError", + transition: "BatchTransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_B), + getParams: () => ({ + to_token_id_pair_list: [ + "List (Pair (ByStr20) (Uint256))", + [ + [getTestAddr(TOKEN_OWNER_A), 2], + [getTestAddr(TOKEN_OWNER_B), 3], + [getTestAddr(STRANGER_A), 4], + ], ], - ], - }), - error: ZRC6_ERROR.SelfError, - want: undefined, - }, - { - name: "token owner B transfers (token owner A, #2), (stranger A, #3), (stranger A, #4)", - transition: "BatchTransferFrom", - getSender: () => getTestAddr(TOKEN_OWNER_B), - getParams: () => ({ - to_token_id_pair_list: [ - "List (Pair (ByStr20) (Uint256))", - [ - [getTestAddr(TOKEN_OWNER_A), 2], - [getTestAddr(STRANGER_A), 3], - [getTestAddr(STRANGER_A), 4], + }), + error: ZRC6_ERROR.SelfError, + want: undefined, + }, + { + name: "token owner B transfers (token owner A, #2), (stranger A, #3), (stranger A, #4)", + transition: "BatchTransferFrom", + getSender: () => getTestAddr(TOKEN_OWNER_B), + getParams: () => ({ + to_token_id_pair_list: [ + "List (Pair (ByStr20) (Uint256))", + [ + [getTestAddr(TOKEN_OWNER_A), 2], + [getTestAddr(STRANGER_A), 3], + [getTestAddr(STRANGER_A), 4], + ], ], - ], - }), - error: undefined, - want: { - expectState: (state) => { - expect(JSON.stringify(state.token_owners)).toBe( - JSON.stringify({ - "1": getTestAddr(TOKEN_OWNER_A).toLowerCase(), - "2": getTestAddr(TOKEN_OWNER_A).toLowerCase(), - "3": getTestAddr(STRANGER_A).toLowerCase(), - "4": getTestAddr(STRANGER_A).toLowerCase(), - }) - ); - - [ - [TOKEN_OWNER_A, 2], - [TOKEN_OWNER_B, 0], - [STRANGER_A, 2], - ].forEach((x) => { - const [tokenOwner, balance] = x; - expect(state.balances[getTestAddr(tokenOwner).toLowerCase()]).toBe( - balance?.toString() + }), + error: undefined, + want: { + expectState: (state) => { + expect(JSON.stringify(state.token_owners)).toBe( + JSON.stringify({ + "1": getTestAddr(TOKEN_OWNER_A).toLowerCase(), + "2": getTestAddr(TOKEN_OWNER_A).toLowerCase(), + "3": getTestAddr(STRANGER_A).toLowerCase(), + "4": getTestAddr(STRANGER_A).toLowerCase(), + }) ); - }); - }, - events: [ - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 4], - }), - }, - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], - to: ["ByStr20", getTestAddr(STRANGER_A)], - token_id: ["Uint256", 3], - }), - }, - { - name: "TransferFrom", - getParams: () => ({ - from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], - to: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], - token_id: ["Uint256", 2], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_BatchTransferFromCallback", - getParams: () => ({}), + [ + [TOKEN_OWNER_A, 2], + [TOKEN_OWNER_B, 0], + [STRANGER_A, 2], + ].forEach((x) => { + const [tokenOwner, balance] = x; + expect(state.balances[getTestAddr(tokenOwner).toLowerCase()]).toBe( + balance?.toString() + ); + }); }, - ], - }, - }, - ]; - - for (const testCase of testCases) { - it(`${testCase.transition}: ${testCase.name}`, async () => { - zilliqa.wallet.setDefault(testCase.getSender()); - const tx: any = await zilliqa.contracts - .at(globalContractAddress) - .call( - testCase.transition, - scillaJSONParams(testCase.getParams()), - TX_PARAMS - ); + events: [ + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 4], + }), + }, - if (testCase.want === undefined) { - // Negative Cases - expect(tx.receipt.success).toBe(false); - expect(tx.receipt.exceptions[0].message).toBe( - getErrorMsg(testCase.error) - ); - } else { - // Positive Cases - expect(tx.receipt.success).toBe(true); - expectEvents(tx.receipt.event_logs, testCase.want.events); - expectTransitions(tx.receipt.transitions, testCase.want.transitions); - - const state = await zilliqa.contracts - .at(globalContractAddress) - .getState(); - - testCase.want.expectState(state); - } - }); - } + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], + to: ["ByStr20", getTestAddr(STRANGER_A)], + token_id: ["Uint256", 3], + }), + }, + { + name: "TransferFrom", + getParams: () => ({ + from: ["ByStr20", getTestAddr(TOKEN_OWNER_B)], + to: ["ByStr20", getTestAddr(TOKEN_OWNER_A)], + token_id: ["Uint256", 2], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_BatchTransferFromCallback", + getParams: () => ({}), + }, + ], + }, + }, + ], + () => globalContractAddress!, + TX_PARAMS, + ) }); diff --git a/tests/zrc6/zrc6.mint.test.ts b/tests/zrc6/zrc6.mint.test.ts index 405e3adfa..ad48e03e5 100644 --- a/tests/zrc6/zrc6.mint.test.ts +++ b/tests/zrc6/zrc6.mint.test.ts @@ -1,34 +1,24 @@ -import { Zilliqa } from "@zilliqa-js/zilliqa"; import { expect } from "@jest/globals"; -import { getAddressFromPrivateKey, schnorr } from "@zilliqa-js/crypto"; import { scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; - +import { GENESIS_PRIVATE_KEY, JEST_WORKER_ID, zilliqa } from "../globalConfig"; import { - getErrorMsg, - expectTransitions, - expectEvents, ZERO_ADDRESS, -} from "./testutils"; - + getAccounts, + runAllTestCases, +} from "./../testutils"; import { - API, TX_PARAMS, CODE, ZRC6_ERROR, TOKEN_NAME, TOKEN_SYMBOL, - FAUCET_PARAMS, BASE_URI, } from "./config"; -const JEST_WORKER_ID = Number(process.env["JEST_WORKER_ID"]); -const GENESIS_PRIVATE_KEY = global.GENESIS_PRIVATE_KEYS[JEST_WORKER_ID - 1]; -const INITIAL_TOTAL_SUPPLY = 3; -const zilliqa = new Zilliqa(API); -zilliqa.wallet.addByPrivateKey(GENESIS_PRIVATE_KEY); +const INITIAL_TOTAL_SUPPLY = 3; -let globalContractAddress; +let globalContractAddress: string | undefined; let globalTestAccounts: Array<{ privateKey: string; @@ -41,28 +31,7 @@ const STRANGER = 2; const getTestAddr = (index) => globalTestAccounts[index]?.address as string; beforeAll(async () => { - const accounts = Array.from({ length: 3 }, schnorr.generatePrivateKey).map( - (privateKey) => ({ - privateKey, - address: getAddressFromPrivateKey(privateKey), - }) - ); - for (const { privateKey, address } of accounts) { - zilliqa.wallet.addByPrivateKey(privateKey); - const tx = await zilliqa.blockchain.createTransaction( - zilliqa.transactions.new( - { - ...FAUCET_PARAMS, - toAddr: address, - }, - false - ) - ); - if (!tx.getReceipt()?.success) { - throw new Error(); - } - } - globalTestAccounts = accounts; + globalTestAccounts = await getAccounts(3); console.table({ JEST_WORKER_ID, @@ -125,538 +94,482 @@ beforeEach(async () => { }); describe("Minter", () => { - const testCases = [ - { - name: "throws NotContractOwnerError by stranger", - transition: "AddMinter", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(STRANGER)], - }), - error: ZRC6_ERROR.NotContractOwnerError, - want: undefined, - }, - { - name: "throws MinterFoundError", - transition: "AddMinter", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(MINTER)], - }), - error: ZRC6_ERROR.MinterFoundError, - want: undefined, - }, - { - name: "adds minter", - transition: "AddMinter", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(STRANGER)], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - state.minters.hasOwnProperty(getTestAddr(STRANGER).toLowerCase()) - ).toBe(true); - }, - events: [ - { - name: "AddMinter", - getParams: () => ({ - minter: ["ByStr20", getTestAddr(STRANGER)], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_AddMinterCallback", - getParams: () => ({ - minter: ["ByStr20", getTestAddr(STRANGER)], - }), - }, - ], + runAllTestCases( + [ + { + name: "throws NotContractOwnerError by stranger", + transition: "AddMinter", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(STRANGER)], + }), + error: ZRC6_ERROR.NotContractOwnerError, + want: undefined, }, - }, - { - name: "throws NotContractOwnerError by stranger", - transition: "RemoveMinter", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(MINTER)], - }), - error: ZRC6_ERROR.NotContractOwnerError, - want: undefined, - }, - { - name: "throws MinterNotFoundError", - transition: "RemoveMinter", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(STRANGER)], - }), - error: ZRC6_ERROR.MinterNotFoundError, - want: undefined, - }, - { - name: "removes minter", - transition: "RemoveMinter", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({ - minter: ["ByStr20", getTestAddr(MINTER)], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - state.minters.hasOwnProperty(getTestAddr(STRANGER).toLowerCase()) - ).toBe(false); - }, - events: [ - { - name: "RemoveMinter", - getParams: () => ({ - minter: ["ByStr20", getTestAddr(MINTER)], - }), + { + name: "throws MinterFoundError", + transition: "AddMinter", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(MINTER)], + }), + error: ZRC6_ERROR.MinterFoundError, + want: undefined, + }, + { + name: "adds minter", + transition: "AddMinter", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(STRANGER)], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.minters.hasOwnProperty(getTestAddr(STRANGER).toLowerCase()) + ).toBe(true); }, - ], - transitions: [ - { - tag: "ZRC6_RemoveMinterCallback", - getParams: () => ({ - minter: ["ByStr20", getTestAddr(MINTER)], - }), + events: [ + { + name: "AddMinter", + getParams: () => ({ + minter: ["ByStr20", getTestAddr(STRANGER)], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_AddMinterCallback", + getParams: () => ({ + minter: ["ByStr20", getTestAddr(STRANGER)], + }), + }, + ], + }, + }, + { + name: "throws NotContractOwnerError by stranger", + transition: "RemoveMinter", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(MINTER)], + }), + error: ZRC6_ERROR.NotContractOwnerError, + want: undefined, + }, + { + name: "throws MinterNotFoundError", + transition: "RemoveMinter", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(STRANGER)], + }), + error: ZRC6_ERROR.MinterNotFoundError, + want: undefined, + }, + { + name: "removes minter", + transition: "RemoveMinter", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({ + minter: ["ByStr20", getTestAddr(MINTER)], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.minters.hasOwnProperty(getTestAddr(STRANGER).toLowerCase()) + ).toBe(false); }, - ], + events: [ + { + name: "RemoveMinter", + getParams: () => ({ + minter: ["ByStr20", getTestAddr(MINTER)], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RemoveMinterCallback", + getParams: () => ({ + minter: ["ByStr20", getTestAddr(MINTER)], + }), + }, + ], + }, }, - }, - ]; - - for (const testCase of testCases) { - it(`${testCase.transition}: ${testCase.name}`, async () => { - zilliqa.wallet.setDefault(testCase.getSender()); - const tx: any = await zilliqa.contracts - .at(globalContractAddress) - .call( - testCase.transition, - scillaJSONParams(testCase.getParams()), - TX_PARAMS - ); - - if (testCase.want === undefined) { - // Negative Cases - expect(tx.receipt.success).toBe(false); - expect(tx.receipt.exceptions[0].message).toBe( - getErrorMsg(testCase.error) - ); - } else { - // Positive Cases - expect(tx.receipt.success).toBe(true); - expectEvents(tx.receipt.event_logs, testCase.want.events); - expectTransitions(tx.receipt.transitions, testCase.want.transitions); - - const state = await zilliqa.contracts - .at(globalContractAddress) - .getState(); - - testCase.want.expectState(state); - } - }); - } + ], + () => globalContractAddress!, + TX_PARAMS + ); }); describe("Mint & Burn", () => { - const testCases = [ - { - name: "throws ZeroAddressDestinationError", - transition: "Mint", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - to: ["ByStr20", ZERO_ADDRESS], - token_uri: ["String", ""], - }), - error: ZRC6_ERROR.ZeroAddressDestinationError, - want: undefined, - }, - { - name: "throws ThisAddressDestinationError", - transition: "Mint", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - to: ["ByStr20", globalContractAddress], - token_uri: ["String", ""], - }), - error: ZRC6_ERROR.ThisAddressDestinationError, - want: undefined, - }, - { - name: "throws NotMinterError", - transition: "Mint", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER)], - token_uri: ["String", ""], - }), - error: ZRC6_ERROR.NotMinterError, - want: undefined, - }, - { - name: "mints token by contract owner", - transition: "Mint", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER)], - token_uri: ["String", ""], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] - ).toBe(getTestAddr(STRANGER).toLowerCase()); - expect(state.token_id_count).toBe( - (INITIAL_TOTAL_SUPPLY + 1).toString() - ); - }, - events: [ - { - name: "Mint", - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: ["String", ""], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptMint", - getParams: () => ({}), - }, - { - tag: "ZRC6_MintCallback", - getParams: () => ({ - to: ["ByStr20", getTestAddr(STRANGER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: ["String", ""], - }), - }, - ], + runAllTestCases( + [ + { + name: "throws ZeroAddressDestinationError", + transition: "Mint", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + to: ["ByStr20", ZERO_ADDRESS], + token_uri: ["String", ""], + }), + error: ZRC6_ERROR.ZeroAddressDestinationError, + want: undefined, }, - }, - { - name: "mints token by minter", - transition: "Mint", - getSender: () => getTestAddr(MINTER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_uri: ["String", ""], - }), - error: undefined, - want: { - expectState: (state) => { - expect( - state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] - ).toBe(getTestAddr(MINTER).toLowerCase()); - expect(state.token_id_count).toBe( - (INITIAL_TOTAL_SUPPLY + 1).toString() - ); - }, - events: [ - { - name: "Mint", - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: ["String", ""], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptMint", - getParams: () => ({}), + { + name: "throws ThisAddressDestinationError", + transition: "Mint", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + to: ["ByStr20", globalContractAddress], + token_uri: ["String", ""], + }), + error: ZRC6_ERROR.ThisAddressDestinationError, + want: undefined, + }, + { + name: "throws NotMinterError", + transition: "Mint", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER)], + token_uri: ["String", ""], + }), + error: ZRC6_ERROR.NotMinterError, + want: undefined, + }, + { + name: "mints token by contract owner", + transition: "Mint", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER)], + token_uri: ["String", ""], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] + ).toBe(getTestAddr(STRANGER).toLowerCase()); + expect(state.token_id_count).toBe( + (INITIAL_TOTAL_SUPPLY + 1).toString() + ); }, - { - tag: "ZRC6_MintCallback", - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: ["String", ""], - }), + events: [ + { + name: "Mint", + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: ["String", ""], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptMint", + getParams: () => ({}), + }, + { + tag: "ZRC6_MintCallback", + getParams: () => ({ + to: ["ByStr20", getTestAddr(STRANGER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: ["String", ""], + }), + }, + ], + }, + }, + { + name: "mints token by minter", + transition: "Mint", + getSender: () => getTestAddr(MINTER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_uri: ["String", ""], + }), + error: undefined, + want: { + expectState: (state) => { + expect( + state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] + ).toBe(getTestAddr(MINTER).toLowerCase()); + expect(state.token_id_count).toBe( + (INITIAL_TOTAL_SUPPLY + 1).toString() + ); }, - ], + events: [ + { + name: "Mint", + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: ["String", ""], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptMint", + getParams: () => ({}), + }, + { + tag: "ZRC6_MintCallback", + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: ["String", ""], + }), + }, + ], + }, }, - }, - { - name: "mints a token with a URI by minter", - transition: "Mint", - getSender: () => getTestAddr(MINTER), - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_uri: [ - "String", - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", - ], - }), - error: undefined, - want: { - expectState: (state) => { - expect(JSON.stringify(state.token_uris)).toBe( - JSON.stringify({ - "4": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", - }) - ); - expect( - state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] - ).toBe(getTestAddr(MINTER).toLowerCase()); - expect(state.token_id_count).toBe( - (INITIAL_TOTAL_SUPPLY + 1).toString() - ); + { + name: "mints a token with a URI by minter", + transition: "Mint", + getSender: () => getTestAddr(MINTER), + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_uri: [ + "String", + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + ], + }), + error: undefined, + want: { + expectState: (state) => { + expect(JSON.stringify(state.token_uris)).toBe( + JSON.stringify({ + "4": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + }) + ); + expect( + state.token_owners[(INITIAL_TOTAL_SUPPLY + 1).toString()] + ).toBe(getTestAddr(MINTER).toLowerCase()); + expect(state.token_id_count).toBe( + (INITIAL_TOTAL_SUPPLY + 1).toString() + ); + }, + events: [ + { + name: "Mint", + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: [ + "String", + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + ], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_RecipientAcceptMint", + getParams: () => ({}), + }, + { + tag: "ZRC6_MintCallback", + getParams: () => ({ + to: ["ByStr20", getTestAddr(MINTER)], + token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], + token_uri: [ + "String", + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + ], + }), + }, + ], }, - events: [ - { - name: "Mint", - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: [ - "String", + }, + { + name: "mints tokens with URIs in batches", + transition: "BatchMint", + getSender: () => getTestAddr(TOKEN_OWNER), + getParams: () => ({ + to_token_uri_pair_list: [ + "List (Pair (ByStr20) (String))", + [ + [ + getTestAddr(STRANGER), "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", ], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_RecipientAcceptMint", - getParams: () => ({}), - }, - { - tag: "ZRC6_MintCallback", - getParams: () => ({ - to: ["ByStr20", getTestAddr(MINTER)], - token_id: ["Uint256", INITIAL_TOTAL_SUPPLY + 1], - token_uri: [ - "String", - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + [ + getTestAddr(STRANGER), + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", + ], + [ + getTestAddr(STRANGER), + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", ], - }), - }, - ], - }, - }, - { - name: "mints tokens with URIs in batches", - transition: "BatchMint", - getSender: () => getTestAddr(TOKEN_OWNER), - getParams: () => ({ - to_token_uri_pair_list: [ - "List (Pair (ByStr20) (String))", - [ - [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", - ], - [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", - ], - [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", ], ], - ], - }), - error: undefined, - want: { - expectState: (state) => { - expect(JSON.stringify(state.token_uris)).toBe( - JSON.stringify({ - "4": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", - "5": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", - "6": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", - }) - ); - expect(state.token_id_count).toBe( - (INITIAL_TOTAL_SUPPLY * 2).toString() - ); - - for ( - let i = INITIAL_TOTAL_SUPPLY + 1; - i <= INITIAL_TOTAL_SUPPLY * 2; - i++ - ) { - expect(state.token_owners.hasOwnProperty(i.toString())).toBe(true); - expect(state.token_owners[i.toString()]).toBe( - getTestAddr(STRANGER).toLowerCase() + }), + error: undefined, + want: { + expectState: (state) => { + expect(JSON.stringify(state.token_uris)).toBe( + JSON.stringify({ + "4": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + "5": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", + "6": "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", + }) ); - } - }, - events: [ - { - name: "BatchMint", - getParams: () => ({ - to_token_uri_pair_list: [ - "List (Pair (ByStr20) (String))", - [ - [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", - ], - [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", - ], + expect(state.token_id_count).toBe( + (INITIAL_TOTAL_SUPPLY * 2).toString() + ); + + for ( + let i = INITIAL_TOTAL_SUPPLY + 1; + i <= INITIAL_TOTAL_SUPPLY * 2; + i++ + ) { + expect(state.token_owners.hasOwnProperty(i.toString())).toBe(true); + expect(state.token_owners[i.toString()]).toBe( + getTestAddr(STRANGER).toLowerCase() + ); + } + }, + events: [ + { + name: "BatchMint", + getParams: () => ({ + to_token_uri_pair_list: [ + "List (Pair (ByStr20) (String))", [ - getTestAddr(STRANGER), - "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", + [ + getTestAddr(STRANGER), + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL4", + ], + [ + getTestAddr(STRANGER), + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL5", + ], + [ + getTestAddr(STRANGER), + "https://ipfs.zilliqa.com/ipfs/Zme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pY0000ZIL6", + ], ], ], - ], - start_id: ["Uint256", 4], - end_id: ["Uint256", 6], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_BatchMintCallback", - getParams: () => ({}), - }, - ], + start_id: ["Uint256", 4], + end_id: ["Uint256", 6], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_BatchMintCallback", + getParams: () => ({}), + }, + ], + }, }, - }, - { - name: "throws NotOwnerOrOperatorError", - transition: "Burn", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({ - token_id: ["Uint256", 1], - }), - error: ZRC6_ERROR.NotOwnerOrOperatorError, - want: undefined, - }, - { - name: "throws TokenNotFoundError", - transition: "Burn", - getSender: () => getTestAddr(TOKEN_OWNER), - getParams: () => ({ - token_id: ["Uint256", 999], - }), - error: ZRC6_ERROR.TokenNotFoundError, - want: undefined, - }, - { - name: "burns a token", - transition: "Burn", - getSender: () => getTestAddr(TOKEN_OWNER), - getParams: () => ({ - token_id: ["Uint256", 1], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.token_owners.hasOwnProperty("1")).toBe(false); - }, - events: [ - { - name: "Burn", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_BurnCallback", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], - token_id: ["Uint256", 1], - }), + { + name: "throws NotOwnerOrOperatorError", + transition: "Burn", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({ + token_id: ["Uint256", 1], + }), + error: ZRC6_ERROR.NotOwnerOrOperatorError, + want: undefined, + }, + { + name: "throws TokenNotFoundError", + transition: "Burn", + getSender: () => getTestAddr(TOKEN_OWNER), + getParams: () => ({ + token_id: ["Uint256", 999], + }), + error: ZRC6_ERROR.TokenNotFoundError, + want: undefined, + }, + { + name: "burns a token", + transition: "Burn", + getSender: () => getTestAddr(TOKEN_OWNER), + getParams: () => ({ + token_id: ["Uint256", 1], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.token_owners.hasOwnProperty("1")).toBe(false); }, - ], + events: [ + { + name: "Burn", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_BurnCallback", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + token_id: ["Uint256", 1], + }), + }, + ], + }, }, - }, - { - name: "burns tokens in batches", - transition: "BatchBurn", - getSender: () => getTestAddr(TOKEN_OWNER), - getParams: () => ({ - token_id_list: ["List (Uint256)", [1, 2, 3]], - }), - error: undefined, - want: { - expectState: (state) => { - expect(state.total_supply).toBe("0"); - expect(JSON.stringify(state.token_owners)).toBe("{}"); - }, - events: [ - { - name: "Burn", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], - token_id: ["Uint256", 3], - }), + { + name: "burns tokens in batches", + transition: "BatchBurn", + getSender: () => getTestAddr(TOKEN_OWNER), + getParams: () => ({ + token_id_list: ["List (Uint256)", [1, 2, 3]], + }), + error: undefined, + want: { + expectState: (state) => { + expect(state.total_supply).toBe("0"); + expect(JSON.stringify(state.token_owners)).toBe("{}"); }, - { - name: "Burn", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], - token_id: ["Uint256", 2], - }), - }, - { - name: "Burn", - getParams: () => ({ - token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], - token_id: ["Uint256", 1], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_BatchBurnCallback", - getParams: () => ({}), - }, - ], + events: [ + { + name: "Burn", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + token_id: ["Uint256", 3], + }), + }, + { + name: "Burn", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + token_id: ["Uint256", 2], + }), + }, + { + name: "Burn", + getParams: () => ({ + token_owner: ["ByStr20", getTestAddr(CONTRACT_OWNER)], + token_id: ["Uint256", 1], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_BatchBurnCallback", + getParams: () => ({}), + }, + ], + }, }, - }, - ]; - - for (const testCase of testCases) { - it(`${testCase.transition}: ${testCase.name}`, async () => { - zilliqa.wallet.setDefault(testCase.getSender()); - const tx: any = await zilliqa.contracts - .at(globalContractAddress) - .call( - testCase.transition, - scillaJSONParams(testCase.getParams()), - TX_PARAMS - ); - - if (testCase.want === undefined) { - // Negative Cases - expect(tx.receipt.success).toBe(false); - expect(tx.receipt.exceptions[0].message).toBe( - getErrorMsg(testCase.error) - ); - } else { - // Positive Cases - expect(tx.receipt.success).toBe(true); - expectEvents(tx.receipt.event_logs, testCase.want.events); - expectTransitions(tx.receipt.transitions, testCase.want.transitions); - - const state = await zilliqa.contracts - .at(globalContractAddress) - .getState(); - - testCase.want.expectState(state); - } - }); - } + ], + () => globalContractAddress!, + TX_PARAMS + ); }); diff --git a/tests/zrc6/zrc6.token.test.ts b/tests/zrc6/zrc6.token.test.ts index efc261416..4500dd25f 100644 --- a/tests/zrc6/zrc6.token.test.ts +++ b/tests/zrc6/zrc6.token.test.ts @@ -1,35 +1,26 @@ -import { Zilliqa } from "@zilliqa-js/zilliqa"; import { expect } from "@jest/globals"; -import { getAddressFromPrivateKey, schnorr } from "@zilliqa-js/crypto"; - import { scillaJSONVal, scillaJSONParams } from "@zilliqa-js/scilla-json-utils"; - +import { JEST_WORKER_ID, GENESIS_PRIVATE_KEY, zilliqa } from "../globalConfig"; import { getErrorMsg, expectTransitions, expectEvents, ZERO_ADDRESS, -} from "./testutils"; - + getAccounts, + runAllTestCases, +} from "./../testutils"; import { - API, TX_PARAMS, CODE, ZRC6_ERROR, TOKEN_NAME, TOKEN_SYMBOL, - FAUCET_PARAMS, BASE_URI, } from "./config"; -const JEST_WORKER_ID = Number(process.env["JEST_WORKER_ID"]); -const GENESIS_PRIVATE_KEY = global.GENESIS_PRIVATE_KEYS[JEST_WORKER_ID - 1]; const INITIAL_TOTAL_SUPPLY = 3; -const zilliqa = new Zilliqa(API); -zilliqa.wallet.addByPrivateKey(GENESIS_PRIVATE_KEY); - -let globalContractAddress; +let globalContractAddress: string | undefined; let globalTestAccounts: Array<{ privateKey: string; @@ -43,28 +34,7 @@ const STRANGER = 3; const getTestAddr = (index) => globalTestAccounts[index]?.address as string; beforeAll(async () => { - const accounts = Array.from({ length: 4 }, schnorr.generatePrivateKey).map( - (privateKey) => ({ - privateKey, - address: getAddressFromPrivateKey(privateKey), - }) - ); - for (const { privateKey, address } of accounts) { - zilliqa.wallet.addByPrivateKey(privateKey); - const tx = await zilliqa.blockchain.createTransaction( - zilliqa.transactions.new( - { - ...FAUCET_PARAMS, - toAddr: address, - }, - false - ) - ); - if (!tx.getReceipt()?.success) { - throw new Error(); - } - } - globalTestAccounts = accounts; + globalTestAccounts = await getAccounts(4); console.table({ JEST_WORKER_ID, @@ -422,7 +392,7 @@ describe("Contract", () => { for (const testCase of testCases) { it(`${testCase.transition}: ${testCase.name}`, async () => { - let state = await zilliqa.contracts.at(globalContractAddress).getState(); + let state = await zilliqa.contracts.at(globalContractAddress!).getState(); expect(JSON.stringify(state)).toBe( JSON.stringify({ _balance: "0", @@ -459,7 +429,7 @@ describe("Contract", () => { zilliqa.wallet.setDefault(testCase.getSender()); const tx: any = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .call( testCase.transition, scillaJSONParams(testCase.getParams()), @@ -479,7 +449,7 @@ describe("Contract", () => { expectTransitions(tx.receipt.transitions, testCase.want.transitions); const state = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .getState(); testCase.want.expectState(state); @@ -490,7 +460,7 @@ describe("Contract", () => { describe("Accept Contract Ownership", () => { beforeEach(async () => { - const tx: any = await zilliqa.contracts.at(globalContractAddress).call( + const tx: any = await zilliqa.contracts.at(globalContractAddress!).call( "SetContractOwnershipRecipient", scillaJSONParams({ to: ["ByStr20", getTestAddr(CONTRACT_OWNERSHIP_RECIPIENT)], @@ -551,7 +521,7 @@ describe("Accept Contract Ownership", () => { for (const testCase of testCases) { it(`${testCase.transition}: ${testCase.name}`, async () => { - let state = await zilliqa.contracts.at(globalContractAddress).getState(); + let state = await zilliqa.contracts.at(globalContractAddress!).getState(); expect(JSON.stringify(state)).toBe( JSON.stringify({ @@ -591,7 +561,7 @@ describe("Accept Contract Ownership", () => { zilliqa.wallet.setDefault(testCase.getSender()); const tx: any = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .call( testCase.transition, scillaJSONParams(testCase.getParams()), @@ -611,7 +581,7 @@ describe("Accept Contract Ownership", () => { expectTransitions(tx.receipt.transitions, testCase.want.transitions); const state = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .getState(); testCase.want.expectState(state); @@ -621,99 +591,64 @@ describe("Accept Contract Ownership", () => { }); describe("Unpaused", () => { - const testCases = [ - { - name: "Throws NotPausedError for the not paused contract", - transition: "Unpause", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({}), - error: ZRC6_ERROR.NotPausedError, - want: undefined, - }, - { - name: "Throws NotContractOwnerError", - transition: "Pause", - getSender: () => getTestAddr(STRANGER), - getParams: () => ({}), - error: ZRC6_ERROR.NotContractOwnerError, - want: undefined, - }, - { - name: "Pause the contract", - transition: "Pause", - getSender: () => getTestAddr(CONTRACT_OWNER), - getParams: () => ({}), - want: { - expectState: (state) => { - expect(JSON.stringify(state.is_paused)).toBe( - JSON.stringify(scillaJSONVal("Bool", true)) - ); - }, - events: [ - { - name: "Pause", - getParams: () => ({ - is_paused: ["Bool", true], - }), - }, - ], - transitions: [ - { - tag: "ZRC6_PauseCallback", - getParams: () => ({ - is_paused: ["Bool", true], - }), + runAllTestCases( + [ + { + name: "Throws NotPausedError for the not paused contract", + transition: "Unpause", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({}), + error: ZRC6_ERROR.NotPausedError, + want: undefined, + }, + { + name: "Throws NotContractOwnerError", + transition: "Pause", + getSender: () => getTestAddr(STRANGER), + getParams: () => ({}), + error: ZRC6_ERROR.NotContractOwnerError, + want: undefined, + }, + { + name: "Pause the contract", + transition: "Pause", + getSender: () => getTestAddr(CONTRACT_OWNER), + getParams: () => ({}), + error: undefined, + want: { + expectState: (state) => { + expect(JSON.stringify(state.is_paused)).toBe( + JSON.stringify(scillaJSONVal("Bool", true)) + ); }, - ], + events: [ + { + name: "Pause", + getParams: () => ({ + is_paused: ["Bool", true], + }), + }, + ], + transitions: [ + { + tag: "ZRC6_PauseCallback", + getParams: () => ({ + is_paused: ["Bool", true], + }), + }, + ], + }, }, - }, - ]; - - for (const testCase of testCases) { - it(`${testCase.transition}: ${testCase.name}`, async () => { - const state = await zilliqa.contracts - .at(globalContractAddress) - .getState(); - - expect(JSON.stringify(state.is_paused)).toBe( - JSON.stringify(scillaJSONVal("Bool", false)) - ); - - zilliqa.wallet.setDefault(testCase.getSender()); - const tx: any = await zilliqa.contracts - .at(globalContractAddress) - .call( - testCase.transition, - scillaJSONParams(testCase.getParams()), - TX_PARAMS - ); - - if (testCase.want === undefined) { - // Negative Cases - expect(tx.receipt.success).toBe(false); - expect(tx.receipt.exceptions[0].message).toBe( - getErrorMsg(testCase.error) - ); - } else { - // Positive Cases - expect(tx.receipt.success).toBe(true); - expectEvents(tx.receipt.event_logs, testCase.want.events); - expectTransitions(tx.receipt.transitions, testCase.want.transitions); - - const state = await zilliqa.contracts - .at(globalContractAddress) - .getState(); - - testCase.want.expectState(state); - } - }); - } + ], + () => globalContractAddress!, + TX_PARAMS + ); }); describe("Paused", () => { beforeEach(async () => { let tx: any = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .call("Pause", [], TX_PARAMS); if (!tx.receipt.success) { @@ -821,7 +756,7 @@ describe("Paused", () => { for (const testCase of testCases) { it(`${testCase.transition}: ${testCase.name}`, async () => { const state = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .getState(); expect(JSON.stringify(state.is_paused)).toBe( @@ -830,7 +765,7 @@ describe("Paused", () => { zilliqa.wallet.setDefault(testCase.getSender()); const tx: any = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .call( testCase.transition, scillaJSONParams(testCase.getParams()), @@ -850,7 +785,7 @@ describe("Paused", () => { expectTransitions(tx.receipt.transitions, testCase.want.transitions); const state = await zilliqa.contracts - .at(globalContractAddress) + .at(globalContractAddress!) .getState(); testCase.want.expectState(state);