Can't pass multiple variables via metamask - async error - solidity

I am getting an error in metamask "the MetaMask Web3 object does not support synchronous methods like eth_sendTransaction without a callback parameter".
I am trying to pass three parameters to a solidity function, but the callbacks I've tried to insert don't seem to work?
Below is the js code, just a button and 3 labels for the variables in the HTML
$('#proposalbutton').click(function() {
log("Calling add proposals...");
community.newProposal($("#address").text(), $("#coinAmount").val(),
$("#jobDescription").text()), (err, res) =>{
if (!err) {
log("Proposals call executed successfully.");;
}
}
});
Can anyone point out what I'm doing wrong? I can't figure out the callbacks.
Below is the solidity function header
function newProposal(
address beneficiary,
uint coinAmount,
string jobDescription
)
onlyMembers public
returns (uint proposalID)

Callbacks are the last parameter sent after your Solidity contract parameters. It looks like you're closing newProposal after just the function parameter list.
community.newProposal($("#address").text(), $("#coinAmount").val(),
$("#jobDescription").text(), (err, res) => {
if (!err) {
log("Proposals call executed successfully.");;
}
});
Additional note: Transaction methods won't return a value. You won't get proposalID in your response. Use either an event or store the result in your contract state and then use a view function to return it.

Related

How to test a solidity payable function with hardhat

