ACL — Access Control Lock

Our smart contract needs to limit access to certain entry points. We can do that by comparing the address of the person, who called our contract via a transaction, with address of owners that we’ve specified as initial storage.

Let’s define a method that we can re-use, we’ll call it can_call , and it will accept a single parameter, which is the current storage value. Additionally, we’ll implement a helper method fail_with_wrong_ownership that makes the current contract transaction fail — we’ll use it when can_call evaluates to false .

Proposing a multi-sig transaction

To propose a new transaction within our multi-sig contract, we’ll use a method called create_proposition , which takes two arguments, one is of type proposition and the second one is of type storage .

Flow of this method is relatively straight forward, firstly check if the current transaction was initiated by one of the owners, if not — fail because of ownership issues. In case an actual multi-sig contract owner is calling this entry point, check if there is an existing proposition already, if there is one, fail because we don’t want to override it and loose it’s data. If there’s no existing propositions, save our new proposition as the current one.

Deleting a multi-sig proposition

It’s always nice to have a way out, in our case, we allow the predefined owners to delete a pending proposition, prior to creating a new one. There’s no parameter for our delete_proposition entry point, hence we use unit .

Again we check who’s calling the contract entry point, if our ACL passes, we delete the existing pending proposition, if not — we abort the operation.

Signing a multi-sig proposition

In order to execute the proposed transaction, we must sign it first. We’ll have an entry point available to do just that, and it will be called by each owner independently to provide a proposition signature unique to the owner.

First we check the ACL as we did in delete_proposition , if it passes, we’ll add a signature based on the Current.sender() → which is the address of the account which called our entry point in a transaction — and that’s one of the contract owners.

Set.add only adds values to the set if they are unique, that means if you call the sign_proposition entry point multiple times, with the same owner signing the contract call transaction, you won’t create a second signature, as the addresses in our Set are unique.

Executing a multi-sig proposition

After all the owners have signed the proposition, we can proceed by executing the proposition.

We begin by verifying our ACL, if it passes, we check if the number of existing signatures, is matching the number of multi-sig contract owners. If we have sufficient (100%) signature coverage, we proceed by creating an Account.transfer transaction, with destination & amount taken from our proposed transaction.

We end the entry point execution by returning a cleaned up storage thanks to delete_proposition helper function defined earlier, that resets our storage back to the initial state with no existing proposition & signatures. And we specify a transaction/operation to be executed as a result of our call — which is the proposed transaction to the destination account with a given amount.

🎉 Congratulations, your smart contract is now ready to be deployed into the blockchain! 🎉

You can find the full contract’s source here.