The DIY guide to building smart contracts on Ethereum

22 August 2016
Andrew Elmore

In my previous post I discussed some of the challenges creating Financial Smart Contracts on the Ethereum platform. This post explores our experiences in actually building one.

The Goal

We chose to model an ISDA Interest Rate Swap. Organisations will often receive the definition of an IRS contract via an FpML message. Our goal was to turn this message into an executable contract on the chain which would track the positions of each party.

There are numerous intricacies to processing an IRS which, while they increase the processing complexity, do not fundamentally changes the steps and challenges below. As such this post deliberately misses out considerations (such as payment dates vs calculation dates) in the interests of clarity.

The Plan

Stage 1 is to convert our FpML document into a contract on the Ethereum chain. FpML documents are prescriptive hence we wanted to construct a template IRS Contract which we could parameterise.

FpML.jpg

Periodically we need to calculate the monies due to each party; thus our contract needs to keep a track of balances and balance adjustments.

ledger.jpg

Finally of course, each party needs to be able to inspect the contract at any time to see their liabilities and the calculations

actors.jpg

Creating the Contract 

The Template Contract 

Our IRS contract is a bipartite agreement and for each party needs to store:

  • Their identity (or address)
  • Their acceptance of the contract
  • Their side of the agreement which includes:
    • The principal (amount and currency)
    • The interest rate (fixed or relative to another rate, possibly with delta)
    • The calculation terms (frequency, how to adjust the rate given the frequency, which calendar to use and how to handle payments which occur on holidays etc)
    • Other items which we'll gloss over in this explanation

It also needs to track each party's current balance and a list of the movements.

We started by creating a generic Bipartite contract:

    
        contract BiPartite {
        
        struct Party {
            address id;
            string name;
            uint dateAccepted;
            int64 balance;
        }
        
        Party _partyA;
        Party _partyB;
        
        ...
        }
    

For a contract to be valid, both parties have to accept the contract: 

    
        function acceptContract() constant returns (bool) {
        
        bool accepted = false;
        
        if(msg.sender == _partyA.id) {
            accepted = true;
            if(_partyA.dateAccepted == 0) {
                _partyA.dateAccepted = now;
            }
        }
        if(msg.sender == _partyB.id) {
            accepted = true;
            if(_partyB.dateAccepted == 0) {
                _partyB.dateAccepted = now;
            }
        }
        
        return accepted;
        }
    

