gamechanger.wallet

Transactions / Payments

Introduction

While we are not going to cover in detail the Cardano eUTXO model, you must know the basics:

Your wallet balance is a sum of outputs someone else or you have previously sent to your address.

Every time you make a payment to another wallet you are sending groups of coins and native assets (tokens, NFTs,etc..) from previous outputs sent to your wallet (eUTXOs) to another address, while often sending some other outputs back to yourself as well (change outputs). The transaction feature that allow you to move assets from one wallet address to another is called outputs.

Also on Cardano we can store arbitrary data, and if you store it using one of many standardized formats you can interact with different applications, services, smart contracts and protocols. One of the kinds of arbitrary data you can use on a transaction is called metadata, or auxiliaryData.

So in this section the native transaction features we will be using are outputs and auxiliaryData.

One output

In GameChanger we define an asset through the assetName and policyId properties.

Finally, remember that to send assets to a wallet you need to know itโ€™s address.

Here is a minimal example of just 1 output of 1 tADA to 1 address.

{
    "type": "script",
    "run": {
        // we craft the off-line transaction, an hexadecimal structure (CBOR)
        "build":{
            "type": "buildTx",
            "tx": {
                //This is the list of transaction outputs
                //"whom" to send to, "how much" to send and "what" to send
                "outputs": [
                    {
                        "address":"addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
                        "assets": [{ 
                            "policyId": "ada", 
                            "assetName": "ada", 
                            // we ALWAYS work with BigNum, lovelace amounts can get so big that is safer to express them as text
                            "quantity": "1000000" 
                        }]
                    }
                ]
            }
        },
        // we ask the user to sign the transaction hexadecimal CBOR with all the available and required private keys
        "sign":{
            "type": "signTxs",
            // everything on GCScript is based on deterministic permissions,
            // but transactions may require different inputs (UTXOs) when built
            // so on these cases we cannot promise a set of permissions upfront  
            "detailedPermissions": false,
            "txs": [
                "{get('cache.build.txHex')}"
            ]
        },
        // until now this point the transaction is like a signed-check non-handed to anybody. Let's now go "for real"
        // once the CBOR carries the embedded signatures or key witnesses, then we can deliver it to a Cardano Node to go on-chain
        "submit":{
            "type": "submitTxs",
            "txs": "{get('cache.sign')}"
        }
    }
}

Run on Cardano Pre-Production Testnet

๐Ÿ” See also: buildTx, signTxs, submitTxs, script

When the script runs on a userโ€™s wallet, a transaction with one outgoing output will be built, require to be signed through wallet user interface and submitted to the blockchain.

Users will be spending 1 tADA from the output + around 0.18 tADA for network fees. The bigger in bytes the transaction gets, bigger will be the fee.

Important:

The fees, the outputs, everything is written in stone, once signed and submitted it will not spend more nor less funds from a user balance randomly or by any means. This is why we say Cardano transactions are deterministic.

Multiple outputs

Here is an example for making a payment to 6 addresses on a single transaction, using GCScript arguments and inline scripting (ISL) for convenience, and this time exporting the transaction hash (the unique ID of transactions that can be used later for checking their status on the chain).

