Smart Contract could transfer ether to an address, but the balance of that address does not update - node.js

I am trying to get my smart contract to transfer all its balance to another address. The transfer line doesn't throw any errors but the balance of contract does not change afterwards.
I am using web3js with ganache to test this function:
My contract:
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.12;
contract Lottery {
address payable public manager;
address payable[] public players;
constructor() {
manager = payable(msg.sender);
}
function enterLottery() public payable {
require(msg.value >= .01 ether);
players.push(payable(msg.sender));
}
function getPlayers() public view returns (address payable[] memory) {
return players;
}
function random() public view returns (uint256) {
return
uint256(
keccak256(
abi.encodePacked(block.difficulty, block.timestamp, players)
)
);
}
function pickWinner() public { // this transfer contract balance to the account
uint256 index = random() % players.length;
players[index].transfer(address(this).balance);
}
}
My test case:
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
contract = await new web3.eth.Contract(abi)
.deploy({ data: evm.bytecode.object })
.send({ from: accounts[0], gas: "1000000" })
})
describe("Lottery", () => {
it("Contract has an address? ", () => {
assert.ok(contract.options.address)
})
it("Prize pool can receive ether", async () => {
await contract.methods.enterLottery().send({ from: accounts[1], gas: "1000000", value: "10000000000000000" });
const contractBalance = await web3.eth.getBalance(contract.options.address)
const hasContractReceivedEntry = contractBalance === "10000000000000000";
assert.equal(hasContractReceivedEntry, true)
})
it("Winner can receive the prize pool", async () => {
await contract.methods.enterLottery().send({ from: accounts[1], gas: "1000000", value: "10000000000000000" });
await contract.methods.pickWinner().call();
const contractBalance = await web3.eth.getBalance(contract.options.address)
console.log(contractBalance) // the contract balance should be 0 after the pickWinner call, but it is still 10000000000000000 wei the enterLottery function gave
})
})
Edit: It is confirmed that the smart contract can run enterLottery and random() as intended

await contract.methods.pickWinner().call();
On this line, you're invoking a read-only call that doesn't update the contract state. You need to send a transaction using the .send() function - just like on the previous line.

Related

Error: No Contract deployed with name Ebay

