Build on Flow | Learn FCL - 9. How to pass Optional and Path Arguments to Scripts
Tags: javascript, tutorial, fcl, onflow
Published: 2022-06-22 01:44:00

## Overview

[Last time](https://dev.to/onflow/build-on-flow-learn-fcl-2-pass-arguments-to-scripts-393f) we covered how to pass arguments in scripts, we’ve intentionally omitted [Optionals](https://docs.onflow.org/cadence/language/values-and-types/#optionals) and [Paths](https://docs.onflow.org/cadence/language/accounts/#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](https://joshuahannan.medium.com/optionals-in-cadence-not-optional-fb39bb4b0081) 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](https://docs.onflow.org/cadence/language/capability-based-access-control/#gatsby-focus-wrapper) 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:

```jsx
// 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:

```jsx
// 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 😊

```jsx
// 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:

```jsx
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:

```jsx
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:

```jsx
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

```jsx
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 at `amount` key might not exist, so it will return us `Maybe(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:

```jsx
(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:

```jsx
{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](https://codesandbox.io/s/dev-to-fcl-09-optional-arguments-vt0d11)
- **Cadence: Optionals** - [https://docs.onflow.org/cadence/language/values-and-types/#optionals](https://docs.onflow.org/cadence/language/values-and-types/#optionals)
- **Cadence: Paths -** [https://docs.onflow.org/cadence/language/accounts/#paths](https://docs.onflow.org/cadence/language/accounts/#paths)
- Cadence: Capabilities - [https://docs.onflow.org/cadence/language/capability-based-access-control](https://docs.onflow.org/cadence/language/capability-based-access-control/#gatsby-focus-wrapper)

Other resources you might find useful:

- **Flow Docs Site** - [https://docs.onflow.org/](https://docs.onflow.org/) - More detailed information about Flow blockchain and how to interact with it
- **Flow Portal** - [https://flow.com/](https://flow.com/) - your entry point to Flow
- **FCL JS** - [https://github.com/onflow/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/](https://docs.onflow.org/cadence/) - Introduction to Cadence
- **Codesandbox** - [https://codesandbox.io](https://codesandbox.io/) - An amazing in-browser IDE enabling quick prototyping