UNPKG

namillum

Version:
1,919 lines (1,735 loc) 106 kB
/* eslint-disable no-undef */ require("dotenv").config({ path: "./.env" }); const { BN } = require("@zilliqa-js/util"); const { scillaJSONVal } = require("@zilliqa-js/scilla-json-utils"); const { getAddressFromPrivateKey, schnorr } = require("@zilliqa-js/crypto"); const { deployAllowlistContract, } = require("../scripts/marketplace/deployAllowlistContract.js"); const { deployFixedPriceContract, } = require("../scripts/marketplace/deployFixedPriceContract.js"); const { deployFungibleToken } = require("../scripts/deployFungibleToken.js"); const { deployCollectionContract, } = require("../scripts/marketplace/deployCollectionContract.js"); const { addTokenToCollection, } = require("../scripts/marketplace/addTokenToCollection"); const { deployNonFungibleToken, } = require("../scripts/deployNonFungibleToken.js"); const { setupBalancesOnAccounts, clearBalancesOnAccounts, } = require("../scripts/utils/call.js"); const { callContract, getBalance, getZRC2State, } = require("../scripts/utils/call.js"); const { getBlockNumber } = require("../scripts/utils/helper"); const { zilliqa } = require("../scripts/utils/zilliqa.js"); const zero_address = "0x0000000000000000000000000000000000000000"; const accounts = { contractOwner: { privateKey: process.env.MASTER_PRIVATE_KEY, address: getAddressFromPrivateKey(process.env.MASTER_PRIVATE_KEY), }, nftSeller: { privateKey: process.env.SELLER_PRIVATE_KEY, address: getAddressFromPrivateKey(process.env.SELLER_PRIVATE_KEY), }, nftBuyer: { privateKey: process.env.BUYER_PRIVATE_KEY, address: getAddressFromPrivateKey(process.env.BUYER_PRIVATE_KEY), }, stranger: { privateKey: process.env.N_01_PRIVATE_KEY, address: getAddressFromPrivateKey(process.env.N_01_PRIVATE_KEY), }, forbidden: { address: getAddressFromPrivateKey(process.env.N_02_PRIVATE_KEY), privateKey: process.env.N_02_PRIVATE_KEY, }, address01: { address: getAddressFromPrivateKey(process.env.TOKEN1_PRIVATE_KEY), privateKey: process.env.N_01_PRIVATE_KEY, }, address02: { address: getAddressFromPrivateKey(process.env.TOKEN2_PRIVATE_KEY), privateKey: process.env.N_02_PRIVATE_KEY, }, address03: { address: getAddressFromPrivateKey(process.env.N_03_PRIVATE_KEY), privateKey: process.env.N_03_PRIVATE_KEY, }, address04: { address: getAddressFromPrivateKey(process.env.N_04_PRIVATE_KEY), privateKey: process.env.N_04_PRIVATE_KEY, }, }; let paymentTokenAddress; let fixedPriceAddress; let nftTokenAddress; let allowlistAddress; let notAllowedpaymentTokenAddress; let collectionContractAddress; let paymentTokenAddress1; let paymentTokenAddress2; async function createCollectionItemParam( collectionContractAddress, tokenAddress, tokenId, collection_id ) { return { constructor: `${collectionContractAddress.toLowerCase()}.CollectionItemParam`, argtypes: [], arguments: [tokenAddress.toLowerCase(), tokenId, collection_id], }; } beforeAll(async () => { await setupBalancesOnAccounts(accounts); }); afterAll(async () => { await clearBalancesOnAccounts(accounts); }); beforeEach(async () => { globalBNum = await getBlockNumber(zilliqa); // Contract Deployments const fungibleTokenDeployParams = { name: "wZIL", symbol: null, decimals: 12, supply: new BN("10000000000000000"), dexCheck: "True", }; const [paymentToken] = await deployFungibleToken( accounts.contractOwner.privateKey, fungibleTokenDeployParams, accounts.contractOwner.address ); paymentTokenAddress = paymentToken.address; if (paymentTokenAddress === undefined) { throw new Error(); } const notAllowedfungibleTokenDeployParams = { name: "wYIL", symbol: null, decimals: 12, supply: new BN("10000000000000000"), dexCheck: "True", }; const [notAllowedpaymentToken] = await deployFungibleToken( accounts.contractOwner.privateKey, notAllowedfungibleTokenDeployParams, accounts.contractOwner.address ); notAllowedpaymentTokenAddress = notAllowedpaymentToken.address; if (notAllowedpaymentTokenAddress === undefined) { throw new Error(); } const fungibleTokenDeployParams1 = { name: "xSGD", symbol: "xSGD", decimals: 6, supply: new BN("10000000000000000"), dexCheck: "True", }; const [paymentToken1] = await deployFungibleToken( accounts.contractOwner.privateKey, fungibleTokenDeployParams1, accounts.contractOwner.address ); paymentTokenAddress1 = paymentToken1.address; if (paymentTokenAddress1 === undefined) { throw new Error(); } const fungibleTokenDeployParams2 = { name: "xIDR", symbol: "xIDR", decimals: 6, supply: new BN("10000000000000000"), dexCheck: "True", }; const [paymentToken2] = await deployFungibleToken( accounts.contractOwner.privateKey, fungibleTokenDeployParams2, accounts.contractOwner.address ); paymentTokenAddress2 = paymentToken2.address; if (paymentTokenAddress2 === undefined) { throw new Error(); } const [collectionContract] = await deployCollectionContract( accounts.contractOwner.privateKey, { initialOwnerAddress: accounts.contractOwner.address, } ); collectionContractAddress = collectionContract.address; if (collectionContractAddress === undefined) { throw new Error(); } const [fixedPriceContract] = await deployFixedPriceContract( accounts.contractOwner.privateKey, { initialOwnerAddress: accounts.contractOwner.address, collectionContract: collectionContract.address.toLowerCase(), } ); fixedPriceAddress = fixedPriceContract.address; console.log("fixedPriceContract =", fixedPriceAddress); if (fixedPriceAddress === undefined) { throw new Error(); } const [allowlistContract] = await deployAllowlistContract( accounts.contractOwner.privateKey, { initialOwnerAddress: accounts.contractOwner.address, } ); allowlistAddress = allowlistContract.address; if (allowlistAddress === undefined) { throw new Error(); } const nonFungibleTokenDeployParams = { name: "TestNFTToken1", symbol: null, baseURI: "https://ipfs.io/ipfs/", }; const [nftContract] = await deployNonFungibleToken( accounts.nftSeller.privateKey, nonFungibleTokenDeployParams, accounts.nftSeller.address ); nftTokenAddress = nftContract.address; if (nftTokenAddress === undefined) { throw new Error(); } // ACCOUNT PREP // Whitelist addresses await callContract( accounts.contractOwner.privateKey, allowlistContract, "Allow", [ { vname: "address_list", type: "List (ByStr20)", value: [ accounts.contractOwner.address, accounts.nftSeller.address, accounts.nftBuyer.address, accounts.stranger.address, ], }, ], 0, false, false ); // Set the allowlist Contract await callContract( accounts.contractOwner.privateKey, fixedPriceContract, "SetAllowlist", [ { vname: "address", type: "ByStr20", value: allowlistAddress, }, ], 0, false, false ); // Batch-mint some NFTs const pair = await createPairADT(accounts.nftSeller.address, ""); await callContract( accounts.nftSeller.privateKey, nftContract, "BatchMint", [ { vname: "to_token_uri_pair_list", type: "List (Pair (ByStr20) (String))", value: [pair, pair, pair], }, ], 0, false, false ); // Set wZil as an allowed payment token await callContract( accounts.contractOwner.privateKey, fixedPriceContract, "AllowPaymentTokenAddress", [ { vname: "address", type: "ByStr20", value: paymentTokenAddress, }, ], 0, false, false ); // Set xSGD as an allowed payment token await callContract( accounts.contractOwner.privateKey, fixedPriceContract, "AllowPaymentTokenAddress", [ { vname: "address", type: "ByStr20", value: paymentTokenAddress1, }, ], 0, false, false ); // Set xIDR as an allowed payment token await callContract( accounts.contractOwner.privateKey, fixedPriceContract, "AllowPaymentTokenAddress", [ { vname: "address", type: "ByStr20", value: paymentTokenAddress2, }, ], 0, false, false ); // Increasing the amount of wZIL the fixedPriceContract can spend await callContract( accounts.nftBuyer.privateKey, paymentToken, "IncreaseAllowance", [ { vname: "spender", type: "ByStr20", value: fixedPriceAddress, }, { vname: "amount", type: "Uint128", value: String(100 * 1000), }, ], 0, false, false ); // Increasing the amount of xSGD the fixedPriceContract can spend await callContract( accounts.nftBuyer.privateKey, paymentToken1, "IncreaseAllowance", [ { vname: "spender", type: "ByStr20", value: fixedPriceAddress, }, { vname: "amount", type: "Uint128", value: String(100 * 1000), }, ], 0, false, false ); // Increasing the amount of xIDR the fixedPriceContract can spend await callContract( accounts.nftBuyer.privateKey, paymentToken2, "IncreaseAllowance", [ { vname: "spender", type: "ByStr20", value: fixedPriceAddress, }, { vname: "amount", type: "Uint128", value: String(100 * 1000), }, ], 0, false, false ); // only token_id 1 to make tests run faster await callContract( accounts.nftSeller.privateKey, nftContract, "SetSpender", [ { vname: "spender", type: "ByStr20", value: fixedPriceAddress, }, { vname: "token_id", type: "Uint256", value: String(1), }, ], 0, false, false ); // set spender for token_id 2 await callContract( accounts.nftSeller.privateKey, nftContract, "SetSpender", [ { vname: "spender", type: "ByStr20", value: fixedPriceAddress, }, { vname: "token_id", type: "Uint256", value: String(2), }, ], 0, false, false ); const tx = await callContract( accounts.contractOwner.privateKey, collectionContract, "RegisterMarketplaceAddress", [ { vname: "address", type: "ByStr20", value: fixedPriceAddress.toLowerCase(), }, ], 0, false, false ); expect(tx.receipt.success).toEqual(true); }); async function createFixedPriceOrder( fixedPriceContractAddress, tokenAddress, tokenId, paymentTokenAddress, salePrice, side, expirationBnum ) { return { constructor: `${fixedPriceContractAddress.toLowerCase()}.OrderParam`, argtypes: [], arguments: [ tokenAddress, tokenId, paymentTokenAddress, salePrice, side, expirationBnum, ], }; } async function createPairADT(address, string) { return { constructor: "Pair", argtypes: ["ByStr20", "String"], arguments: [address, string], }; } async function createOrder( _tokenId, _salePrice, _side, _expiryBlock, _paymentToken ) { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, _tokenId, _paymentToken, _salePrice, _side, _expiryBlock ); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(true); return tx; } describe("Native ZIL", () => { beforeEach(async () => { // First we succesfully create a sell order const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const sellOrderParams = { tokenId: String(1), paymentToken: "0x0000000000000000000000000000000000000000", //price: new BN("10000"), price: "10000", side: "0", expiryBlock: String(globalBNum + 20), }; const buyOrderParams = { tokenId: String(1), paymentToken: "0x0000000000000000000000000000000000000000", price: "10000", side: "1", expiryBlock: String(globalBNum + 20), }; // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedSaleAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, sellOrderParams.tokenId, sellOrderParams.paymentToken, sellOrderParams.price, sellOrderParams.side, sellOrderParams.expiryBlock ); const txSellOrder = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedSaleAdtOrder, }, ], 0, false, false ); const txEvent = txSellOrder.receipt.event_logs.filter( (e) => e._eventname === "SetOrder" )[0]; // console.log(txSellOrder.receipt) // expect(txSellOrder.receipt.success).toEqual(true) const contractState = await fixedPriceContract.getState(); const sellOrders = contractState.sell_orders; const newSellOrder = Object.keys(sellOrders).filter((order) => order.includes(1) ); // Then a buy order for the same token_id // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedBuyAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, buyOrderParams.tokenId, buyOrderParams.paymentToken, buyOrderParams.price, buyOrderParams.side, buyOrderParams.expiryBlock ); const txBuyOrder = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedBuyAdtOrder, }, ], buyOrderParams.price, false, false ); // const event = txBuyOrder.receipt.event_logs.filter( // (e) => // e._eventname === 'DeleteDis' // )[0] // console.log('event', event) // console.dir(txBuyOrder, { depth: null }) expect(txBuyOrder.receipt.success).toEqual(true); }); test("SetOrder: throws NotAllowedUserError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, "1", "0x0000000000000000000000000000000000000000", "10000", "0", String(globalBNum + 35) ); const tx = await callContract( accounts.forbidden.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); console.log(tx.receipt.exceptions); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -19))])', }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from SetOrder" }, ]); }); test("SetOrder: throws NotTokenOwnerError (stranger creates sell order for token #1)", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, "1", zero_address, "20000", "0", String(globalBNum + 35) ); const tx = await callContract( accounts.stranger.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -9))])', }, { line: 1, message: "Raised from RequireValidTotalFees" }, { line: 1, message: "Raised from RequireThisToBeSpender" }, { line: 1, message: "Raised from RequireAllowedPaymentToken" }, { line: 1, message: "Raised from RequireNotExpired" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from SetOrder" }, ]); }); test("SetOrder: throws TokenOwnerError (seller creates buy order for token #1)", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, "1", zero_address, "20000", "1", String(globalBNum + 35) ); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -10))])', }, { line: 1, message: "Raised from RequireValidTotalFees" }, { line: 1, message: "Raised from RequireThisToBeSpender" }, { line: 1, message: "Raised from RequireAllowedPaymentToken" }, { line: 1, message: "Raised from RequireNotExpired" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from SetOrder" }, ]); }); test("SetOrder: throws NotSelfError (stranger must not update the order)", async () => { // tx pass when it maybe shouldnt? Is a stranger allowed to do a SetOrder (buy) on a token not his? const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, "1", zero_address, "10000", "1", String(globalBNum + 35) ); const tx = await callContract( accounts.stranger.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -13))])', }, { line: 1, message: "Raised from RequireSenderNotToBeTokenOwner" }, { line: 1, message: "Raised from RequireValidTotalFees" }, { line: 1, message: "Raised from RequireThisToBeSpender" }, { line: 1, message: "Raised from RequireAllowedPaymentToken" }, { line: 1, message: "Raised from RequireNotExpired" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from SetOrder" }, ]); }); test("SetOrder: buyer updates expiration_bnum of buy order", async () => { // This is not updating an order, simply setting the value on the first order. Should be Changed const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(10000); const side = String(1); const newExpiryBlock = String(globalBNum + 99999); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, tokenId, zero_address, salePrice, side, newExpiryBlock ); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(true); // Confirming that the order was executed correctly based on the emitted event const txEvent = tx.receipt.event_logs.filter( (e) => e._eventname === "SetOrder" )[0]; let tokenAddress = txEvent.params[2].value; tokenAddress = tokenAddress.toLowerCase(); expect(txEvent.params[1].value).toEqual(side); expect(txEvent.params[3].value).toEqual(tokenId); expect(txEvent.params[5].value).toEqual(salePrice); expect(txEvent.params[6].value).toEqual(newExpiryBlock); // Confirming that our buy order was in fact updated correctly const contractState = await fixedPriceContract.getState(); console.log(contractState.buy_orders); expect(JSON.stringify(contractState.buy_orders)).toBe( JSON.stringify({ [nftTokenAddress.toLowerCase()]: { [1]: { [zero_address.toLowerCase()]: { [10000]: scillaJSONVal( `${fixedPriceAddress}.Order.Order.of.ByStr20.BNum`, [accounts.nftBuyer.address, newExpiryBlock] ), }, }, }, }) ); }); test("SetOrder: Seller creates sell order for token #1", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(20000); const side = String(0); const expiryBlock = String(globalBNum + 35); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, tokenId, zero_address, salePrice, side, expiryBlock ); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(true); // Confirming that the order was executed correctly based on the emitted event const txEvent = tx.receipt.event_logs.filter( (e) => e._eventname === "SetOrder" )[0]; let tokenAddress = txEvent.params[2].value; tokenAddress = tokenAddress.toLowerCase(); expect(txEvent.params[1].value).toEqual(side); expect(txEvent.params[3].value).toEqual(tokenId); expect(txEvent.params[5].value).toEqual(salePrice); expect(txEvent.params[6].value).toEqual(expiryBlock); }); test("SetOrder: Buyer creates buy order for token #1", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const fixedPriceStartingBalance = await getBalance(fixedPriceAddress); const buyerStartingBalance = await getBalance(accounts.nftBuyer.address); const tokenId = String(1); const salePrice = String(20000); const side = String(1); const expiryBlock = String(globalBNum + 35); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, tokenId, zero_address, salePrice, side, expiryBlock ); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "SetOrder", [ { vname: "order", type: `${fixedPriceAddress}.OrderParam`, value: formattedAdtOrder, }, ], salePrice, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(true); // Confirming that the order was executed correctly based on the emitted event const txEvent = tx.receipt.event_logs.filter( (e) => e._eventname === "SetOrder" )[0]; let tokenAddress = txEvent.params[2].value; tokenAddress = tokenAddress.toLowerCase(); expect(txEvent.params[1].value).toEqual(side); expect(txEvent.params[3].value).toEqual(tokenId); expect(txEvent.params[5].value).toEqual(salePrice); expect(txEvent.params[6].value).toEqual(expiryBlock); // Confirming that the token balance of buyer and contract updated correctly const fixedPriceEndingBalance = await getBalance(fixedPriceAddress); const buyerEndingBalance = await getBalance(accounts.nftBuyer.address); const txFee = parseInt(tx.receipt.cumulative_gas) * parseInt(tx.gasPrice); // const bid = new BN(salePrice const totalExpense = parseInt(salePrice + txFee); console.log("starting Balance:", buyerStartingBalance); console.log("ending balance:", buyerEndingBalance); console.log("gas total cost:", txFee); console.log("buy offer + gas total cost:", parseInt(salePrice) + txFee); expect(parseInt(fixedPriceEndingBalance)).toBe( parseInt(fixedPriceStartingBalance) + parseInt(salePrice) ); // expect(parseInt(buyerEndingBalance)).toBe((totalExpense) - parseInt(buyerStartingBalance)) // Confirming that our buy order was executed correctly by reading the contract state const contractState = await fixedPriceContract.getState(); expect(JSON.stringify(contractState.buy_orders)).toBe( JSON.stringify({ [nftTokenAddress.toLowerCase()]: { [1]: { [zero_address]: { [10000]: scillaJSONVal( `${fixedPriceAddress}.Order.Order.of.ByStr20.BNum`, [ accounts.nftBuyer.address, contractState.buy_orders[nftTokenAddress.toLowerCase()][1][ zero_address.toLowerCase() ][10000].arguments[1], ] ), [salePrice]: scillaJSONVal( `${fixedPriceAddress}.Order.Order.of.ByStr20.BNum`, [accounts.nftBuyer.address, expiryBlock] ), }, }, }, }) ); }); test("SetBatchOrder: throws PausedError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const orderList = []; const tokenId = String(1); const salePrice = String(20000); const side = String(0); const expiryBlock = String(globalBNum + 35); const pauseTx = await callContract( accounts.contractOwner.privateKey, fixedPriceContract, "Pause", [], 0, false, false ); expect(pauseTx.receipt.success).toEqual(true); // The 'SetOrder' takes in an ADT called 'OrderParam' so need to construct it first const formattedAdtOrder = await createFixedPriceOrder( fixedPriceAddress, nftTokenAddress, tokenId, zero_address, salePrice, side, expiryBlock ); for (let i = 1; i < 4; i++) { orderList.push(formattedAdtOrder); } const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "SetBatchOrder", [ { vname: "order_list", type: `List ${fixedPriceAddress}.OrderParam`, value: orderList, }, ], 0, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(false); }); test("FulfillOrder: throws NotAllowedUserError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const tx = await callContract( accounts.forbidden.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: paymentTokenAddress, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -19))])', }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from FulfillOrder" }, ]); }); test("FulfillOrder: throws SellOrderNotFoundError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(999); const salePrice = String(10000); const side = String(0); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: paymentTokenAddress, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -6))])', }, { line: 1, message: "Raised from RequireValidDestination" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from FulfillOrder" }, ]); }); test("FulfillOrder: throws BuyOrderNotFoundError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(999); const salePrice = String(10000); const side = String(1); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: paymentTokenAddress, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftSeller.address, }, ], 0, false, false ); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -7))])', }, { line: 1, message: "Raised from RequireValidDestination" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from FulfillOrder" }, ]); }); test("FulfillOrder: throws ExpiredError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); await zilliqa.provider.send("IncreaseBlocknum", 1000); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], salePrice, false, false ); console.log("FulfillOrder: throws ExpiredError", tx.receipt); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -11))])', }, { line: 1, message: "Raised from RequireValidDestination" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from FulfillOrder" }, ]); }); test("FulfillOrder: throws NotEqualAmountError", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); console.log("buy balance"); console.log(await getBalance(accounts.nftBuyer.address)); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const txAmount = 100; // this is less than the order amount, so expected to fail const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], txAmount, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -17))])', }, { line: 1, message: "Raised from RequireNotSelf" }, { line: 1, message: "Raised from RequireNotExpired" }, { line: 1, message: "Raised from RequireValidDestination" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireAllowedUser" }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from FulfillOrder" }, ]); }); test("FulfillOrder: Buyer fullfills sell order (not collection item)", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const nftContract = await zilliqa.contracts.at(nftTokenAddress); const setRoyaltyFee = await callContract( accounts.nftSeller.privateKey, nftContract, "SetRoyaltyFeeBPS", [ { vname: "fee_bps", type: "Uint128", value: "2211", }, ], 0, false, false ); console.log(setRoyaltyFee.receipt); expect(setRoyaltyFee.receipt.success).toEqual(true); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress.toLowerCase(), }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: "0x0000000000000000000000000000000000000000", }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], salePrice, false, false ); console.log(tx.receipt); expect(tx.receipt.success).toEqual(true); const event = tx.receipt.event_logs.filter( (e) => e._eventname === "FulfillOrder" )[0]; console.log(event); }); test("FulfillOrder: Buyer fullfills sell order (IS collection item)", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const collectionContract = await zilliqa.contracts.at( collectionContractAddress ); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const createCollectionTx = await callContract( accounts.address01.privateKey, collectionContract, "CreateCollection", [ { vname: "commission_fee", type: "Uint128", value: "129", }, ], 0, false, false ); expect(createCollectionTx.receipt.success).toEqual(true); const collectionItem = await createCollectionItemParam( collectionContractAddress, nftTokenAddress, "1", "1" ); // await addTokenToCollection( // collectionContract, // accounts.nftSeller.privateKey, // accounts.address01.privateKey, // collectionItem // ) const sendRequestTx = await callContract( accounts.address01.privateKey, collectionContract, "RequestTokenToCollection", [ { vname: "request", type: `${collectionContractAddress}.CollectionItemParam`, value: collectionItem, }, ], 0, false, false ); expect(sendRequestTx.receipt.success).toEqual(true); const acceptRequestTx = await callContract( accounts.nftSeller.privateKey, collectionContract, "AcceptCollectionRequest", [ { vname: "request", type: `${collectionContractAddress}.CollectionItemParam`, value: collectionItem, }, ], 0, false, false ); expect(acceptRequestTx.receipt.success).toEqual(true); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress.toLowerCase(), }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: "0x0000000000000000000000000000000000000000", }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], salePrice, false, false ); const eventFulfillOrder = tx.receipt.event_logs.filter( (e) => e._eventname === "FulfillOrder" )[0]; const eventCommissionFeePaid = tx.receipt.event_logs.filter( (e) => e._eventname === "CommissionFeePaid" )[0]; console.log("FulfillOrder event", eventFulfillOrder); console.log("CommissionFeePaid event", eventCommissionFeePaid); // console.log(tx.receipt) console.log(tx.receipt.transitions); expect(tx.receipt.success).toEqual(true); }); test("FulfillOrder: Seller fullfills buy order", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(10000); const side = String(1); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "FulfillOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, { vname: "dest", type: "ByStr20", value: accounts.nftBuyer.address, }, ], salePrice, false, false ); expect(tx.receipt.success).toEqual(true); }); test("CancelOrder: throws NotAllowedToCancelOrder by stranger", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(10000); const side = String(1); const tx = await callContract( accounts.stranger.privateKey, fixedPriceContract, "CancelOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, ], 0, false, false ); //console.log(tx) //console.log(tx.receipt) expect(tx.receipt.success).toEqual(false); expect(tx.receipt.exceptions).toEqual([ { line: 1, message: 'Exception thrown: (Message [(_exception : (String "Error")) ; (code : (Int32 -12))])', }, { line: 1, message: "Raised from RequireNotPaused" }, { line: 1, message: "Raised from CancelOrder" }, ]); }); test("CancelOrder: Buyer cancels buy order", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const fixedPriceStartingBalance = await getBalance(fixedPriceAddress); const buyerStartingBalance = await getBalance(accounts.nftBuyer.address); const tokenId = String(1); const salePrice = String(10000); const side = String(1); const tx = await callContract( accounts.nftBuyer.privateKey, fixedPriceContract, "CancelOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, ], 0, false, false ); //console.log(tx) //console.log(tx.receipt) expect(tx.receipt.success).toEqual(true); // Confirming that the order was executed correctly based on the emitted event const txEvent = tx.receipt.event_logs.filter( (e) => e._eventname === "CancelOrder" )[0]; //let tokenAddress = txEvent.params[2].value //tokenAddress = tokenAddress.toLowerCase() console.log(txEvent); expect(txEvent.params[0].value).toEqual( accounts.nftBuyer.address.toLowerCase() ); expect(txEvent.params[5].value).toEqual(salePrice); const fixedPriceEndingBalance = await getBalance(fixedPriceAddress); const buyerEndingBalance = await getBalance(accounts.nftBuyer.address); console.log(fixedPriceStartingBalance, fixedPriceEndingBalance, salePrice); console.log(buyerStartingBalance, buyerEndingBalance, salePrice); expect(parseInt(fixedPriceEndingBalance)).toBe( parseInt(fixedPriceStartingBalance) - parseInt(salePrice) ); // expect(parseInt(buyerEndingBalance)).toBe((salePrice) + parseInt(buyerStartingBalance)) // Confirming that our buy order was in fact updated correctly const contractState = await fixedPriceContract.getState(); expect(JSON.stringify(contractState.buy_orders)).toBe( JSON.stringify({ [nftTokenAddress.toLowerCase()]: { [1]: { [zero_address.toLowerCase()]: {} }, }, }) ); }); test("CancelOrder: Seller cancels sell order", async () => { const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const tokenId = String(1); const salePrice = String(10000); const side = String(0); const tx = await callContract( accounts.nftSeller.privateKey, fixedPriceContract, "CancelOrder", [ { vname: "token_address", type: "ByStr20", value: nftTokenAddress, }, { vname: "token_id", type: "Uint256", value: tokenId, }, { vname: "payment_token_address", type: "ByStr20", value: zero_address, }, { vname: "sale_price", type: "Uint128", value: salePrice, }, { vname: "side", type: "Uint32", value: side, }, ], 0, false, false ); //console.log(tx) console.log(tx.receipt); expect(tx.receipt.success).toEqual(true); // Confirming that the order was executed correctly based on the emitted event const txEvent = tx.receipt.event_logs.filter( (e) => e._eventname === "CancelOrder" )[0]; //let tokenAddress = txEvent.params[2].value //tokenAddress = tokenAddress.toLowerCase() console.log(txEvent); expect(txEvent.params[0].value).toEqual( accounts.nftSeller.address.toLowerCase() ); expect(txEvent.params[5].value).toEqual(salePrice); // Confirming that our buy order was in fact updated correctly const contractState = await fixedPriceContract.getState(); expect(JSON.stringify(contractState.sell_orders)).toBe( JSON.stringify({ [nftTokenAddress.toLowerCase()]: { [1]: { [zero_address.toLowerCase()]: {} }, }, }) ); }); }); describe("Wrapped ZIL", () => { beforeEach(async () => { // First we succesfully create a sell order const fixedPriceContract = await zilliqa.contracts.at(fixedPriceAddress); const paymentTokenContract = await zilliqa.contracts.at( paymentTokenAddress ); const notAllowedpaymentTokenContract = await zilliqa.contracts.at( notAllowedpaymentTokenAddr