I am trying to figure out how transactions between contracts work and what you can and cannot do. I want to have a contract that makes some logical decision about who to pay when poked by another contract.
Here is the contract i've been trying to emulate this behaviour with:
pragma solidity ^0.4.17;
contract DecisionMaker{
Sender sd;
function someLogicToDecidePayment(address receiver, address sender) public {
//Some logic then:
sd = Sender(sender);
sd.send_transfer(receiver, 1000);
}
}
contract Sender {
DecisionMaker dm;
constructor() public payable{}
function send(address _receiver) payable {
_receiver.call.value(10000000).gas(20317)();
}
function send_transfer(address _receiver, uint _amount) payable public {
_receiver.transfer(_amount);
}
function placeBuy(address receiver, address decisionMaker) public {
dm = DecisionMaker(decisionMaker);
dm.someLogicToDecidePayment(receiver, address(this));
}
}
contract Receiver {
uint public balance = 0;
constructor() public payable{}
function () payable {
balance += msg.value;
}
}
When trying to run placeBuy from the Sender Contract. This will create an instance of DecisionMaker so the function can run someLogicToDecidePayment(). Following some logic to decide who to pay i would like for the contract to invoke send_transfer from the Senders Contract to send some ether to Receiver contract. However this fails, with the following error:
VM error: revert.
revert The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value. Debug the transaction to get more information.
If I am to use the function send within the Sender Contract instead of send_transfer, the transaction goes through, but the balance of Receiver is not increased. It seems that I am not able to send value with a transaction when using contracts. Could anyone help me understand why I can't successfully transfer ether from one contract to another with either of those methods?
Thank you in advance.
EDIT
I have added a payable constructor to both the sender and receiver as suggested by Lượng. Unfortunately, I was still unable to send ether between the contracts using the transfer function - send_transfer() - however was successful to using the send() method. I have attached my test script for anyone that would like to test this out in the future:
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const compiledDecision = require('../ethereum/build/DecisionMaker.json');
const compiledReceiver = require('../ethereum/build/Receiver.json');
const compiledSender = require('../ethereum/build/Sender.json');
let accounts;
let decision;
let sender;
let receiver;
let confirmation;
let post_balance;
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
decision = await new web3.eth.Contract(JSON.parse(compiledDecision.interface))
.deploy({ data: compiledDecision.bytecode })
.send({ from: accounts[0], gas: '1999999' });
receiver = await new web3.eth.Contract(JSON.parse(compiledReceiver.interface))
.deploy({ data: compiledReceiver.bytecode })
.send({ from: accounts[0], gas: '1999999' });
sender = await new web3.eth.Contract(JSON.parse(compiledSender.interface))
.deploy({ data: compiledSender.bytecode })
.send({ from: accounts[0], gas: '1999999' });
});
describe('Testing Contracts', () => {
it('Attempts to make a transaction from sender to receiver through DecisionMaker', async () =>{
let pre_balance = await receiver.methods.balance().call();
await sender.methods.deposit().send({
from: accounts[0],
gas: 1000000,
value: 1000000
});
let senderBalance = await web3.eth.getBalance(sender.options.address);
console.log('senders contract balance', senderBalance);
console.log('pre balance of receiver (variable)', pre_balance);
try{
let confirmation= await sender.methods.placeBuy(receiver.options.address, decision.options.address).send({
from: accounts[0],
gas: 1999999
})
post_balance = await receiver.methods.balance().call();
}catch(err){
console.log(err);
}
let balanceOfAddress = await web3.eth.getBalance(receiver.options.address);
senderBalance = await web3.eth.getBalance(sender.options.address);
console.log('confirmation of transaction', confirmation);
console.log('post balance of receiver (variable)', post_balance);
console.log('balance of receiver contract address', balanceOfAddress);
console.log('senders balance post transaction', senderBalance);
console.log('finished test');
});
});
I am still curious and would appreciate if anyone could give a suggestion for why the send_transfer() function doesn't work but the send() does :)