Our contract is also timebound:

    
        contract Timebound {
        
            uint64 _startDateTimeInSecs;
            uint64 _terminateDateTimeInSecs;
        
            ...
        }
    
        contract IRS is Timebound, BiPartite, ... {
    

Breaking the contract up in this way is good for re-use and OO-cleanliness but there's another benefit - we can pass in more initial state. While our contract can implement setters to allow fields to be set at an aribtrary point in time and can include logic to prevent that state being changed once set, it makes sense to allow fundamental, immutable aspects of the contract to be passed in via a constructor. This communicates 2 things clearly to users:

1. It is not valid to have a contract without this information

and

2. If a field does not have a setter method, it is clear that the values are intended to be immutable

There are (relatively low) limits however in how many parameters we can pass in to a given function, such limit being dependent on the number and size of the parameters. This limit however is based on the arguments that a given function receives; if our constructor delegates some parameters to an ancestor's constructor those parameters no longer count against 'our' limit.

    
        function IRS(address partyA, string partyAName, address partyB, string partyBName, uint64 startDateInSecs, uint64 terminationDateInSecs, int64 partyAPrincipal, string partyACurrency, ...) 
            BiPartite(partyA, partyAName, partyB, partyBName)
            Timebound(startDateInSecs, terminationDateInSecs)  {
            
            ...
        }
    

Beyond some additional internal logic and plumbing, there are 3 key sets of functions that our contract needs to provide:

  1. Query functionality about the mechanics of the contract, such as its execution schedules and the rates it needs to perform a calculation.
  2. A function to trigger the execution of the function, taking in the current rates.
  3. An event to record executions, creating a log in the contract of the dues incurred under the contract.

Solidity allows us to create event methods in our contract which create a corresponding log entry associated with the relevant block, e.g.:

    
        event balanceUpdated(uint256 executionTime, bool isBalanceA, int64 appliedRate2dp, int64 balanceAdjustment, int64 runningBalance);
    

 (Solidity has no support for decimal/fractional types, requiring us to work in integer multiples of our smallest unit. We're working a 2 decimal places of accuracy in this example contract but in practice we'd likely use higher precision and need to ensure that we pick a data type that is still capable of representing our maximum values).

Instantiating the Contract

C24 iO made it easy for us to ingest our FpML contract. Using some simple transport adapters we exposed the service as a REST API, as well as monitoring queues and file-systems for inbound messages. On receipt, we parsed and validated them with:

    
        Fpmlmain56DocumentRoot root = C24.parse(Fpmlmain56DocumentRoot.class).from(message);
    C24.validate(root);
    

Now we knew we had a valid contract, we needed to extract out the information we needed to supply to the Solidity contract's constructor, for example we can extract the first party's ID:

    
        String partyAId = root.getDataDocument().getPartiesAndAccountsmodel().getParty(0).getPartyId(0);
    

Similar operations allow us to extract all the party, rate and schedule information from the message. For more complicated scenarios we could have used a C24 iO transform, but our simplistic use case requires relatively little data.

As discussed in the previous post, we used EthereumJ to front the compilation of the contracts and interaction with the blockchain. A simple ContractsManager facade allowed us to hide the logic on how to call contract constructors with parameters:

    
        public byte[] deployContract(ContractMetadata meta, Object... params) throws InterruptedException {
        
        CallTransaction.Contract contract = new CallTransaction.Contract(meta.abi);
        CallTransaction.Function inc = getConstructor(contract);
        byte[] functionCallBytes = inc.encodeArguments(params);
        
        // From http://solidity.readthedocs.io/en/latest/contracts.html
        // Internally, constructor arguments are passed after the code of the contract itself, but you do not have to care about this if you use web3.js.
        
        TransactionReceipt receipt = node.sendTxAndWait(new byte[0], ByteUtil.merge(Hex.decode(meta.bin), functionCallBytes));

        byte[] contractAddress = receipt.getTransaction().getContractAddress();
        logger.info("Contract created: " + Hex.toHexString(contractAddress));
        
        return contractAddress;
    }
    

Interacting with the Contract

Our contract exists within a wider ecosystem. We need external feeds of information to perform our calculations and something needs to be responsible for invoking those exeuctions. Then of course there's the blockchain itself - we need to be able to read and track our contracts. 

Reading contracts from the chain

Our Contracts Manager ultimately became more stateful, discovering and managing the contracts on the chain. However we needed to spot when new contracts (conforming to our template) were added to the chain and also to refresh the list of contracts when restarting the server. The plan was to examine the transaction list on each block and, where we identified instances of our contract, we'd keep a record of them and interrogate them as to their requirements (i.e. execution schedule and required rates).

Identifying the contracts seemed fairly simple:

    
        Block block = ...;
            if(block != null && block.getTransactionsList() != null) {
                for(Transaction tx : block.getTransactionsList()) {
                    if(tx.isContractCreation()) {
                        registerContract(tx.getContractAddress(), tx.getData());
                    }
                }
            }
    

We could spot if it was an instance of our contract by seeing if the transaction data started with the binary version of our compiled contract (started with, as the transaction data will have the encoded constructor parameters appended to the end of it).

Curiously when we restarted our server it would sometimes fail to find existing contracts on the chain, but not always. It transpires that *the Solidity compiler is not deterministic!* Compiling the exact same contract multiple times occasionally (but not usually) yielded different binary forms. Outside of some of the gcc optimisation options, I'm aware of very few non-deterministic modern compilers as the process stability provided by a deterministic compiler generally outweighs any perceived advantages of non-determinism (interestingly(?)) despite initial deliberate decisions to not provide a determinism guarantee, the C# compiler has subsequently moved towards determinism. The fix was simple - to store the binary form alongside our source contract and to reuse that rather than recompiling on start-up - however not something that we had planned for.

Periodic Execution

We built a lightweight scheduler around Quartz to manage our execution schedules. When woken, we looked up the interest rates required for the contract whose execution we were triggering, found their current values then invoked the execute function on the contract. This simple statement however involves some complexity in a distributed system:

  • Who is 'we'? For resiliance purposes, we have to assume that multiple nodes could have the responsibility to execute the contract. Our block consensus algorithm will detect (& fix) collisions between blocks where 2 nodes have both executed the contract but we also have to protect against a time skew meaning that one node will attempt to execute the contract even after its chain contains a block where another node has executed the same operation. Our solution was to have the contract store its last execution time and a minimum period between executions for each term. Attempts to execute again during this blackout period would be rejected.
  • Where are the interest rates read from? In a production system we will likely have a feed from an external source, for full repeatability potentially these too will exist on a blockchain. For our simple sample we created a simple REST API which interest rates can be set via a basic web UI.
  • Who is allowed to execute the contract? They must be trusted to execute the contract on the required schedule and to supply the correct rates (albeit the transparency of the chain means their actions are open to scrutiny). Our contract restricts the addresses that are allowed to invoke sensitive methods to the creator's address but in practice we'd likely have at least 2 authorised bodies (and a trusted mechanism to add/remove parties). We accomplish this with a new base contract that our IRS extends:
    
        contract owned {
      // Store the creator's address
      function owned() { owner = msg.sender; }
      address owner;
    
      // This modifier can be applied to any function in our derived contracts
      // If anyone else attempts to invoke the function, the attempt will be rejected with an exception
      modifier onlyOwner { 
        if (msg.sender == owner) throw;
        _ 
      }
    }

    

The actual calculation in this case is simple - apply the interest rate (weighted to the execution period as defined in the original FpML message) to the principal amount and increase (or decrease) the balance accordingly.

Finally we invoke our balanceUpdated event, for example: 

    
        balanceUpdated(_lastRun, true, rate, partyABalanceAdjustment, _partyA.balance);
    

This creates a log entry which nodes will store (alongside but outside of the blockchain) when processing the transations in the chain. We can pick these up when traversing the chain in order to display a history of updates. For future proofing we developed a simple class which would parse the events from our ABI and register a suitable handler:

    
        private Set createEventHandlers(ContractMetadata metadata) {
        JsonParser parser = new JsonParser();
        JsonArray root = (JsonArray) parser.parse(metadata.abi);

        Set eventHandlers = new HashSet<>();

        for(JsonElement e : root) {
            if(e.isJsonObject()) {
                JsonObject obj = (JsonObject) e;
                if(obj.getAsJsonPrimitive("type").getAsString().equals("event")) {
                    EventHandler handler = new EventHandler(obj.getAsJsonPrimitive("name").getAsString());
                    
                    for(JsonElement inputElement : obj.getAsJsonArray("inputs")) {
                        Parameter p = new Parameter(Type.named(inputElement.getAsJsonObject().get("type").getAsString()), inputElement.getAsJsonObject().get("name").getAsString());
                        handler.addParameter(p);
                    }
                    
                    eventHandlers.add(handler);
                }
            }
        }
        
        return eventHandlers;

    }
    

Each event has a binary signature which we can build up from the ABI information:

    
        String sig = ...; // effectively ([parameterType][,]*)
        DataWord dWordSignature = new DataWord(SHA3Helper.sha3(sig.toString().getBytes("UTF-8"));
    

and we can use that to detect if a given log entry is an instance of that event:

    
        public boolean matches(LogInfo info) {
            DataWord eventId = info.getTopics().get(0);       
            return eventId.equals(getSignatureDWord());
        }
    

Interest Rate Management

As alluded to above, in a production system it's likely that we'd have a trusted feed of interest rates, with the provenance of that feed securely established (for example via a chain of its own or an authenticated external service). Our needs were rather simpler - our contract enables us to ask it for the interest rates it needs, thus we created a basic Interest Rate Manager with which the Contract Manager registers interest in the interest rates required by its contracts.

The Interest Rate Manager has a basic UI (more below) where a user can manually update the relevant rates. 

Putting It All Together

While our production endpoints would be more automated, for demonstration purposes a UI is the way to go. We threw together a lightweight, AngularJS-based UI to allow creation, maintenance and inspection of our services.

Create_Contract.png

The starting point is creating a contract. The UI allows us to paste in our FpML document, which the back-end will parse and validate. The calcuation periods in an IRS will typically be in the order of weeks or months - not great when running an interactive demo, so we added a toggle which will shrink any periods in the contract by a few orders of magnitude (so 60 days becomes 60 seconds).

Submitting the contract (assuming it is valid) causes our server to create an instance of our template Solidity contract on the chain, parameterised by the data in the FpML document. However we now need to wait for a block containing that transaction to appear on the chain; when it does the UI is updated with the contract's address.

Contract_Created.png

 The contract appearing in a block on the chain will cause our backend services to interrogate it, the result being visible in the following screens.

Setting the Interest Rates

Setting contract interest rates

The UI is dynamically built according to the set of interest rates that active contracts need. As we now have a contract which requires the EUR_LIBOR_BBA rate, we have the ability to set that rate in the UI. The Contract Manager will use whatever the current rate is when it triggers the execution of the contract.

Scheduled Activity 

If we now view the list of active contracts in the UI we can see our new contract, along with its next execution time. Our scheduler is now managing the timed executions of the contract. 

Contract List next execution time

Of course, each execution creates a transaction hence there need to be miners adding new blocks to the chain with our transactions in them.

Viewing the Activity

Contract_Activity.png

We can view the current state of our contract at any time. The shot above shows the contract after a few executions (the demo mode does not (yet) take into account scaling the weighted percentage!).

All of these details are derived from information on the chain and can be verified by any node in the network - all the transactions (and their parameters) are recorded in blocks.