I have the following smart contract function:
function safeMint(address to, uint256 tokenId) public onlyOwner payable {
require(msg.value >= mintPrice, "Not enough ETH to purchase NFT; check price!");
_safeMint(to, tokenId);
}
and the following test function in chai to test it.
describe("mint", () => {
it("should return true when 0.5 ethers are sent with transaction", async function () {
await contract.deployed();
const cost = ethers.utils.parseEther("0.1");
await contract.safeMint("0x65.....",1,cost
});
However the test function is not working and gives me an error on cost.
Error: "Type 'BigNumber' has no properties in common with type 'Overrides & { from?: PromiseOrValue; }'." I fail to understand where the error lies.
Try this, it's the valid syntax to send value with the call:
await contract.safeMint("0x65.....", 1, {value: cost});
I had a similar problem while testing a payable function, it kept saying 'object is not an instanceof of BigInt'. How I solved this problem and ensured testing ran smoothly was to test the balance of the receiver (in your case, the 'to' address), to make sure the balance has been updated. That is, if the safeMint function was successful during test, the 'to' address should be increased by 1.
You can test by:
const balOfToAdress = await contract.balanceOf(to.address)
expect(balOfToAddress).to.equal(1)
Note: the 'expect' above is gotten by requiring chai at the top of your code i.e.
const {expect} = require('chai')
and, you have to test specifically for the balance of 'to' address (Here, I just assumed the initial balance of mints on 'to' address is 0).

How would you call a contract method that takes Enum type via hardhat?

In your contract, if you have method that receives an Enum type, how would you pass the arguments from hardhat script?
contract SomeContract {
enum WinStatus {
PENDING,
LOST,
WON
}
WinStatus status;
function updateWinStatus(WinStatus _status) public {
status = _status;
}
}
// in your hardhat script
...
await someContract.updateWinStatus() // how should i call it. bare in mind hardhat is setup using javascript not typescript in my case.
i tried passing a number, hoping it will get it by order(index). But I am getting 'invalid BigNumber value'. Also, I tried passing a string like "PENDING" or "WinType.PENDING" :thinking:
Javascript natively doesn't support very large numbers (up to the uint256 type supported in Solidity), so Ethers.js (included in Hardhat) accepts a BigNumber instance instead.
const myNumber = ethers.BigNumber.from("0") // pass the numeric value as a string
await someContract.updateWinStatus(myNumber) // pass the BigNumber instance

Testing ChainlinkClient callbacks - how to bypass recordChainlinkFulfillment?

I've got a Chainlink client contract which makes a DirectRequest to an oracle. The oracle does its thing and then returns the answer via the typical callback selector passed in via the ChainlinkRequest. It all works well, but I'd like to write some tests that test the callback implementation
My client contract is as follows:
contract PriceFeed is Ownable, ChainlinkClient {
function updatePrice() onlyOwner returns (bytes32 requestId) {
// makes Chainlink request specifying callback via this.requestCallback.selector
}
function requestCallback(bytes32 _requestId, uint256 _newPrice) public
recordChainlinkFulfillment(_requestId) {
price = _newPrice;
}
}
The problem arises when the test code calls requestCallback(...) and the code hits the recordChainlinkFulfillment(...) modifier. The ChainlinkClient complains that the requestId being passed in by the test below isn't in the underling private pendingRequests mapping maintained by the ChainlinkClient.
The simplified version of ChainlinkClient looks like this:
contract ChainlinkClient {
mapping(bytes32 => address) private pendingRequests;
modifier recordChainlinkFulfillment(bytes32 _requestId) {
require(msg.sender == pendingRequests[_requestId], "Source must be the oracle of the request");
delete pendingRequests[_requestId];
emit ChainlinkFulfilled(_requestId);
_;
}
}
My Foundry/Solidity test is as follows:
contract PriceFeedTest is Test {
function testInitialCallback() public {
priceFeed.requestCallback("abc123", 1000000); // fails on this line
assertEq(1000000, priceFeed.price(), "Expecting price to be 1000000");
}
}
The code fails on first line of the testInitialCallback() line with: Source must be the oracle of the request
How can I trick the ChainklinkClient into allowing my callback to get past the modifier check? AFAIK I can't access and pre-populate the private pendingRequests mapping. Is there another way?
I know that Foundry provides Cheatcodes to help in testing and there's a stdstorage cheatcode, but I'm not familiar on how to construct a call to stdstorage to override pendingRequests if thats even possible with a cheatcode.
contract PriceFeedTest is Test {
function testInitialCallback2() public {
stdstore
.target(address(priceFeed))
.sig("pendingRequests()")
.with_key("abc123")
.checked_write(address(this));
priceFeed.requestCallback("abc123", 1000000);
assertEq(1000000, priceFeed.price(), "Expecting price to be 1000000");
}
}
The above code throws the following error: No storage use detected for target
Any help would be greatly appreciated. Many thanks.
When you execute the updatePrice function in your test, you should be able to strip out the requestId from the transaction receipt event. Once you have that, you can then use it in your call to requestCallback. Check out this example unit test from the hardhat starter kit for an example of this

Is there any way to catch an event fired within another contract which is called by low level 'call' opcode from main contract in solidity

I have a Multisig contract that when reaches the minimum quorum, it can execute a low level call Transaction which may be held on another contract.
function _execute(Transaction storage transaction) internal {
//some code
// solhint-disable-next-line
(bool success, ) = transaction.target.call{value: transaction.value}(callData); // FIRES AN EVENT IN OTHER CONTRACT
if (success) {
emit TransactionExecuted( // FIRES SECOND
//some code
);
} else {
emit TransactionFailed(
//some code
);
//some code
}
}
My execute function fires an event after execution of the Transaction (call) whether it was successful or not, in the meanwhile if call function request has an event to fire, I can catch the event fired by the contract, but event parameters are not there, The second contract which is called by _execute() is written as follows:
function _addMember(
address memberAddress,
bytes32 memberName,
Membership _membership
)
internal
{
//some code
// Fire an event
emit MembershipChanged(memberAddress, true, _membership); // FIRES FIRST
}
The following is the test written in typescript, I can get the event fired on called contract, but there is no data in it
it("should contain two events from previous transaction, adding a new core member and running a Transaction by multisig", async () => {
//r is the receipt of the caller (multisig) contract
expect(r.events!.length).to.be.eq(2); // MembershipChanged, TransactionExecuted
//NOTE: r.events![0].address === memberReg.address // memReg is the callee contract
/*THE FOLLOWING DOESN'T CONTAIN EVENT DATA NOR TOPICS OF memReg CONTRACT*/
expect(r.events![0].event).to.be.eq("MembershipChanged"); //faild
expect(r.events![0].args!.member).to.be.eq(coreCandidateAddr) //faild
expect(r.events![0].args!.isMember).to.be.true; //fails
expect(r.events![0].args!.membership).to.be.eq(Membership.Core); //faild
/* THE FOLLOWING WORKS WELL */
expect(r.events![1].event).to.be.eq("TransactionExecuted"); //passed
//some code
})
I guess it would be possible to catch those events in production easily by listening to that deployed contract, but I don't know how to do this in test environment
Ok, Thanks to #bbbbbbbbb here is the simple solution, it should listen on before the transaction call
memberReg.once("MembershipChanged", (member, isMember, membership, event) => {
expect(event.event).to.be.eq("MembershipChanged"); //passed
expect(member).to.be.eq(coreCandidateAddr); //passed
expect(isMember).to.be.true; //passed
expect(membership).to.be.eq(Membership.Core); //passed
});
r = await awaitTx(multisig.execute(id)); // execution happens here
Your functions _execute and _addMember are marked as internal, it should be mark as external or public to be called in testing environment, or create a public function that call it.
If you are using hardhat the easy way is using the testing pattern for events suggested by hardhat documentation, this also works for low level calls.
const tx = await YourContract.execute(foo);
//Check that the event fired by a low level call is correct
//withArgs receive the expected values for the event
expect(tx).to.emit(YourContract, "MembershipChanged").withArgs(foo, bar...);
For more info check the implementation for hardhat docs: https://hardhat.org/tutorial/testing-contracts#full-coverage

How to correctly utilise redux-saga putResolve

I'm trying to sequence my API calls so that I can call an endpoint, wait for it to succeed, then select the required value out of the state.
I'm very new to redux-saga and am finding it difficult to understand how to achieve this!
What I've tried:
So we try and block whilst we perform an API call:
const resp = yield putResolve(assistantActions.fetchSustainability());
Later in the code we select the updated value out of the state:
const assistant = yield select(getAssistant);
The action that is called looks like:
export const fetchSustainability = () => ({ type: FETCH_SUSTAINABILITY });
In the saga we:
yield takeEvery(FETCH_SUSTAINABILITY, fetchSustainability);
Then the function returns a promise:
function fetchSustainability() {
return axios.get(`${url}/sustainability`);
}
I'm not sure that I've set this up any where near right as I've made lots of changes trying to get it working!
The select statement executes immediately and as the call isn't blocked the state isn't updated correctly.
Is my approach correct? If not is someone able to provide a simple working example on how to achieve a blocking call.
So the second parameter to takeEvery needs to be a generator function;
within that you can call your fetchSustainability
function* generator() {
yield call(fetchSustainability); // calls the actual function
}
// in the saga
yield takeEvery(FETCH_SUSTAINABILITY, generator);
your action fetchSustainability should return a Promise.
Here's an example to highlight the difference between put and putResolve.
https://codesandbox.io/s/redux-saga-example-x77jb
The example uses thunkMiddleware