Getting this error while testing my Ebay Contract
ITs An Ebay Smart Contract Where Products are being Auctioned
yarn run v1.22.15
warning package.json: No license field
$ "E:\Block Chain Projects\30_SMARTCONTRACTS.sol\A Way To Testing\node_modules\.bin\hardhat" test
Ebay Smart Contract
Upload Product function
This is deployer0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
1) "before each" hook for "if the base Price is less than 0 or invalid should revert"
0 passing (2s)
1 failing
1) Ebay Smart Contract
"before each" hook for "if the base Price is less than 0 or invalid should revert":
Error: No Contract deployed with name Ebay
at Object.getContract (node_modules\#nomiclabs\hardhat-ethers\src\internal\helpers.ts:447:11)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at Context.<anonymous> (test\Test.js:18:12)
error Command failed with exit code 1.
MY TEST.js
//it
//describe
//beforeEach
const { expect, assert } = require("chai");
const { ethers, getNamedAccounts } = require("hardhat");
describe("Ebay Smart Contract", async function() {
let Ebay;
let deployer;
const product = {
name: "CYCLE",
description: "gare cycles",
BasePrice: "5",
};
beforeEach(async () => {
deployer = (await getNamedAccounts()).deployer;
console.log("This is deployer" + deployer);
Ebay = await ethers.getContract("Ebay", deployer);
});
describe("Upload Product function", async function() {
it("if the base Price is less than 0 or invalid should revert", async function() {
expect(
Ebay.UploadProduct(product.name, product.description, product.BasePrice)
).to.be.revertedWith(Base_Price_cannot_be_Zero());
});
});
});
My deploy.js
const { ethers, deployments } = require("hardhat");
require("dotenv").config();
async function main() {
// const { deploy, log } = deployments;
//const { deployer } = await getNamedAccounts();
const Auction = await ethers.getContractFactory("Ebay");
const auction = await Auction.deploy();
await auction.deployed();
console.log(auction.address);
// const Auction = await deploy("Auctionbargain", {
// from: deployer,
// args: [],
// log: true,
// waitconfirmations: network.config.blockConfirmations || 1,
// });
//console.log(Auction.address);
//log("------------------------------------");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.log(error);
process.exit(1);
});
hardhatconfig.js
require("#nomicfoundation/hardhat-toolbox");
require("hardhat-deploy");
require("dotenv").config();
/** #type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
compilers: [{ version: "0.6.6" }, { version: "0.8.8" }],
},
networks: {
hardhat: {
chainId: 31337,
},
// goerli: {
// url: GOERLI_RPC_URL,
// accounts: [PRIVATE_KEY],
// chainId: 5,
// blockConfirmations: 6,
// },
},
namedAccounts: {
deployer: {
default: 0, // here this will by default take the first account as deployer
},
},
};
It was correctly deployed but gave error everytime i try to test it
I Tried using ABI in place of deployer in my test.js but it did give me the same error
Are you sure you deployed the contract on the same network on which you’re testing? it says:
Error: No Contract deployed with name Ebay
this can happen if you‘re testing on another network where the contract isn’t deployed at.
Also consider that the contract is really named Ebay. I can’t check that since you didn’t provide the solidity file.

How to test mongoose methods using sinon fakes?

I have the following arrangement of tests using sinon, mocha and chai:
type ModelObject = {
name: string;
model: typeof Categoria | typeof Articulo | typeof Usuario;
fakeMultiple: () => object[];
fakeOne: (id?: string) => object;
}
const models: ModelObject[] = [
{
name: 'categorias',
model: Categoria,
fakeMultiple: () => fakeMultiple({ creator: oneCategoria }),
fakeOne: oneCategoria
},
{
name: 'articulos',
model: Articulo,
fakeMultiple: () => fakeMultiple({ creator: oneArticulo }),
fakeOne: oneArticulo
},
{
name: 'usuarios',
model: Usuario,
fakeMultiple: () => fakeMultiple({ creator: oneUsuario }),
fakeOne: oneUsuario
}
];
const randomModel = models[Math.floor(Math.random() * models.length)];
describe(`v1/${randomModel.name}`, function () {
this.afterEach(function () {
sinon.restore();
});
context.only("When requesting information from an endpoint, this should take the Model of the requested endpoint and query the database for all the elements of that model", function () {
it.only(`Should return a list of elements of ${randomModel.name} model`, function (done) {
const fakes = randomModel.fakeMultiple();
const findFake = sinon.fake.resolves({ [randomModel.name]: fakes });
sinon.replace(randomModel.model, 'find', findFake);
chai.request(app)
.get(`/api/v1/${randomModel.name}`)
.end(
(err, res) => {
expect(res).to.have.status(200);
expect(res.body.data).to.be.an('object');
expect(res.body.data).to.have.property(randomModel.name);
expect(res.body.data[randomModel.name]).to.have.lengthOf(fakes.length);
expect(findFake.calledOnce).to.be.true;
done();
}
)
});
}}
I use this to test an endpoint that arbitrary returns information about a given model. In my controllers, I'm using a dynamic middleware to determine which model is going to be queried, for example, if the route consumed is "api/v1/categorias", it will query for Categorias model. If the route consumed is "api/v1/articulos", it will query for Articulos model, and so on.
To make the query, i use the following service:
import { Articulo } from '../models/articulo';
import { Usuario } from '../models/usuario';
import { Categoria } from '../models/categoria';
import logger from '../config/logging';
import { Model } from 'mongoose';
const determineModel = (model: string): Model<any> => {
switch (model) {
case 'articulos':
return Articulo;
case 'usuarios':
return Usuario;
case 'categorias':
return Categoria;
default:
throw new Error(`Model ${model} not found`);
}
};
export const getInformation = async (schema: string, page: number, limit: number) => {
try {
const model = determineModel(schema);
const data = await model.find().skip((page - 1) * limit).limit(limit);
const dataLength = await model.find().countDocuments();
return {
data,
total: dataLength,
};
} catch (err) {
logger.error(err);
console.log(err);
throw err;
}
};
The problem here lies when running my tests, it seems that is unable to run the .skip() and .limit() methods for my model.find()
error: model.find(...).skip is not a function
TypeError: model.find(...).skip is not a function
I think that I need to fake those methods, because when running the same test without skip and limit, it works as a charm. My problem lies in the fact that I don't know how to fake those, or to see if my guess is correct.
As a note, I have default params for the variables page and limit (1 and 15 respectively) so I'm not passing empty values to the methods.

How to return an Array in an struct?

I wrote a contract that keeps an array of the "wRequest", which is the type of a constructer I wrote. But when I add something to the list and try to return it, I can only get the arguments that are not arrays.
what seems to be problem?
Contract :
pragma solidity ^0.8.0;
contract s {
WRequest[] public WRequestList;
struct WRequest {
address receiver;
address[] tokenAddresss;
uint256[] amounts;
uint approved;
}
function submitWithdrawRequest(address receiver, address[] memory tokenAddresss, uint256[] memory amounts) public {
WRequest memory request = WRequest(receiver, tokenAddresss, amounts, 0);
WRequestList.push(request);
}
}
deploy.js
const hre = require("hardhat");
async function main() {
const Lock = await hre.ethers.getContractFactory("s");
const lock = await Lock.deploy();
const [owner, account1 ,account2] = await ethers.getSigners();
await lock.deployed();
console.log("deployed")
await lock.submitWithdrawRequest(owner.address, [account1.address], [7])
const list = await lock.WRequestList(0);
console.log(list);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Result
[
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
BigNumber { value: "0" },
receiver: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
approved: BigNumber { value: "0" }
]
It only includes the receiver address and approved, and not the array of tokenAddresss and amounts.
You can write a function for this
function getWRequest(uint _index) public view returns(WRequest memory) {
return WRequestList[_index];
}

ms bot framework onMembersAddedActivity does not get invoked using nodejs

i am using nodejs google cloud functions with ms bot framework. I have the invoke code looks like below:
const BotFrameworkAdapter = require('botbuilder').BotFrameworkAdapter
const { TeamsConversationBot } = require('./flashmsteamsbot');
const msadapter = new BotFrameworkAdapter({
appId: 'XXX',
appPassword: 'XXX'
});
const msteamsbot = new TeamsConversationBot()
const app = express();
app.post('/api/messages', (req:any, res:any) => {
msadapter.processActivity(req, res, async (context:any) => {
// Route to main dialog.
await msteamsbot.run(context)
});
});
the teams class looks like below:
const {
TurnContext,
TeamsActivityHandler,
CardFactory,
AttachmentLayoutTypes,
ActionTypes
} = require('botbuilder');
class TeamsConversationBot extends TeamsActivityHandler {
constructor() {
super();
this.onMessage(async (context:any, next:any) => {
TurnContext.removeRecipientMention(context.activity);
let msg = context.activity.text
const senderId = context.activity.from.aadObjectId
switch (msg) {
case 'don1':
await this.don1(context, keyword.trim(), userKey)
break;
default:
await this.help(context)
break;
}
await next();
});
this.onMembersAddedActivity(async (context:any, next:any) => {
functions.logger.log("start of onMembersAddedActivity", context)
context.activity.membersAdded.forEach(async (teamMember:any) => {
if (teamMember.id !== context.activity.recipient.id) {
await context.sendActivity(`Welcome to the team ${ teamMember.givenName } ${ teamMember.surname }`);
}
});
await next();
});
}
Whenever i send a message to the bot the this.onMessage is getting invoked. However, when i add a new member to a group where my bot is already present, the onMembersAddedActivity is not invoked. what i am missing here?
This is partially an issue in our docs and code comments, which I addressed here and here, respectively. The other issue is that you're using <method>Activity() instead of <method>Event().
The latest instructions are in the code comments, which just got merged, but basically,
Developers may handle Conversation Update activities sent from Microsoft Teams via two methods:
Overriding methods starting with on.. and not ending in ..Event() (e.g. onTeamsMembersAdded()), or instead
Passing callbacks to methods starting with on.. and ending in ...Event() (e.g. onTeamsMembersAddedEvent()),
to stay in line with older {#link ActivityHandler} implementation.
Developers should use either #1 or #2, above for all Conversation Update activities and not both #2 and #3 for the same activity. Meaning,
developers should override onTeamsMembersAdded() and not use both onTeamsMembersAdded() and onTeamsMembersAddedEvent().
Developers wanting to handle Invoke activities must override methods starting with handle...() (e.g. handleTeamsTaskModuleFetch()).
So, for you, you can either:
constructor() {
[...]
// This is passing in a callback
this.onTeamsMembersAddedEvent(async (
membersAdded: TeamsChannelAccount[],
teamInfo: TeamInfo,
context: TurnContext,
next: () => Promise<void>) => {
functions.logger.log("start of onMembersAddedActivity", context)
context.activity.membersAdded.forEach(async (teamMember:any) => {
if (teamMember.id !== context.activity.recipient.id) {
await context.sendActivity(`Welcome to the team ${ teamMember.givenName } ${ teamMember.surname }`);
}
});
await next();
});
}
or
constructor() {
[...]
}
[...]
// This is an override
async onTeamsMembersAdded(context: TurnContext): Promise<void> {
functions.logger.log("start of onMembersAddedActivity", context)
context.activity.membersAdded.forEach(async (teamMember:any) => {
if (teamMember.id !== context.activity.recipient.id) {
await context.sendActivity(`Welcome to the team ${ teamMember.givenName } ${ teamMember.surname }`);
}
});
}

How to create manual mock with jest in nodejs app for mailgun-js?

code in app/src/emails/account.js
const mailgun = require("mailgun-js");
const DOMAIN = "sxxxxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org";
const mg = mailgun({apiKey: process.env.MAILGUN_API_KEY, domain: DOMAIN});
const sendWelcomeEmail = async (email, name) => {
const dataForMail = {
to: email,
from: 'zzz#xxx.com',
subject: 'Testing!',
text: `Welcome to the app, ${name}, let me know how you get along with the app.`,
}
mg.messages().send(dataForMail)
}
code in app/test/__mocks__/mailgun-js:
module.exports = {
messages() {
},
send() {
},
mailgun() {
}
}
Whenever I run jest, it says 'mailgun is not a function'. How can I create a manual mock for this constructor?
My mock is an object, not a function. Test doubles need to match the interface of the thing they're replacing; in this case, it needs to be a function that returns an object with a messages method (which returns an object with the send method). My mock doesn't match that structure at all. (Big thanks to #jonrsharpe for informing this)
mailgun-js.js needs to be edited the same way.
module.exports = function(apiKey, domain) {
const object2 = {
send() {
}
}
const object1 = {
messages () {
return object2
}
}
return object1
}

Resources