CardanoSharp: Cardano Multisig Addresses
A multi-signature address is an address with an attached native script. The native script is evaluated when a spend transaction is submitted to the ledger. A native script is a small smart contract that enforces a set of rules agreed upon when the address was initially created.
The typical use case of a multi-signature address is to have more than one person in control of signing a transaction. This is very useful if the address represents a business or joint funding of multiple stakeholders.
In this scenario, we will demonstrate how 3 users, who are the directors of a business, use the system. Each maintains their own wallet and secures their private key. They need to fund a project, and part of the rule is that 2 out of 3 directors must sign each spending transaction associated with this project's funding.
We start by creating 3 wallets, one for each director, and 1 wallet to represent the business
// rewards wallet/business
var (_, _, stackPub) = GetPolicyKeysForWallet("slight velvet useful ...");
// stakeholder 1
var (u1Pri, u1Pub, _) = GetPolicyKeysForWallet("planet loud together ...");
// stakeholder 2
var (u2Pri, u2Pub, _) = GetPolicyKeysForWallet("orphan hub pepper ...");
// stakeholder 3
var (u3Pri, u3Pub, _) = GetPolicyKeysForWallet("dad wine match ...");
// payment script
var paymentScript = NativeScriptBuilder.Create
.SetScriptNofK(2, new[]
{
NativeScriptBuilder.Create.SetKeyHash(HashUtility.Blake2b224(u1Pub.Key)),
NativeScriptBuilder.Create.SetKeyHash(HashUtility.Blake2b224(u2Pub.Key)),
NativeScriptBuilder.Create.SetKeyHash(HashUtility.Blake2b224(u3Pub.Key)),
});
// create a multisig address
var address = AddressUtility.GetScriptAddress(paymentScript.Build(), stackPub, CardanoSharp.Wallet.Enums.NetworkType.Preview);
// addr_test1zrcs3trrey7np8f4m4kkmzn4jxxyyduvx6ymq0z7fdaywcc7x4yvjh8mxkr0f0czsw66e249nfymk9j87a7jha6h9vnqpnmwf3
Once you have created an address using the above code, you can send ADA to fund it as usual.
To send funds out of the address, a minimum of 2 out of the 3 directors must sign the transaction, as indicated by the payment script.
Here is a partial code example of a transaction signed by 2 directors.
// Build transaction body
var transactionBody = TransactionBodyBuilder.Create
.AddInput(utxo.tx_hash, utxo.tx_index)
.AddOutput(receiverAddress, transferAmount, outputPurpose: OutputPurpose.Spend)
.AddOutput(address, utxo.value - transferAmount, outputPurpose: OutputPurpose.Change)
.SetTtl(currentSlot + 1200)
.SetFee(0);
// adding signature keys to the UTxO from 2 out of 3
var witnessSet = TransactionWitnessSetBuilder.Create
.AddNativeScript(paymentScript)
.AddVKeyWitness(u1Pub, u1Pri)
.AddVKeyWitness(u3Pub, u3Pri);
Here is how the address would appear in CardanoScan with 2 transactions.
Implementation Notes
The above example is very basic and shows the 3 wallets accessible in the same code path. In reality, each user has their own wallet, where they sign the transaction independently from each other.
The transaction is presented to each user via a web application, where they can sign the transaction using their wallet. They then send it to a backend API that assembles the witness signatures and submits the transaction to the blockchain only once it has the minimum required signers.
Closing Thoughts
Cardano offers unique built-in functionality that enables the integration of simple yet effective rules within its addresses. These rules, once attached, are evaluated by the ledger at the time of each transaction. This method is more straightforward compared to an EVM smart contract, as it focuses on rules that are directly associated with individual transactions, thus streamlining the process and eliminating the need to evaluate against the entire account.
Another very useful use case is a vesting address. The funds in the address can not be spent until a certain amount of time has passed.
However, if native scripts are written incorrectly, they could end up locking the funds in the address forever. For example, incorrectly using 'Time locking' scripts with 'after' and 'before' conditions can lead to this issue, so it's important to test the script on a testnet before deploying it to the mainnet.
Here, you will find the documentation of the available native scripts in Cardano.
Subscribe to my newsletter
Read articles from Sarmaad Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Sarmaad Amin
Sarmaad Amin
I started my journey in programming when I was just 8 years old, writing my first lines of code in BASIC. This early experience sparked a deep love for programming in me, leading me to land my first paid job as a developer at the age of 16. In this role, I built desktop applications using FoxPro, marking the beginning of a rewarding career path. Fast forward a couple of decades, and I am now a full-time full-stack developer and solutions architect. My work involves building web applications, mobile applications, and focusing on backend automation and integration. I've had the opportunity to work with various technology stacks, which has greatly broadened my skill set and understanding of different programming environments. Throughout my career, I've witnessed the evolution of technology and have adapted to these changes, continually updating my skills and knowledge. My journey from a young coding enthusiast to a professional in the field is a testament to my passion for programming and my commitment to this ever-evolving industry.