Skip to content Skip to sidebar Skip to footer

Blockchain Engineering: Solidity Tutorials -Role-6.


Interfacing with other Contracts

There are ii ways to interface amongst other contracts: Either phone call a method of a contract whose address is known or create a new contract. Both uses are shown inwards the instance below. Note that (manifestly) the rootage code of a contract to live created needs to live known, which agency that it has to come up before the contract that creates it (too cyclic dependencies are non possible since the bytecode of the novel contract is actually contained inwards the bytecode of the creating contract).
contract OwnedToken    // TokenCreator is a contract type that is defined below. It is fine to reference it   // every bit long as it is non used to create a new contract.   TokenCreator creator;   address owner;   bytes32 advert;   office OwnedToken(bytes32 _name)      address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;     nameReg.call("register", _name);     owner = msg.sender;     // We do an explicit type conversion from `address` to `TokenCreator` and assume that the type of     // the calling contract is TokenCreator, there is no real way to check.     creator = TokenCreator(msg.sender);     name = _name;      office changeName(bytes32 newName)      // Only the creator can alter the name -- contracts are explicitly convertible to addresses.     if (msg.sender == address(creator)) name = newName;      office transfer(address newOwner)      // Only the current owner can transfer the token.     if (msg.sender != owner) return;     // We also want to ask the creator if the transfer is fine.     // Note that this calls a function of the contract defined below.     // If the call fails (e.g. due to out-of-gas), the execution here stops     // immediately (the ability to catch this will be added later).     if (creator.isTokenTransferOK(owner, newOwner))       owner = newOwner;     contract TokenCreator    role createToken(bytes32 refer) returns (address tokenAddress)      // Create a new Token contract and return its address.     return address(new OwnedToken(name));      function changeName(address tokenAddress, bytes32 bring up)      // We need an explicit type conversion because contract types are not part of the ABI.     OwnedToken token = OwnedToken(tokenAddress);     token.changeName(name);      function isTokenTransferOK(address currentOwner, address newOwner) returns (bool ok)      // Check some arbitrary condition.     address tokenAddress = msg.sender;     return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);     

Constructor Arguments

A Solidity contract expects constructor arguments later on the end of the contract data itself. This means that y'all pass away the arguments to a contract past putting them afterwards the compiled bytes as returned by the compiler in the common ABI format.

Contract Inheritance

Solidity supports multiple inheritance by copying code including polymorphism. Details are given inwards the following instance.
contract owned      office owned()  owner = msg.sender;      address owner;   // Use "is" to derive from some other contract. Derived contracts can access all members // including individual functions and storage variables. contract soul is owned      part kill()  if (msg.sender == owner) suicide(owner);    // These are solely provided to make the interface known to the compiler. contract Config  office lookup(uint id) returns (address adr)   contract NameReg  function register(bytes32 advert)  function unregister()    // Multiple inheritance is possible. Note that "owned" is as well a base of operations form of // "soul", all the same in that location is only a single example of "owned" (equally for virtual // inheritance in C++). contract named is owned, individual      function named(bytes32 bring up)          address ConfigAddress = 0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970;         NameReg(Config(ConfigAddress).lookup(1)).register(name);       // Functions tin be overridden, both local too message-based part calls have // these overrides into business relationship.     role kill()          if (msg.sender == possessor)              address ConfigAddress = 0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970;             NameReg(Config(ConfigAddress).lookup(1)).unregister(); // It is still possible to call a specific overridden function.              mortal.kill();                 // If a constructor takes an argument, it needs to live provided in the header. contract PriceFeed is owned, soul, named("GoldFeed")     function updateInfo(uint newInfo)        if (msg.sender == owner) info = newInfo;         office get() constant returns(uint r)  return info;      uint info;  
Note that in a higher place, we call somebody.kill() to "frontward" the devastation asking. The way this is done is problematic, as seen inward the next instance:
contract mortal is owned      part kill()  if (msg.sender == owner) suicide(owner);   contract Base1 is soul      role kill()  /* do cleanup 1 */ mortal.kill();   contract Base2 is individual      role kill()  /* do cleanup 2 */ mortal.kill();   contract Final is Base1, Base2   
A phone call to Final.kill() volition phone call Base2.kill as the near derived override, simply this office volition bypass Base1.kill, basically because it does not fifty-fifty know nearly Base1. The fashion around this is to use super:
contract someone is owned      function kill()  if (msg.sender == owner) suicide(owner);   contract Base1 is person      office kill()  /* do cleanup 1 */ super.kill();   contract Base2 is person      part kill()  /* do cleanup 2 */ super.kill();   contract Final is Base1, Base2   
If Base1 calls a role of super, it does non but phone call this part on ane of its base of operations contracts, it rather calls this office on the adjacent base of operations contract in the concluding inheritance graph, and then it volition phone call Base2.kill(). Note that the actual office that is called when using super is not known in the context of the class where it is used, although its type is known. This is similar for ordinary virtual method lookup.

