I have this contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract CampaignFactory {
address[] public deployedCampaigns;
function createCampaign(uint minimum) public {
address newCampaign = address(new Campaign(minimum, msg.sender));
deployedCampaigns.push(newCampaign);
}
function getDeployedCampaigns() public view returns(address[] memory) {
return deployedCampaigns;
}
}
contract Campaign {
struct Request {
string description;
uint value;
address recipient;
bool complete;
uint approvalCount;
mapping(address => bool) approvals;
}
Request[] public requests;
address public manager;
uint public minimumContribution;
mapping(address => bool) public approvers;
uint public approversCount;
modifier restricted() {
require(msg.sender == manager);
_;
}
constructor(uint minimum, address creator) {
manager = creator;
minimumContribution = minimum;
}
function contribute() public payable {
require(msg.value > minimumContribution);
approvers[msg.sender] = true;
approversCount++;
}
function createRequest(string calldata description, uint value, address recipient) public restricted {
Request storage newRequest = requests.push();
newRequest.description = description;
newRequest.value = value;
newRequest.recipient = recipient;
newRequest.complete = false;
newRequest.approvalCount = 0;
}
function approveRequest(uint index) public {
Request storage request = requests[index];
require(approvers[msg.sender]);
require(!request.approvals[msg.sender]);
request.approvals[msg.sender] = true;
request.approvalCount++;
}
function finalizeRequest(uint index) public restricted {
Request storage request = requests[index];
require(request.approvalCount > (approversCount / 2));
require(!request.complete);
payable(request.recipient).transfer(request.value);
request.complete = true;
}
}
And test:
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const compiledFactory = require('../ethereum/build/CampaignFactory.json');
const compiledCampaign = require('../ethereum/build/Campaign.json');
let accounts;
let factory;
let campaignAddress;
let campaign;
beforeEach(async() => {
accounts = await web3.eth.getAccounts();
web3.eth.getBalance(accounts[0]).then(result => console.log(result));
factory = await new web3.eth.Contract(compiledFactory.abi)
.deploy({ data: compiledFactory.evm.bytecode.object })
.send({ from: accounts[0], gas: '1000000' });
await factory.methods.createCampaign('100').send({
from: accounts[0],
gas: '1000000'
});
[campaignAddress] = await factory.methods.getDeployedCampaigns().call();
campaign = await new web3.eth.Contract(
JSON.parse(compiledCampaign.interface),
campaignAddress
);
});
describe('Campaigns', () => {
it('deploys a factory and a campaign', () => {
assert.ok(factory.options.address);
assert.ok(campaign.options.address);
});
});
When I run the test, I get VM Exception while processing transaction: out of gas, but it logs that accounts[0] balance is 100000000000000000000. The problem occurs where the factory is assigned a new contract instance saying there isn't enough gas, while there clearly is.
Where you have .send({ from: accounts[0], gas: '1000000' })
, you are specifying that a maximum of 1000000
gas can be used to process that transaction, regardless of the balance of the from
account.
If you're deploying a large enough contract, this amount will be too small. Increasing this amount should resolve the issue.
Note, if the number you specify for gas is larger than the amount actually used, the difference will be refunded.