We have seen how each credential on Cardano Shelley-Era most used addresses points to specific user keys by encoding their key hashes inside.
As seen on previous chapters Native Scripts and Plutus Scripts are the 2 protocol-native programmable code languages that can be executed on-chain and that are used as validators, as transaction witnesses based on a customizable logic, smart contracts if you wish to call them that way.
Here, on Cardano, you can use hashes to reference on-chain scripts instead of key-hashes to reference user keys on address credentials. This allow spend and stake permissions based on code logic instead of plain user signatures.
For all the examples on this chapter let’s automate the creation of your own set of Gift Wallets for each testing persona using the walletGenerator
API function, which generates them at scale if you wish, or individually like on the following script.
</br>
{
"type": "script",
"title": "Creating Alice, Bob and Charles",
"description": "Generates 3 unique QR password-encrypted Gift Wallets for testing multisig with your own set of keys. QR codes will be downloaded as PNG files. Import them into GC Wallet and store script results.",
"exportAs": "MyAlterEgos",
"run": {
"Alice": {
"type": "walletGenerator",
"amount": 1,
"defaultNamePattern": "Alice",
"defaultPasswordPattern": "ImAlice1234",
"defaultDescriptionPattern": "Use to sign as Alice",
"defaultKeyPattern": "wallet",
"defaultHintPattern": "your password is '{password}'",
"qr": true,
"download": true,
"extra":true
},
"Bob": {
"type": "walletGenerator",
"amount": 1,
"defaultNamePattern": "Bob",
"defaultPasswordPattern": "ImBob1234",
"defaultDescriptionPattern": "Use to sign as Bob",
"defaultKeyPattern": "wallet",
"defaultHintPattern": "your password is '{password}'",
"qr": true,
"download": true,
"extra":true
},
"Charles": {
"type": "walletGenerator",
"amount": 1,
"defaultNamePattern": "Charles",
"defaultPasswordPattern": "ImCharles1234",
"defaultDescriptionPattern": "Use to sign as Charles",
"defaultKeyPattern": "wallet",
"defaultHintPattern": "your password is '{password}'",
"qr": true,
"download": true,
"extra":true
}
}
}
🔍 See also: walletGenerator, script
Note: use "secrets":true
and run in system mode to also obtain the random-generated seed phrases and private keys on function output. Remember you can also create Gift Wallets (onboarding) or Express Wallets (personal) QR wallets from seed phrases through GameChanger Wallet user interface.
These were my results, the ones I will use along the chapter. I recommend you to create and store your own set of wallets and results to avoid testing interference across the multisig exercises.
</br>
We will use their Main Spend and Main Stake keys key-hashes to later build our native scripts. These hashes can be obtained on your results under the spendKeyHashHex and stakeKeyHashHex properties.
Alice | Bob | Charles | |
---|---|---|---|
encrypted private key | |||
main spend key hash | 45a8acd4529988b5b6ada6ab75514aaf8a6c0c6d769821d0a267dfba |
42c2e9c8cf9b18b406dee72dd33d7c15ec932670b6c567cf6ec87921 |
d7903f520d585f55df7eebb0b330d87a252e1ddbd9b45cef8c507f91 |
main stake key hash | 896f31da01424a4c6037b21fc4d498170f43f237abd1dcf57e08e07e |
fab802bd709e1eea862ce8055a35b76c07bfdd3c5d8b8d136528cdf0 |
c613a698b348a999a5c8cdb9b99aabf7365194e8a9891285044c5eaf |
</br>
On this chapter we will continue exploring the importance and usefulness of Native Scripts, or dumb contracts, by using them to code the logic required for special types of addresses that require a combination of more than one user key signature in transactions to grant spend or stake permissions: the usually called multisig addresses.
We will explore most common use cases among the infinite logic combinations that can be done.
When designing multisig workspaces, a smart pattern a developer can follow is to embed the party credentials on the code making it static, agnostic from any specific wallet pov. As a result, the same code can be executed on Alice, Bob and Charles devices pairing them all with the same multisig address
When encoding the gcscript as a URL or QR code, you can make your multisig users life easier: a single shareable URL to load the exact same multisig on any device
It’s native script grants spend and stake permissions on transactions consuming funds or managing stake on the multisig address only if: Alice and Bob signs
The shareable workspace loader code:
{
"type": "script",
"title": "Multisig 2 of 2 Workspace",
"description": "Alice and Bob must sign",
"args":{
"Alice":{
"spendKeyHashHex": "45a8acd4529988b5b6ada6ab75514aaf8a6c0c6d769821d0a267dfba",
"stakeKeyHashHex": "896f31da01424a4c6037b21fc4d498170f43f237abd1dcf57e08e07e"
},
"Bob":{
"spendKeyHashHex": "42c2e9c8cf9b18b406dee72dd33d7c15ec932670b6c567cf6ec87921",
"stakeKeyHashHex": "fab802bd709e1eea862ce8055a35b76c07bfdd3c5d8b8d136528cdf0"
}
},
"run": {
"walletSetup": {
"type": "loadConfig",
"updateId": "multisigs_update_1",
"layers": [
{
"type": "Workspace",
"items": [
{
"namePattern": "multisigs",
"titlePattern": "Multisig Wallets",
"descriptionPattern": "Example multisigs involving Alice, Bob and Charles wallets"
}
]
},
{
//this layer will build Native Scripts artifacts
"type": "NativeScript",
"workspaceIds": [
"multisigs"
],
// we use the "key" string template variable to replace by the key of the key-value map of items provided
"namePattern": "2_of_2_{key}_script",
"items": {
// for this item where the key variable equals "spend" ,
// the artifact name will be "2_of_2_spend_script"
"spend": {
//"all" is a native script that enforces that all sub-native script items on list be met
"all": {
"alice": {
//"pubKeyHashHex" is a native script that enforces an specific key signature referenced by key hash
"pubKeyHashHex": "{get('args.Alice.spendKeyHashHex')}"
},
"bob": {
"pubKeyHashHex": "{get('args.Bob.spendKeyHashHex')}"
}
}
},
// for this item where the key variable equals "stake" ,
// the artifact name will be "2_of_2_stake_script"
"stake": {
"all": {
"alice": {
"pubKeyHashHex": "{get('args.Alice.stakeKeyHashHex')}"
},
"bob": {
"pubKeyHashHex": "{get('args.Bob.stakeKeyHashHex')}"
}
}
}
}
},
{
"type": "Address",
"workspaceIds": [
"multisigs"
],
"items": [
{
"namePattern": "2_of_2",
//for spend credential we use the native script hash of native script named "2_of_2_spend_script"
"spendNativeScriptName": "2_of_2_spend_script",
//for stake credential we use the native script hash of native script named "2_of_2_stake_script"
"stakeNativeScriptName": "2_of_2_stake_script"
}
]
}
]
}
}
}
🔍 See also: loadConfig, script
After selecting Multisig Wallets
as current workspace, this is how your Address Picker will look like:
Note: using similar script logic for spend and for stake credentials is a common practice among Cardano multisig wallet software, but actually you are free to use different credential types on GameChanger Wallet.
It’s native script grants spend and stake permissions on transactions consuming funds or managing stake on the multisig address only if: At least 2 of Alice, Bob and Charles signs
The shareable workspace loader code:
{
"type": "script",
"title": "Multisig 2 of 3 Workspace",
"description": "At least 2 of Alice, Bob and Charles must sign",
"args":{
"Alice":{
"spendKeyHashHex": "45a8acd4529988b5b6ada6ab75514aaf8a6c0c6d769821d0a267dfba",
"stakeKeyHashHex": "896f31da01424a4c6037b21fc4d498170f43f237abd1dcf57e08e07e"
},
"Bob":{
"spendKeyHashHex": "42c2e9c8cf9b18b406dee72dd33d7c15ec932670b6c567cf6ec87921",
"stakeKeyHashHex": "fab802bd709e1eea862ce8055a35b76c07bfdd3c5d8b8d136528cdf0"
},
"Charles":{
"spendKeyHashHex": "d7903f520d585f55df7eebb0b330d87a252e1ddbd9b45cef8c507f91",
"stakeKeyHashHex": "c613a698b348a999a5c8cdb9b99aabf7365194e8a9891285044c5eaf"
}
},
"run": {
"walletSetup": {
"type": "loadConfig",
"updateId": "multisigs_update_2",
"layers": [
{
"type": "Workspace",
"items": [
{
"namePattern": "multisigs",
"titlePattern": "Multisig Wallets",
"descriptionPattern": "Example multisigs involving Alice, Bob and Charles wallets"
}
]
},
{
"type": "NativeScript",
"workspaceIds": [
"multisigs"
],
"namePattern": "2_of_3_{key}_script",
"items": {
"spend": {
//"atLeast" and "ofThese" builds a native script that enforces that "atLeast" N number of sub-native script items on "ofThese" list must be met
"atLeast":2,
"ofThese": {
"alice": {
"pubKeyHashHex": "{get('args.Alice.spendKeyHashHex')}"
},
"bob": {
"pubKeyHashHex": "{get('args.Bob.spendKeyHashHex')}"
},
"charles": {
"pubKeyHashHex": "{get('args.Charles.spendKeyHashHex')}"
}
}
},
"stake": {
"atLeast":2,
"ofThese": {
"alice": {
"pubKeyHashHex": "{get('args.Alice.stakeKeyHashHex')}"
},
"bob": {
"pubKeyHashHex": "{get('args.Bob.stakeKeyHashHex')}"
},
"charles": {
"pubKeyHashHex": "{get('args.Charles.stakeKeyHashHex')}"
}
}
}
}
},
{
"type": "Address",
"workspaceIds": [
"multisigs"
],
"items": [
{
"namePattern": "2_of_3",
"spendNativeScriptName": "2_of_3_spend_script",
"stakeNativeScriptName": "2_of_3_stake_script"
}
]
}
]
}
}
}
🔍 See also: loadConfig, script
After selecting Multisig Wallets
as current workspace, this is how your Address Picker will look like:
Note: using similar script logic for spend and for stake credentials is a common practice among Cardano multisig wallet software, but actually you are free to use different credential types on GameChanger Wallet.
GameChanger Wallet design has numerous advantages, since 2021 it has never been trivial the fact that transactions are built by the wallet itself and otherwise, while also supported, is not advised.
The Transaction Builder API functions are fully integrated with Workspace API, allowing wallet user interface (end users) and dapps (developers) to benefit from a seamless transition between building transactions from:
This is done by a couple of novel features such as Automatic Artifact Provision where transaction building recipes missing some pieces of information are healed using artifacts from the local workspace storage. Also the concept of Automatic Optional Signers plays a key role on calculating the worst transaction fee required in order to support the case where all the involved signatures referenced by native scripts are required. These and other underlying features ensures transaction validity and wallet-agnostic building arguments that are ready for any wallet context.
Here’s an example of a seamless and wallet-agnostic transaction design that works also for multisig wallets:
{
"type": "script",
"run": {
// building an off-chain transaction..
"build":{
"type": "buildTx",
"tx": {
"outputs": [
//an output of 1 coin to some address
{
"address":"addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
"assets": [
{
"policyId": "ada",
"assetName": "ada",
"quantity": "1000000"
}
]
}
],
// Add these lines if you also want seamless support for multisig wallets
"options": {
"autoProvision": {
"workspaceNativeScript": true
},
"autoOptionalSigners": {
"nativeScript": true,
}
}
}
},
// signing the transaction with user's private keys
"sign":{
"type": "signTxs",
"detailedPermissions": false,
"txs": [
"{get('cache.build.txHex')}"
]
},
// submitting the transaction to a Cardano Node, for going on-chain
"submit":{
"type": "submitTxs",
"txs": "{get('cache.sign')}"
}
}
}
🔍 See also: buildTx, signTxs, submitTxs, script
“But wait, what that has to do with me?”
</br>
Previous: Personal Account Wallets | Home: General Documentation |