Or how the blockchain can also revolutionize our relationships…
Since the moment I heard about it, I have always been fascinated by the huge possibilities that smart contracts give us and I feel that they are still underestimated.
I started getting interested in Bitcoin almost two years ago but it's only a few months ago I discovered the Ethereum blockchain and Solidity. Since then, I haven't been able to stop myself from reading everything I can about the subject. I subscribed to mailing lists to receive articles about Ethereum and Solidity directly in my mailbox, I often take a peek at the Solidity docs and I read countless tutorials about the subject.
However, it looks like outside big companies, there aren't a lot of people out there who try to use the Ethereum blockchain in a more "disruptive" way. Most of the tutorials teach how to create ERC20 tokens or voting systems. I often let my mind wander and imagine what we could achieve if the blockchain would replace our governments. Which tasks could smart contracts allow us to take back and own again? That's how the idea of writing marriage certificate smart contracts came to me. After all, it is something relatively simple: two human beings want to be wed (for better or for worse), they register their union in front of an authority and they go back to the same authority for instance when they need to prove their marriage or when they want to divorce.
In real life, a marriage is a real challenge and people often end up wasting a lot of time and money. A smart contract can help them get married (or divorced) in a matter of seconds for a few cents. The immutability of the blockchain allows making sure the marriage certificate is not altered beyond its inner functions and makes it available anywhere at any time.
This is how the idea of celebrating marriages on the blockchain was born! Through the website, couples can create a smart contract that will store their personal identity information (like names, location and ID number) in an immutable fashion to be able to prove ownership of the certificate in a simple way, they can without efforts update the status of the marriage (a divorce would literally take seconds and a few cents to perform) and finally, the design of the smart contract allows couples to use the certificate to store common funds in two different ways: a "joint account" where each spouse can freely deposit and withdraw money and a "savings account" where the agreement of both spouses is required before withdrawing funds.
I will describe below the code of the smart contract (available at this address), function after function, in order to give a better understanding of what it does but also to receive useful feedback :) It may also be useful for beginners as I was a few months ago to get a detailed walk-through of how a smart contract works.
The Marriage Certificate Factory
Every marriage certificate is created by a factory smart contract that is in charge of setting the right state with the right data and savings the certificates addresses. As it is customary, the smart contract starts with the version pragma:
pragma solidity ^0.5.0;
The smart contract uses the latest version of Solidity compared to a lot of smart contracts written in the fourth version.
contract MarriageCertificateCreator {
MarriageCertificate[] public certificates;
address payable public owner;
uint public certificateFee;
string[3] public lastMarriage;
This is the contract declaration. It creates four state variables: a certificates variable (a dynamic array containing values of type "MarriageCertificate") which ultimately will contain the addresses of every certificate created, an owner variable (to use various functions of the smart contract), a certificateFee variable (an unsigned integer representing the fee in wei) and a lastMarriage variable (an array of 3 strings that will hold the names and location from the last registered certificate).
constructor() public {
owner = msg.sender;
certificateFee = 100000000000000000 wei;
}
The constructor is pretty straight forward, it saves the owner's address (mine) and sets the fee for the creation of a new marriage certificate to 0.1 ether. This fee can be manually updated in another function.
event NewCertificateCreated(
MarriageCertificate newCertificateAddress, uint numberOfCertificates
);
This is the only event in this smart contract, it is emitted every time a new certificate is created and returns the address of the new contract and the total number of marriage certificates to the web3 interface.
The createNewCertificate function
This is where all the magic happens! The following function is in charge of receiving the data from the web3 interface, of validating them, of updating the state of the contract factory and obviously of creating the new marriage certificate.
You will find below a breakdown of the different parts of this function.
function createNewCertificate(
string memory spouse1, string memory spouse2, address spouse2address, string memory location
) public payable {}
The function accepts 4 arguments:
- The spouse1 variable is a JavaScript object in JSON format that contains various information about the first spouse (who is also the initiator of the marriage certificate). A JSON string is a better choice to store in the smart contract for multiple reasons: it allows to store different information in the future, it is easier to deal with in Solidity than other structures like array or structs, it is easier to fetch from the web3 interface (no need to convert it if it was, for example, a struct) and most of the information provided during the smart contract creation is not vital for its execution.
- The spouse2 variable is also a JavaScript object in JSON format that holds the information relative to the second spouse. As the marriage certificate is created by one person, the second actor must validate the information later to make the marriage valid. The marriage stays in a permanent invalid state until the second spouse comes and approves the marriage.
- The spouse2address variable contains the address of the second spouse who must approve the marriage. The two addresses will be stored independently as they are required to update the state of the newly created certificate.
- The location variable is a Javascript object in JSON format holding the names of the city and the country chosen by the couple for the registration process.
MarriageCertificate newCertificate =
new MarriageCertificate(
msg.sender,
spouse1,
spouse2,
spouse2address,
location
);
The body of the function is responsible for passing down the arguments received earlier, the msg.sender being the address of spouse1. Note that whoever creates the marriage certificate will be considered as the first spouse, whatever value is passed in the spouse1 variable.
Note also that in Solidity 0.5, when you create a new smart contract from a smart contract, the value it returns is ultimately an address but it will be of type "NameOfCreatedSmartContract". This is also important to know as we will store these values in an array.
certificates.push(newCertificate);
lastMarriage = [spouse1, spouse2, location];
emit NewCertificateCreated(newCertificate, certificates.length);
The next lines of code are pretty straight forward: we push the address of the new certificate into the certificates array (whose values must be of type "NameOfCreatedSmartContract" as per Solidity 0.5), we update the lastMarriage variable so the value is always set to the last registered marriage and we emit an event that will be caught by the web3 interface.
The Marriage Certificate
The Marriage Certificate smart contract is a pretty simple one as it is mostly used to store immutable information about the two spouses, mutable information about the status of the marriage and common funds in two separate accounts, plus a couple of functions to deposit and withdraw the abovementioned funds.
A few variables are set during the creation of the contract as well as two event emitters: location, spouse1 and spouse2 will hold the information about the spouses and the place of registration. It should be noted that the ID numbers provided by the spouses during the certificate creation are encrypted client-side and a decryption key is provided, should the spouses have to prove they are the ones who registered the marriage.
The isValid variable is an array of 2 booleans representing the marriage status chosen by each spouse, spousesAddresses is an array of two addresses containing the spouses' addresses mostly used in require functions to make sure the status of the contract can only be modified by the two spouses.
The timestamp variable holds the creation timestamp and the accounts mapping keeps track of the amount of ether in the "joint" and "savings" account.
A withdrawRequestFromSavings struct will be created every time one of the spouses makes a withdrawal request from the savings account. The request is then saved in the withdrawRequests mapping.
Finally, the MarriageValidity event returns the isValid array and the NewWithdrawalRequestFromSavings event returns the newly created request.
The constructor
The constructor function is pretty standard:
It receives different information about the newlywed couple, stores it in the variables created earlier and set the "joint" and "savings" accounts to 0. The isValid variable is set to [true, false]
as the marriage can only be valid after the second spouse approves it.
The contract state manipulations
The smart contract allows the spouses to update the status of the marriage as they wish. The status is calculated using a boolean operation with the &&
operator between the two values in the isValid array. This means that, for the marriage to be valid, both spouses must agree with it. A simple reading of the boolean values in the isValid array can also indicate which spouse agrees or disagrees with the marriage.
modifier onlySpouses {
require (
msg.sender == spousesAddresses[0] || msg.sender == spousesAddresses[1], "The contract status can only be changed by one of the spouses."
);
_;
}
This modifier will be used a couple of times and makes sure that the person updating the state of the smart contract does it from one of the two addresses that were provided during its creation.
The changeMarriageStatus function does a simple boolean flip when one of the spouses asks for a change in the marriage status. Note that both spouses must set both values in the isValid array to true
in order for the divorce to be effective.
The deposit/withdrawal functions
A smart contract being basically an Ethereum account with code, it would be a pity not to use this functionality :) To push it further, the marriage certificate is divided into two different accounts: a "joint" account and a "savings" account.
The "joint" account is an account where both spouses (or anyone else with an Ethereum address for that matter) can send ether and both spouses can withdraw as much as they want.
The "savings" account is an account where only the two spouses can send money to and in order to withdraw money, the initiator of the withdrawal must register a request that will have then to be approved by the second spouse.
Let's dive into the code:
The deposit function allows the spouses to deposit money through the web interface. It accepts 2 parameters: the amount to be added and the name of the account to increment. The onlySpouses modifier makes sure only the two spouses have access to this function.
As a safety measure, the contract verifies that the amount selected by the user is also the amount that was sent. The stringsAreEqual function checks the bytes32 variable and the contract decides to increment the joint or the savings account accordingly. The last assert function is there to check that whatever happens, the sum of the joint account and the savings account is always equal to the total balance of the contract.
Every change in balance that goes through the functions of the smart contract emits a LogBalance event with the total balance, the balance of the "joint account" and the balance of the "savings balance".
The withdraw function is a bit more intricate. None of the spouses is allowed to withdraw money from the savings account without the approval of the second one and this function must make sure it doesn't happen.
If one of them sends a transaction to withdraw ether from the savings account, a request is saved in the contract and a request ID is issued. The second spouse must use his/her own account address to approve the request.
First of all, the onlySpouses modifier ensures that no one other but the two spouses can withdraw money from the contract. The contract then checks if the requested amount doesn't exceed the balance in the selected account.
The first condition verifies the name of the selected account and checks the requested amount to be sure it doesn't exceed the balance of the account.
If the "joint" account is chosen, the result is straightforward: the amount is deducted from the balance and sent to the sender.
If the "savings" account is chosen, the contract creates a (pseudo) unique ID adding the current timestamp, the block difficulty and the block number. As it is very unlikely that two requests come exactly at the same time, this should suffice. A new struct of type withdrawRequestFromSavings is created and saved in the withdrawRequests mapping using the ID as a key. Once the new request is saved in the mapping, we emit an event to fetch the request ID in the web interface.
To finish, we check once again that the joint account balance and the savings account balance equal the total balance and the contract is reverted to its previous state if it isn't the case.
Now let's have a look at the function allowing the spouses to approve withdrawal requests from the savings account and sending the funds.
The approveWithdrawRequestFromSavings function takes the request ID as a parameter. The function can only be accessed by one of the spouses. The contract fetches the request from the mapping in storage and runs a few checks: by checking the timestamp and the amount, it makes sure the request exists (they will be equal to 0 by default if the request struct was not found), then it checks whether the requested amount is still available and whether the request is still pending (it wouldn't make sense to approve a request that's already approved!)
If the request is pending, the next condition ensures that the spouse approving the request is NOT the one who created it (users trying to approve a request they created themselves will trigger the revert function).
If everything is all right, the request is marked as approved (true
), the requested amount is deducted from the savings account and sent to the spouse who initiated the request.
The pay function allows spouses to use the money in the joint account to pay for various expenses like bills. The function checks if the amount that is requested is available in the joined account before deducting it and sending it.
Once this is done, the function makes sure the total balance equals the joint account balance and the savings account balance.
In the end, a LogBalance event is emitted to update the user interface.
To conclude, it is worth mentioning the fallback function :
Any ether sent directly to the contract will be added to the joint account. Please note that the transaction will fail if around 42.000 gas is not provided.
Conclusion
I hope this article helped you understand the inner mechanics of a smart contract created to register marriages on the blockchain, to keep track of the marriage status, to manage a marital budget and to ease various processes involved in the bureaucracy of marriage.
Beyond the practical side of such a contract, it is also envisioned as a demonstration of how far the blockchain can push the limits of our centralized society and open the gates of a decentralized world where we will all take back the power to make decisions for ourselves!
Links
You can find the website here, the smart contract at this address or check the Github repository.
Top comments (1)
Would you please suggest some good resources( courses ) to learn blockchain as a develeoper?