Overview
In this post we will learn:
- Signing roles -
payer
,proposer
andauthorizers
- What is a signing function and how to create one
- How to sign a transaction with one of your wallets
- How to sign a transaction with your private key
Preface
Once we have our wallet or private keys, we can start signing transactions. Woohoo!
For better understanding of how this works, you will need some extra knowledge.
The signing process on Flow requires 3 different parties to sign the transaction:
-
Proposer
- this is the account, which proposes to include changes done by transaction into the blockchain. Proposer role can be filled by any account on Flow blockchain. -
Payer
is the account, who will pay the gas fees for transaction execution. Similar toproposer
, this can be any account, who have enough FLOW tokens to cover the fee and is eager to do it. -
Authorizers
- this is a list of accounts, who authorize any changes in their accounts, done by transaction they are signing. These should be accounts, that want to make said changes. If there is only a single account, it should still be a list of one.
💡All of those roles can be filled by the same account! You don’t need to have 3 “actors” to sign transaction 😅
Step 1 - Installation
Add "@onflow/fcl": "1.0.0"
as your dependency
Step 2 - Setup
Just like the last time we will import necessary methods and setup FCL. This time, though, we will extend our configuration with some new values to enable Wallet Discovery.
FCL Wallet Discovery
allows you to connect to all known and registered wallets at once! If you are coming from Ethereum, you can think about it as Flow variant of WalletConnect sans multichain support 😅
Note that we are using slightly different way to specify config by passing in object instead of calling multiple .put
calls:
// Import methods from FCL
import { query, config } from "@onflow/fcl";
// Configure FCL
config({
// Use Testnet Access Node
"accessNode.api": "https://rest-testnet.onflow.org",
// We will also specify the network as some of the FCL parts need it to properly do it's work
"flow.network": "testnet",
// This will be the title of our DApp
"app.detail.title": "Meow DApp",
// This is just a kitten photo, we will use for the icon
"app.detail.icon": "https://placekitten.com/g/200/200",
// Next two will define where Wallet Discovery is located
"discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn",
"discovery.authn.endpoint":
"https://fcl-discovery.onflow.org/api/testnet/authn",
// We will also set alias for the contract we will use in this example
"0xBasic": "0xafabe20e55e9ceb6"
});
You can read more information about those and other configuration keys on FCL documentation page
Step 3 - Get Current Chain State
We’ve deployed a Basic contract at 0xafabe20e55e9ceb6
. If you click on that link it will lead you to Flow View Source. You can see there is a single public variable counter
, that we can read and a couple of methods to modify it. Let’s use the knowledge from previous articles and read it.
const readCounter = async () => {
const cadence = `
import Basic from 0xBasic
pub fun main():UInt{
return Basic.counter
}
`;
const counter = await query({ cadence });
console.log({ counter });
};
This simple query looks exactly like what we’ve built before, except this time we are using 0xBasic
alias for the address. FCL will use the value we set in the config - 0xafabe20e55e9ceb6
- and replace it. We are doing it this way, cause we will make calls to the contract from our transaction as well. So we have this value nice and handy in one place and we don’t need to constantly reference it or memorize it 😉
You should see some output in your console, similar to this (number can be different, as there are multiple users, who would want to alter it):
{counter: "19"}
Step 4 - Modify Counter
We will use method mutate
provided by FCL package to modify the state of the chain. In its most basic form mutate
is very similar to query
, but it’s using some hidden mechanisms in order to sign the transaction (we will demystify them in later article).
Let’s define another method shiftCounter
, which we will be using to modify the value of the counter
. If you go back to the Basic
contract you will see that it defines two methods. We will use incrementCounterBy(_ step: UInt8)
We will need to pass UInt8
value as an argument to the transaction - we do it the same way we’ve done previously using the query
method.
const shiftCounter = async (value) => {
// Our Cadence code. Notice we are using alias here as well
const cadence = `
import Basic from 0xBasic
transaction(shift: UInt8){
prepare(signer: AuthAccount){
Basic.incrementCounterBy(shift)
}
}
`;
// List of arguments
const args = (arg, t) => [
// We need to pass UInt8 value, but it should be a string, remember? :)
arg(value.toString(), t.UInt8)
];
// "mutate" method will return us transaction id
const txId = await mutate({
cadence,
args,
limit: 999
});
// We will use transaction id in order to "subscribe" to it's state change and get the details
// of the transaction
const txDetails = await tx(txId).onceSealed();
return txDetails
};
Wait, but what about those roles we’ve mentioned above. You see, mutate
will populate all of them with the current authenticated user…
That’s part of the FCL and Wallet Discovery magic 🎇. If you don’t specify any role in that mutate
method it will ask you to connect to the wallet and then use selected account as current user. Then it will use all the necessary params to fill in the roles in that interaction. Pretty cool, ha? 😎
Finally
Let's add an IIFE at the end of the file and populate it with methods we have just defined:
(async () => {
console.clear();
// You can [un]comment this line to toggle Wallet Discovery
unauthenticate();
// Let's read value, currently set on contract
await readCounter();
// Let's modify the state now
const txDetails = await shiftCounter(13);
console.log({ txDetails });
// Now we can check that value is actually changed
await readCounter();
})();
When the code on the page will execute it will present you a popup, allowing to choose which wallet to use.
⚠️ You need to have Lilico installed in order for it to appear in the list. Consult one of our previous articles for details how to install and setup Lilico.
If you were to choose Lilico
, you will be presented with following flow. First it will ask you to connect wallet to our dapp:
Then ask for confirmation. You can expand/collapse script pane to check the code of the transaction.
💡 Actually, you should ALWAYS check what you are signing. Even on Testnet. It’s a good habit, that will help you make your interaction with Flow more secure 😉
Blocto will guide your through a similar process:
And it also has a way to check the transaction code:
After all said and done you should see the following in your console (numbers might change):
{ counter: "234" }
{ txDetails: Object }
{ counter: "247" }
Expanding txDetails
will give your more information about the transaction:
blockId: "ad7605f10f9846dd1c5603723b1d12995a3152b19a1ca24e47a3a2e28cee0641"
status: 4
statusString: "SEALED"
statusCode: 0
errorMessage: ""
events: Array(3)
Until next time! 👋
Resources
- Full Source Code - https://codesandbox.io/s/dev-to-part-13-modify-chain-state-1oy1l7
-
FCL -
mutate
method - https://docs.onflow.org/fcl/reference/api/#mutate - Cadence - Transactions - https://docs.onflow.org/cadence/language/transactions/
- Lilico Wallet - https://lilico.app/
Other resources you might find useful:
- Flow Docs Site - https://docs.onflow.org/ - More detailed information about Flow blockchain and how to interact with it
- Flow Portal - https://flow.com/ - your entry point to Flow
- FCL JS - https://github.com/onflow/fcl-js - Source code and ability to contribute to the FCL JS library
- Cadence - https://docs.onflow.org/cadence/ - Introduction to Cadence
- Codesandbox - https://codesandbox.io - An amazing in-browser IDE enabling quick prototyping
Top comments (0)