Overview
Last time we covered how to pass arguments in scripts, we’ve intentionally omitted Optionals and Paths arguments to lower the mental load. Those 2 are still important and quite common, so we will cover them in this tutorial.
After you follow and complete example covered in this post you will be able to:
- pass any Optional argument, regardless of its base type
- pass Path arguments
But first, let’s look at what they are!
Theory
Optionals
are a bit like Schroedinger’s cat in programming: they can represent the presence or the absence of a value, when you check what’s inside. Optionals have two cases:
- there is a value
- there is
nil
(nothing)
One use case where you might find this useful (that comes to my mind) is when you want to pass a Dictionary {String: String?}
and want to initialize some of the fields with empty values (for whatever reason 🤷♂️)
Paths
arguments can be used when you want to use or access account storage in your script or transaction and doesn’t want to hardcode that value, but rather make it more flexible.
Let’s take a look at some common path /public/flowTokenVault
- this is a path to the publicly available Capability to access the FLOW token Vault resource. With this specific Capability you can use it to get reference to that resource to read it’s balance or deposit more tokens into it. Not all Capabilities are equal though! Some might give your more functionality and others less.
Back to our capability. If you take a look at it you can see that it consists of two parts:
-
/public - this is called a
domain
. - /flowTokenVault - and this is an identifier
Technically /
character is a separator, but I think it’s easier to see it this way 😉
There are only three valid domains: storage
, private
, and public
. In all of the scripts you will use public
domain - since it won’t allow you to access private
and storage
areas of other accounts.
Let’s try to pass both of those into some scripts! 💪
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:
// Import methods from FCL
import { query, config } from "@onflow/fcl";
// Specify the API endpoint - this time we will use Mainnet
const api = "https://rest-mainnet.onflow.org";
// Configure FCL to use mainnet as the access node
config().put("accessNode.api", api);
Step 3 - Pass Path arguments
We will try to pass 3 different paths - PublicPath
, PrivatePath
and StoragePath
- each one for respective domain:
// Path Arguments
const passPathArgument = async () => {
const cadence = `
pub fun main(public: PublicPath, private: PrivatePath, storage: StoragePath): PrivatePath{
// we can return any value, but let's return one of the Paths to see
// how FCL decodes it into value
return private
}
`;
// Since we are not gonna use any of those for actual data access
// we can construct any paths we like
// this one will encode "/public/flowTokenVault"
const publicPath = {
domain: "public",
identifier: "flowTokenVault"
};
// encode "/private/flowTokenVault"
const privatePath = {
domain: "private",
identifier: "flowTokenVault"
};
// encode "/storage/flowTokenVault"
const storagePath = {
domain: "storage",
identifier: "flowTokenVault"
};
// Notice that t.Path is not a function, but a constant!
const args = (arg, t) => [
arg(publicPath, t.Path),
arg(privatePath, t.Path),
arg(storagePath, t.Path)
];
const result = await query({ cadence, args });
console.log({ result });
};
Please, note that t.Path
is a constant, even though we are passing object 🤪
Step 4 - Pass Optional Integer
To pass Optional, we need to wrap the type into t.Optional()
call. And that’s it 😊
// Optionals
const passOptionalIntegers = async () => {
const cadence = `
pub fun main(a: Int?): Int?{
return a
}
`;
// We will set value of our Int argument to null to check that script works correctly
const a = null;
const args = (arg, t) => [arg(a, t.Optional(t.Int))];
const result = await query({ cadence, args });
console.log({ result });
};
Step 5 - Pass Other Optional Types
In similar fashion to our original post about arguments let’s pass other Optional types all at once:
const passMultiOptional = async () => {
const cadence = `
pub fun main(a: String?, b: Bool?, c: UFix64?, d: Address?): Address?{
return d
}
`;
// it's perfectly fine to pass non-nil values alongside "empty"
const a = "Hello";
const b = null;
const c = null;
const d = "0x01";
// Note the types are the same as we specify in Cadence code
const args = (arg, t) => [
arg(a, t.Optional(t.String)),
arg(b, t.Optional(t.Bool)),
arg(c, t.Optional(t.UFix64)),
arg(d, t.Optional(t.Address))
];
const result = await query({ cadence, args });
console.log({ result });
showResult("multiple", result);
};
Step 6 - Pass Optional Array and Array of Optionals
The difference between those two is in target for t.Optional()
wrapper.
Here’s array of Optionals:
const passArrayOfOptinalStrings = async () => {
const cadence = `
pub fun main(a: [String?]): String?{
return a[0]
}
`;
const a = ["Hello", null];
// Type of the argument is composed of t.Array, t.Optional and t.String
const args = (arg, t) => [arg(a, t.Array(t.Optional(t.String)))];
const result = await query({ cadence, args });
console.log({ result }); //
};
And this one for optional array of Strings:
const passOptionalArray = async () => {
const cadence = `
pub fun main(a: [String]?): String?{
if let arr = a {
return arr[0]
}
return nil
}
`;
const a = null;
// This time we will wrap our array in "t.Optional()" call
const args = (arg, t) => [arg(a, t.Optional(t.Array(t.String)))];
const optionalArray = await query({ cadence, args });
console.log({ optionalArray }); //
};
Step 7 - Pass Dictionary with Optional values
const passDictionaryOfOptionals = async () => {
// In this example we will pass a Cadence Dictionary as argument
// keys will be of type "String" and values will be Optionals of type "Int?"
const cadence = `
pub fun main(a: {String: Int?}): Int??{
return a["amount"]
}
`;
// Dictionaries should be represented as array of key/value pairs of respective types
// Note that we shall pass numeric value as string here
const a = [{ key: "amount", value: "42" }];
// Dictionary type is composed out of t.Dictionary, t.String and t.Int for our case
const args = (arg, t) => [
arg(a, t.Dictionary({ key: t.String, value: t.Optional(t.Int) }))
];
const result = await query({ cadence, args });
console.log({ result });
};
💡 Have you noticed that script in last example returns Double Optional
Int??
? That’s because value atamount
key might not exist, so it will return usMaybe(Maybe(Int))
- you will need to unwrap it twice if you want to use it later in the code 😉
Finally
Let's add an IIFE at the end of the file and populate it with methods we have just defined:
(async () => {
console.clear();
// Path arguments
await passPathArgument();
// Optional arguments
await passOptionalIntegers();
await passMultiOptional();
await passArrayOfOptinalStrings();
await passOptionalArray();
await passDictionaryOfOptionals();
})();
Some sparkling 🎇 moments after you should see the output in the console log:
{result: Object}
result: {
domain: "private",
identifier: "flowTokenVault"
}
{result: null}
{result: "0x0000000000000001"}
{result: "Hello"}
{optionalArray: null}
{result: "42"}
You did it - now those types are not a challenge to you! 💪
Hope you find information in this post useful and will get back for more 😉
Until next time! 👋
Resources
- Full Source Code - https://codesandbox.io/s/dev-to-fcl-09-optional-arguments-vt0d11
- Cadence: Optionals - https://docs.onflow.org/cadence/language/values-and-types/#optionals
- Cadence: Paths - https://docs.onflow.org/cadence/language/accounts/#paths
- Cadence: Capabilities - https://docs.onflow.org/cadence/language/capability-based-access-control
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)