Skip to content

Commit

Permalink
tests for burnable zrc2 added, tests refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
lukozill committed Apr 15, 2024
1 parent e99967a commit 1b35173
Show file tree
Hide file tree
Showing 9 changed files with 1,494 additions and 1,370 deletions.
22 changes: 22 additions & 0 deletions tests/globalConfig.ts
Original file line number Diff line number Diff line change
@@ -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),
};
126 changes: 126 additions & 0 deletions tests/testutils.ts
Original file line number Diff line number Diff line change
@@ -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<string, Array<unknown>>,
error: number | undefined,
want: {
expectState: (state: any) => void,
events: Array<{
name: string,
getParams: () => Record<string, Array<unknown>>,
}>,
transitions: Array<{
tag: string,
getParams: () => Record<string, Array<unknown>>,
}>,
} | 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<string, Array<unknown>>,
}>
) => {
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<ContractTestCaseDefinition>,
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);
}
});
}
}
28 changes: 28 additions & 0 deletions tests/zrc2/fungible-token-burnable/config.ts
Original file line number Diff line number Diff line change
@@ -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,
};
201 changes: 201 additions & 0 deletions tests/zrc2/fungible-token-burnable/fungibleBurnableToken.test.ts
Original file line number Diff line number Diff line change
@@ -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
)
});
Loading

0 comments on commit 1b35173

Please sign in to comment.