Sending Ether Between Two Contracts

Written by quijanoflores | Published 2017/02/17
Tech Story Tags: ethereum | blockchain | smart-contracts | solidity

TLDRvia the TL;DR App

What is the real world application?We have Providers that provide services to people, such as internet, telephone, water, food and shelter. People who want to use these services can subscribe to these services. Providers can charge debts that Users owe for utilising the service. Users can clear their debts by sending ether to the Provider.

Providers have jobs that they keep track of in order to provide their services and hire staff to complete those jobs. Staff Users can get paid by withdrawing ether from the Provider.

This post will cover the engagement of a Staff user completing a job and withdrawing ether from the Provider contract in order to help provide clarity around Invalid JUMP errors with sending ether to a contract. We’ll use the Singularity Kitchen as an example. I’ll first present the tests and then smart contracts.

Disclaimercontracts are not audited and contain security flaws, do not use for any other purpose than learning.

Tests

it('init', function() {return Kitchen.new("SingularityKitchen", "Healthy living for the next gen", {from:_owner}).then(function(kitchenContract) {if(kitchenContract.address){kitchen = kitchenContract;} else {throw new Error("no contract address");}return true;})})

it('pay-out-job', function(){var jobName = "Single Customer Order";assert.equal(balance(kitchen.address), web3.fromWei(1100000000000000000,"ether"))return kitchen.payOutJob(staff.address, jobName, {from:_owner}).then(function(txHash){return kitchen.staffList(staff.address);}).then(function(staffInfo){//this is the payout stored in a structassert.equal(staffInfo[2].toString(), 300000000000000000);return kitchen.collectPayout(staff.address, {from:_staff1}).then(function(txHash){return kitchen.staffList(staff.address);}).then(function(staffInfo){console.log(balance(kitchen.address))assert.equal(staffInfo[2].toString(), 0);assert.equal(balance(staff.address), web3.fromWei(300000000000000000, "ether"));assert.equal(balance(kitchen.address), web3.fromWei(800000000000000000, "ether"));})})})

I’ll walk through these two tests here, the rest of tests for the contract can be found on the github repo.

In ‘init’, we create a new instance of the Kitchen contract by using the new method with some arguments and data about who is sending this transaction. Creating a new instance of a smart contract returns the Smart Contract and we can check if the smart contract has an address, if it does then we set it to a global variable ‘kitchen’. If the contract does not have an address then we throw a new error.

In ‘pay-out-job’, we first check to see that the kitchen contract has ether to pay people. Then we try to call on the payOutJob function from the kitchen contract. It takes an address of a User contract and the jobName that was completed. This transaction is called upon by some kind of owner like an authorized user who is allowed to engage with the contract to pay people for completing jobs. Once the payoutjob function will return a txHash, we’re not going to do anything with the txHash, but we could console log it and see the data that it contains. We’re going to call on a public attribute of the contract ‘staffList’ which is a mapping data type so we’ll pass in the address of the User contract. Passing in the address of the User contract into staffList will return a struct data type that contains a field for storing information about the payout to the Staff User. We can access contents of the struct by calling upon them like elements in an array. StaffInfo[2] is a uint256 data type and we can call a toString method to get the amount to be paid to the User contract in Wei. Here we’re asserting that it is 300000000000000000 Wei or the rate of the “Single Customer Order” Job that is set in the Kitchen contract. When the Staff User is ready to collect their payout, they can call on the collectPayout function from the Kitchen contract. We get back a transaction hash and call on the staffList so that we can check that there has been a change in the struct. We check that the payout has been set to 0 and that the balances of both the Kitchen and the Staff contract have changed appropriately.

Smart Contracts

The two smart contracts that we need in order to provide autonomy for this engagement are a User contract and a Provider contract. I’m building off of these contract from this YouTube tutorial series by Shlomi Zeltsinger.

contract User {string public userName;address public owner;

mapping (address => Service) public services;

struct Service{bool active;uint lastUpdated;uint debt;}

function User(string _name) payable {userName = _name;owner = msg.sender;}

function payable {}

function registerToProvider(address _providerAddress){services[_providerAddress] = Service({active:true,lastUpdated: now,debt: 0});}

function setDebt(uint256 _debt){if(services[msg.sender].active){services[msg.sender].lastUpdated = now;services[msg.sender].debt += _debt;} else {throw;}}

function clearDebt() returns (bool result){if (services[msg.sender].active){services[msg.sender].lastUpdated = now;services[msg.sender].debt = 0;} else {throw;}}

function unsubcribe(address _providerAddress){if(services[_providerAddress].debt == 0){services[_providerAddress].active = false;} else {throw;}}

contract Provider {string public providerName;string public description;

mapping (address => staffUser) public staffList;mapping (bytes32 => Job) public jobs;

struct staffUser{bool active;uint lastUpdated;uint256 payout;}

struct Job {bytes32 name;uint256 rate;}

function Provider(string _name, string _description) {providerName = _name;description = _description;}

function setDebt(uint256 _debt, address _userAddress) {User person = User(_userAddress);person.setDebt(_debt);}

function recievePayment(address _userAddress) payable returns (bool result) {User person = User(_userAddress);person.clearDebt();return true;}

function addStaff(address _userAddress) {staffList[_userAddress] = staffUser({active:true,lastUpdated: now,payout: 0});}

function addJob(bytes32 _name, uint256 _rate) {jobs[_name] = Job({name:_name,rate:_rate});}

function updateJobRate(bytes32 _name, uint256 _rate){jobs[_name].rate = _rate;}

function payOutJob(address _userContractAddress, bytes32 _jobName){staffList[_userContractAddress].payout += jobs[_jobName].rate;}

function collectPayout(address _userContractAddress) {uint256 _amount = staffList[_userContractAddress].payout;staffList[_userContractAddress].payout = 0;if(!_userContractAddress.send(_amount)){throw;}}

}

I’ll highlight some functions in the contracts that are relevant to this post. Here’s a really helpful example of sending ether.

In our test, the ‘staff’ variable is an instance of the User contract. The big function to highlight and the main reason for writing this code review is the payable function. This is a fallback function. A fallback function is a function that doesn’t run any anything. More about fallback functions can be found here.

If a contract is receiving ether it needs to have this fallback function otherwise you’ll receive an Invalid Jump error.

invalid JUMP means you called code that didn’t exist, or there was an array out of bounds. Calling code that doesn’t exist can happen in many forms: 1) There’s no code at the address of your contract, for some reason, 2) you’re calling a contract function that isn’t actually available, 3) your contract is calling another contract or function that’s not available. The easiest way right now is to comment out code until the error goes away, then slowly let it back in until you figure out the correct line. The next big feature for Truffle will be getting stack traces for these messages. — Tim

function payable {}

The payOutJob function takes an User contract address and the name of a job and all this function does is gets the payout field from the staffUser struct by calling on the staffList mapping using the User contract address and then updates the payout by getting the job rate field from the Job struct by calling on the jobs mapping using the jobName.

The collectPayout function takes a User contract address and sets a variable _amount to the payout field from the staffUser struct by calling on the staffList mapping using the User contract address. Then we update the payout field to 0 and then try to send the amount of ether to the user contract using ‘_userContractAddress.send(_amount)’. If we’re not able to send the ether, then throw will revert everything in our transaction and the payout field will remain unchanged.

Thanks Tim Coulter and Nikita Fuchs for providing clarity on Invalid Jumps.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.

To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!


Published by HackerNoon on 2017/02/17