{
    "type": "script",
    "title": "๐Ÿš€ 6-in-1 Payment",
    "description": "This is a payment request to send 1 tADA to 6 different addresses on a single transaction",
    "args":{
        "addressA": "addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
        "addressB": "addr_test1vrp4jqn97fg7ucmhpsncfm87hcg3h788alzfpapme9yyj9cvlh24z",
        "addressC": "addr_test1vpzmm2ffh9qjcvckw8sg5v9ekfkkn5uw4u9le8avhvtprxg9zqwll",
        "addressD": "addr_test1vz4nsqaw9hnlzzqaersvwldxr2me4s20qw56uax04fnk22c25mjz7",
        "addressE": "addr_test1vzqtssvqusv2qp8tchw0hsyvzp7k0unp0h0w2y9n2m3dxxs6w79am",
        "addressF": "addr_test1vqqj3qgy50d26lv7kzqlyc6s4qr343694nd9wckkq9vf5ygfalzy8"
    },
    "exportAs": "multiPayment",
    "return": {
        "mode": "last"
    },
    "run": {
        "assetsToSend":{
            "type":"data",
            "value":[
                {
                    "policyId": "ada",
                    "assetName": "ada",
                    "quantity": "1000000"
                }
            ]
        },
        "build": {
            "type": "buildTx",
            "title": "๐Ÿš€ 6-in-1 Payment",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressA')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressB')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressC')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressD')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressE')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressF')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
        "sign": {
            "type": "signTxs",
            "detailedPermissions": false,
            "txs": [
                "{get('cache.build.txHex')}"
            ]
        },
        "submit": {
            "type": "submitTxs",
            "txs": "{get('cache.sign')}"
        },
        "results": {
            "type": "macro",
            "run": {
                "txHash":"{get('cache.build.txHash')}"
            }
        }
    }
}

Run on Cardano Pre-Production Testnet

๐Ÿ” See also: data, buildTx, signTxs, submitTxs, macro, script

When the script runs on a userโ€™s wallet, the transaction will be built, signed and submitted, the script will return something like this:

{
  "exports": {
    "multiPayment": {
      "txHash": "2ced21a5f6a978d3e95e14828f0bd974082cbc9c34c366b3b784214910ca40e4"
    }
  }
}

As you can see, there is no coin selection being done by the developer here (cherry-picking which UTXOs to consume to build the new transaction), nor the change output design (the calculation of which values you are not spending on the transaction that you need to send back to your wallet). This is because by default coin selection and change output optimization are taken care by GameChanger Wallet itself, nor by users delegating them complicate tasks like late-fixing their own wallets, nor by dapps letting them hold the power to damage user wallets with unhealthy eUTXO management. This is the GameChanger Wallet way.

If you want to consume an specific eUTXO you can do it by specifying it on the inputs property, also coin selection and change optimization can be customized or disabled using the options property.

Outputs + Auxiliary Data

You didnโ€™t notice that you already used metadata on the previous example.

When we included the title property on the buildTx function we instructed the wallet to use a metadata specification for attaching a title to the transaction that will be rendered on the wallet user interface. A title text that is now stored forever on your transaction on Cardano blockchain.

Letโ€™s send a unique message to the 6 addresses using CIP-20, a well known message over metadata specification or protocol.

{
    "type": "script",
    "title": "๐Ÿš€ 6-in-1 Payment",
    "description": "This is a payment request to send 1 tADA to 6 different addresses on a single transaction",
    "args":{
        "addressA": "addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
        "addressB": "addr_test1vrp4jqn97fg7ucmhpsncfm87hcg3h788alzfpapme9yyj9cvlh24z",
        "addressC": "addr_test1vpzmm2ffh9qjcvckw8sg5v9ekfkkn5uw4u9le8avhvtprxg9zqwll",
        "addressD": "addr_test1vz4nsqaw9hnlzzqaersvwldxr2me4s20qw56uax04fnk22c25mjz7",
        "addressE": "addr_test1vzqtssvqusv2qp8tchw0hsyvzp7k0unp0h0w2y9n2m3dxxs6w79am",
        "addressF": "addr_test1vqqj3qgy50d26lv7kzqlyc6s4qr343694nd9wckkq9vf5ygfalzy8"
    },
    "exportAs": "multiPayment",
    "return": {
        "mode": "last"
    },
    "run": {
        "assetsToSend":{
            "type":"data",
            "value":[
                {
                    "policyId": "ada",
                    "assetName": "ada",
                    "quantity": "1000000"
                }
            ]
        },
        "build": {
            "type": "buildTx",
            "title": "๐Ÿš€ 6-in-1 Payment",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressA')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressB')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressC')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressD')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressE')}",
                        "assets":"{get('cache.assetsToSend')}"
                    },
                    {
                        "address":"{get('args.addressF')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ],
                "auxiliaryData": {
                    "674": {
                        "msg": [
                            "Created with GameChanger Wallet API. CIP-20 message ahead\n",
                            "Invoice-No: 1234567890\n",
                            "Customer-No: 555-1234\n",
                            "P.S.1: i will shop again at your store :-) ",
                            "I find you very sexy, what's your Cardano address? ;-)"
                        ]
                    }
                }
            }
        },
        "sign": {
            "type": "signTxs",
            "detailedPermissions": false,
            "txs": [
                "{get('cache.build.txHex')}"
            ]
        },
        "submit": {
            "type": "submitTxs",
            "txs": "{get('cache.sign')}"
        },
        "results": {
            "type": "macro",
            "run": {
                "txHash":"{get('cache.build.txHash')}"
            }
        }
    }
}