Multiple Inheritance as well as Linearization

Languages that let multiple inheritance take to deal alongside several problems, i of them being the Diamond Problem. Solidity follows the path of Python too uses "C3 Linearization" to force a specific lodge inwards the DAG of base of operations classes. This results inward the desirable belongings of monotonicity merely disallows around inheritance graphs. Especially, the club inward which the base classes are given inward the is directive is important. In the next code, Solidity volition reach the error "Linearization of inheritance graph impossible".
contract X  contract A is X  contract C is A, X  
The argue for this is that C requests X to override A (by specifying A, X inwards this society), but A itself requests to override X, which is a contradiction that cannot live resolved.
A unproblematic dominion to remember is to specify the base classes inwards the society from "about base-similar" to "nigh derived".

Abstract Contracts

Contract functions tin can lack an implementation as in the following instance (bank bill that the function declaration header is terminated past ;).
contract feline    function utterance() returns (bytes32);  
Such contracts cannot be compiled (even if they comprise implemented functions amongst non-implemented functions), just they can live used equally base of operations contracts:
contract Cat is feline    office utterance() returns (bytes32)  return "miaow";   
If a contract inherits from an abstract contract together with does non implement all not-implemented functions by overriding, it will itself live abstract.

Visibility Specifiers

Functions together with storage variables can be specified as being worldinternal or individual, where the default for functions is populace in addition to internal for storage variables. In add-on, functions tin can too be specified every bit external.
External: External functions are role of the contract interface in addition to they tin can live called from other contracts as well as via transactions. An external function f cannot live called internally (i.e. f() does non operate, just this.f() industrial plant). Furthermore, all office parameters are immutable.
Public: Public functions are part of the contract interface too can be either called internally or via messages. For populace storage variables, an automatic accessor part (meet below) is generated.
Inherited: Those functions together with storage variables tin can solely be accessed internally.
Private: Private functions as well as storage variables are solely visible for the contract they are defined inward as well as not in derived contracts.
contract c    office f(uint a) private returns (uint b)  return a + 1;    office setData(uint a) inherited  data = a;    uint public data;  
Other contracts tin can phone call c.data() to think the value of information inward storage, just are non able to call f. Contracts derived from c tin telephone call setData to modify the value of data (just alone in their own storage).

Accessor Functions

The compiler automatically creates accessor functions for all populace state variables. The contract given below will have a office called information that does not take any arguments too returns a uint, the value of the state variable data. The initialization of state variables tin can be done at declaration.
contract exam       uint public data = 42;  
The next example is a scrap more complex:
contract complex    struct Data  uint a; bytes3 b; mapping(uint => uint) map;    mapping(uint => mapping(bool => Data[])) public data;  
It will generate a part of the following class:
part information(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b)    a = data[arg1][arg2][arg3].a;   b = data[arg1][arg2][arg3].b;  
Note that the mapping inwards the struct is omitted because there is no adept fashion to supply the central for the mapping.

Fallback Functions

A contract tin can have just one unnamed office. This part cannot take arguments and is executed on a phone call to the contract if none of the other functions matches the given office identifier (or if no information was supplied at all).
contract Test    function()  x = 1;    uint x;   contract Caller    part callTest(address testAddress)      Test(testAddress).send(0);     // results in Test(testAddress).x becoming == 1.     

Function Modifiers

Modifiers tin live used to easily modify the demeanour of functions, for example to automatically check a status prior to executing the function. They are inheritable properties of contracts too may live overridden past derived contracts.
contract owned    function owned()  owner = msg.sender;    address owner;    // This contract only defines a modifier but does not use it - it will   // be used in derived contracts.   // The function body is inserted where the special symbol "_" in the   // definition of a modifier appears.   modifier onlyowner  if (msg.sender == owner) _   contract mortal is owned    // This contract inherits the "onlyowner"-modifier from "owned" and   // applies it to the "kill"-function, which causes that calls to "kill"   // only have an effect if they are made by the stored owner.   function kill() onlyowner      suicide(owner);     contract priced    // Modifiers can receive arguments:   modifier costs(uint price)  if (msg.value >= price) _   contract Register is priced, owned    mapping (address => bool) registeredAddresses;   uint cost;   role Register(uint initialPrice)  price = initialPrice;    part register() costs(cost)      registeredAddresses[msg.sender] = true;      part changePrice(uint _price) onlyowner      price = _price;     
Multiple modifiers tin be applied to a role by specifying them in a whitespace-separated listing and volition live evaluated in lodge. Explicit returns from a modifier or office body like a shot go out the whole role, patch control flow reaching the cease of a part or modifier body continues later the "_" inward the preceding modifier. Arbitrary expressions are allowed for modifier arguments in addition to in this context, all symbols visible from the function are visible in the modifier. Symbols introduced inward the modifier are not visible inwards the role (equally they mightiness change by overriding).