Ethereum Tutorials

SMART CONTRACTS

Calling Other Contracts' Functions

So far we have a "ShareHolder" contract that receives payments from an "attached" "Duke of Ether" contract, distributing the profit among share holders. We expect that these people will promote an "attached" business, as their profit depends directly from it. However, no mater how profitable "Duke" can be, it is just a single contract. If we create another profitable contract and make it a child of a different "ShareHolder", we will have two contracts instead of one, and potential investor will have to choose... which is bad, especially if we create ten contracts... or more... Can we "attach" multiple contracts to the same "ShareHolder"? This way we can allow the investor to buy shares of a single "ShareHolder" contract and to enjoy dividends from all "attached" contracts. The rest of the logic remains the same: being interested in all "attached" contracts' profit, the share holder will likely promote all of them. However, in case of multiple contracts, we can not inherit all of them from ShareHolder, not unless we deploy them all simultaneously. Instead, we need to deploy the ShareHolder first, and to attach more and more contracts to it as we create them. 82. Calling one contract from another is not that much different from calling functions of a contract from web3.js: we need to know the name and arguments of a function we intend to call and we need to know the contract's address. As for address, we know it as ShareHolder contract is going to be deployed first. As for the function prototype, we have to declare it in a calling contract; in other words, we need what in C language is called a forward declaration. Solidity compiler requires this information so it knows what to call. Think of it as of function interface if you want. contract ShareHolder { function addToShareHoldersProfit(uint nProfit) { ... } } contract DukeOfEther { address m_addrShareHolder = 0xEB1e2c19bd833b7f33F9bd0325B74802DF187935; ... function becomeDuke(string strNickName) payable { ... ShareHolder contractShareHolder = ShareHolder(m_addrShareHolder); contractShareHolder.addToShareHoldersProfit(nFee); ... } } It is also possible to issue a call: address ShareHolder = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2; nameReg.call(bytes4(keccak256("addToShareHoldersProfit(uint256)")), nFee); Both examples above, of course, were just theoretical: we can not just call ShareHolder's addToShareHoldersProfit as we did before in this tutorial: ShareHolder is now a separate contract, and we need to send it money, as it does not have access to Duke's funds anymore. To transfer ether between contracts: targetAddress.call.gas(200000).value(this.balance)(); will call the fallback function. targetAddress.call.gas(200000).value(this.balance)(bytes4(sha3("pay()"))); will call the pay function. However, as we know, "call" should be avoided: The general syntax for calling a function in another contract with arguments and sending funds is: address.func.value(amount)(arg1, arg2, arg3) func needs to have the payable modifier ShareHolder contract (simplified to fit the topic): pragma solidity ^0.4.15; contract ShareHolder { function addToShareHoldersProfit( ) payable { // ... } } DukeofEther contract (simplified to fit the topic): pragma solidity ^0.4.15; // This is a forward declaration to let Solidity know that ShareHolder exists and it has addToShareHoldersProfit function contract ShareHolder { function addToShareHoldersProfit( ) payable { // ... } } contract DukeOfEther { address m_addrShareHolder = 0xeC0B68f26222982Bc86Eb33223f1EB6a83B99d54; function becomeDuke( ) payable { // ... ShareHolder contractShareHolder = ShareHolder(m_addrShareHolder); contractShareHolder.addToShareHoldersProfit.value(msg.value)(); // ... } } Now use Mist to call Duke's "becomeDuke" function. Send it 1 ether. The money are transfered to ShareHolder. 83. Our ShareHolder contract needs to keep track of payments in order to present it to share holders. Particularly, it would be nice to know which contract paid what amount of money. We can, of course, use contracts' addresses for it, but a contract also has name and it is easier (for humans) to work with it. So let's modify the code to pass the name of a paying contract: pragma solidity ^0.4.15; // This is a forward declaration to let Solidity know that ShareHolder exists and it has addToShareHoldersProfit function ShareHolder contract (simplified to fit the topic): pragma solidity ^0.4.15; contract ShareHolder { string m_strLastPaid; function addToShareHoldersProfit(string strLastPaid) payable { m_strLastPaid = strLastPaid; } } DukeofEther contract (simplified to fit the topic): pragma solidity ^0.4.15; contract ShareHolder { function addToShareHoldersProfit(string) payable { // ... } } contract DukeOfEther { address m_addrShareHolder = 0xeC0B68f26222982Bc86Eb33223f1EB6a83B99d54; function becomeDuke( ) payable { // ... ShareHolder contractShareHolder = ShareHolder(m_addrShareHolder); contractShareHolder.addToShareHoldersProfit.value(msg.value)("Duke of Ether"); // ... } } 84. In Web UI for our contracts that we have created in previous chapters, we assumed that we know all our contracts: ShareHolder and DukeOfEther. What if there will be more? What if we manage to turn our contract to a full scale stock exchange and people we do not even know will "attach" their contracts to our ShareHolder? Should we insist that those contracts comply to some standard, providing us data, so that we could automatically add their tabs to our web site (same way they do in "real" stock exchanges)? This is a situation that happens in almost any project: you started building a screw driver and ended up with a space ship, or so it seems. I believe a developer should be able to stop at this point, otherwise another monster project will be created, and there are plenty of them already. So, let's be realistic. 1. We are not building a clone of New York Stock Exchange. 2. We are not going to provide Web UI to 3rd party contracts. However, we can (and definitely should) list contracts "attached" to our ShareHolder contract, with their historical profits and contract names... Why don't we also keep the contract page's URL so that the contract owner could manage contract's charts and tutorials and not us? pragma solidity ^0.4.15; contract ShareHolder { function addToShareHoldersProfit(string strContractName, string strContractUrl) payable { } } DukeofEther contract (simplified to fit the topic): pragma solidity ^0.4.15; contract ShareHolder { function addToShareHoldersProfit(string, string) payable { // ... } } contract DukeOfEther { address m_addrShareHolder = 0xeC0B68f26222982Bc86Eb33223f1EB6a83B99d54; function becomeDuke( ) payable { // ... ShareHolder contractShareHolder = ShareHolder(m_addrShareHolder); contractShareHolder.addToShareHoldersProfit.value(msg.value)("Duke of Ether", "duke-of-ether.com/duke_of_ether.htm"); // ... } } Of course, we have to keep in mind that Web is a very dangerous place, and we have to strip the strContractUrl of potentially dangerous parts, before presenting it to visitors of our Web site. 85. Share Holder contract, revisited. 86. This is a rather complex contract, so in order to test it, let's create 3 "attached" contracts: pragma solidity ^0.4.15; contract ShareHolder { function addToShareHoldersProfit(string, string) payable { // ... } } contract One { address m_addrShareHolder = 0x6c68df17Cf93dECf3F33564EC5c0008BD1e183A6; function becomeDuke( ) payable { // ... ShareHolder contractShareHolder = ShareHolder(m_addrShareHolder); contractShareHolder.addToShareHoldersProfit.value(msg.value)("One", "http://donor_1.com"); // ... } } Deploy them (do not forget to change the m_addrShareHolder to an address your contract has) and do some basic testing. 87. Using ShareHolder together with ERC20. Consider the following case: you are starting an online casino that is, of course, based on an Ethereum contract. Unlike "Duke of Ether", casino requires some money to start, as in most cases it should be able to match players' bets. An obvious solution is to start an ICO. What makes this example different from most ICOs we can see on the emerging and unregulated (so far) Ethereum market? The nature of its business. It makes profits in the block chain. In other words, you can attach this contract to ERC20 to perform fund rising AND you can attach this same contract to ShareHolder to pay back to share holders. This was an important point, as you can not do the same in case you run a "we are going to create a new, faster computer with detacheable quad copter instead of a cooler" startup: your profits are not in block chain, so there is no way of linking them directly to share holders' profits. In the world of "real" stock market, there are countless regulations allowing you to be more or less confident that you will not be tricked; in the world of ICOs, there is no such confidence. Unless... Unless your contract, as I said already, makes all its profits in the block chain. A casino. An exchange. The Oraclize-like services... In this case, you can attach it to ShareHolder contract, and as the code for both contracts is online available for review, there is no way of abusing the agreement between investor and shares issuer. Well, unless someone figures out how to hack it. So if you want to do an "honest" fund rising, you should: 1) Make your contract based on ERC20 2) If fund rising fails, return money to investors (this is part of a standard fund rising contract) 3) If fund rising succeeds, begin sending part of profits to Share Holder contract your contract is attached to (again, it should be part of your contract, so your clients can review it). This way share holders are guaranteed to participate in profits.







(C) snowcron.com, all rights reserved

Please read the disclaimer