Run on Cardano Pre-Production Testnet

๐Ÿ” See also: data, buildTx, signTxs, submitTxs, macro, script

Run the example with addresses from wallets you own or you have access to, to check the senderโ€™s message ;)

This example is great for showing how building on Cardano looks like. You are combining several protocol native features like outputs and auxiliaryData into a single transaction. You can pack all the features you want on a transaction, until you hit the transaction size limit. The more bytes a transaction has, more network fee you will require to pay, but it never gets too big in comparison with what you can end up paying on a bad (network busy) day on Ethereum.

Multiple transactions

GameChanger is a wallet designed for simple and also very advanced use cases. With GCFS, our On-Chain File Storage Protocol on Cardano you can store entire file systems on-chain, and this is done by splitting the file system data into a sequence of several transactions on a single GCScript execution, so buildTx, signTxs and submitTxs are functions well prepared to handle more than one transaction at a time, with an eUTXO management system that prevents consuming same eUTXOs on several concurrent transactions and other features like improved user experience for multi-transaction (even multi-sig) operations as no other wallet can do on Cardano.

Letโ€™s create now the opposite situation, we will send 1 tADA to each of the 6 previous addresses but now on 6 different transactions with one outgoing output each.

{
    "type": "script",
    "title": "๐Ÿš€ 1-in-6 Payments",
    "description": "This is a payment request to send 1 tADA to 6 different addresses on 6 different transactions",
    "args":{
        "addressA": "addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
        "addressB": "addr_test1vrp4jqn97fg7ucmhpsncfm87hcg3h788alzfpapme9yyj9cvlh24z",
        "addressC": "addr_test1vpzmm2ffh9qjcvckw8sg5v9ekfkkn5uw4u9le8avhvtprxg9zqwll",
        "addressD": "addr_test1vz4nsqaw9hnlzzqaersvwldxr2me4s20qw56uax04fnk22c25mjz7",
        "addressE": "addr_test1vzqtssvqusv2qp8tchw0hsyvzp7k0unp0h0w2y9n2m3dxxs6w79am",
        "addressF": "addr_test1vqqj3qgy50d26lv7kzqlyc6s4qr343694nd9wckkq9vf5ygfalzy8"
    },
    "exportAs": "multiTransaction",
    "return": {
        "mode": "last"
    },
    "run": {
        "assetsToSend":{
            "type":"data",
            "value":[
                {
                    "policyId": "ada",
                    "assetName": "ada",
                    "quantity": "1000000"
                }
            ]
        },
        "buildA": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment A",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressA')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
        "buildB": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment B",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressB')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
       "buildC": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment C",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressC')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
       "buildD": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment D",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressD')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
       "buildE": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment E",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressE')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },       
        "buildF": {
            "type": "buildTx",
            "title": "๐Ÿš€ Payment F",
            "tx": {
                "outputs": [
                    {
                        "address":"{get('args.addressF')}",
                        "assets":"{get('cache.assetsToSend')}"
                    }
                ]
            }
        },
        "sign": {
            "type": "signTxs",
            "detailedPermissions": false,
            "txs": [
                "{get('cache.buildA.txHex')}",
                "{get('cache.buildB.txHex')}",
                "{get('cache.buildC.txHex')}",
                "{get('cache.buildD.txHex')}",
                "{get('cache.buildE.txHex')}",
                "{get('cache.buildF.txHex')}"
            ]
        },
        "submit": {
            "type": "submitTxs",
            "txs": "{get('cache.sign')}"
        },
        "results": {
            "type": "macro",
            "run": {
                "txHashes":{
                    "A":"{get('cache.buildA.txHash')}",
                    "B":"{get('cache.buildB.txHash')}",
                    "C":"{get('cache.buildC.txHash')}",
                    "D":"{get('cache.buildD.txHash')}",
                    "E":"{get('cache.buildE.txHash')}",
                    "F":"{get('cache.buildF.txHash')}"
                }
            }
        }
    }
}

Run on Cardano Pre-Production Testnet

๐Ÿ” See also: data, buildTx, signTxs, submitTxs, macro, script

And after the script builds the 6 transactions sequentially, sign and submit all the transactions at once, you will receive some exported results like this:

{
  "exports": {
    "multiTransaction": {
      "txHashes": {
        "A": "8f50bfefc6639bab1f40e404dbf75db19b95edad76bf32ce046b3937dbb9988a",
        "B": "b637f3296a05bbcd0267c64e86e80cd79215cb49be6fdc58bd62c25b06cfd69e",
        "C": "b0574c4623507b04a7cff4a5e2c9896a3d22f33235ae258b4e11b1cd84da4e0c",
        "D": "50780b3b8f210bcaf55f64576d1106feba29309e26f6647aba9b3737244ca8c8",
        "E": "d6b0c2fcd77a4642b07e75185e82166020ecf80a029da706ce4f71f054b16121",
        "F": "529bcc546bc5b9f41f741d57f461b1db5f193ee5eb1d91ba0e3ab4a447a0f5a7"
      }
    }
  }
}

Multi-asset outputs

โ€œWhat about sending coins, NFTs and Tokens on a single transaction output?โ€

Well for that you will need those on your wallet. We will mint some on the next section but for now keep in mind that this is how it looks like:

{
    "type": "script",
    "run": {
        "build":{
            "type": "buildTx",
            "tx": {
                "outputs": [
                    {
                        "address":"addr_test1vrv2myc3je5q7fxfnajjgj4qnynhdp82rsylnj2lm8yawtswwgyaw",
                        "assets": [
                            {
                                "policyId": "94154242672900d611533747d21b8d6df80933d6f9805756927d5269",
                                "assetName": "GameChangerNFT #1",
                                "quantity": "1"
                            },
                            {
                                "policyId": "a1267cbb4f224250db4f7735e571b7b4bdc8ea0be7180756b7fb6b3e",
                                "assetName": "FakeUSD",
                                "quantity": "5"
                            }
                        ]
                    }
                ]
            }
        },
        "sign":{
            "type": "signTxs",
            "detailedPermissions": false,
            "txs": [
                "{get('cache.build.txHex')}"
            ]
        },
        "submit":{
            "type": "submitTxs",
            "txs": "{get('cache.sign')}"
        }
    }
}

Run on Cardano Pre-Production Testnet

๐Ÿ” See also: buildTx, signTxs, submitTxs, script

On Cardano there are several ledger rules, one of those is that there is a practical minimum coin value per output that you need to fulfill in order for a transaction to be valid.

On this transaction we did not specify any coin to be sent on the output, so GameChanger will adapt it to became valid by adding a minimum coin value (around 1.25tADA) on the output for you, so you donโ€™t have to calculate this on your own.

Remember you can always review each GCScript prior to execution, and each transaction prior to be signed.

Previous: Overview Next: Minting