Skip to content

Instantly share code, notes, and snippets.

@orodio

orodio/intro.md Secret

Last active January 19, 2022 18:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orodio/3bf977a0bd45b990d16fdc1459b129a2 to your computer and use it in GitHub Desktop.
Save orodio/3bf977a0bd45b990d16fdc1459b129a2 to your computer and use it in GitHub Desktop.
Unofficial FCL Crash Course

Unofficial FCL Crash Course (Web App)

Following guide is relevant per @onflow/fcl@0.0.73

Installation

npm install --save @onflow/fcl

Configuration

Learn More: https://github.com/onflow/fcl-js/blob/master/docs/reference/configure-fcl.mdx

import {config} from "@onflow/fcl"

config({
  "accessNode.api": "https://testnet.onflow.org",
  "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn",
  "0xProfile": "0xba1132bc08f82fe2"
})

The above code configures three things.

  • accessNode.api this value tells FCL how to talk to the blockchain, in this case we are telling it to talk to the Flow Testnet.
  • discovery.wallet this value tells FCL how to discover the users wallets. Like most values you will configure it will change between instances of Flow (ie: This value will be different on Flow Testnet vs Flow Mainnet)
  • 0xProfile is the value on testnet for the Profile contract we are going to use. You can check it out here. Like the wallet discovery value it is going to be different on each instance of Flow the contract is deployed to.

Things that change between instances of Flow most likely belong in configuration. We recommend populating these values from environment variables where possible.

Configuration should only need to be called once, but must be called/executed before your application communicates with Flow, as it most likely configures how your application communicates with Flow.

Querying Flow

Learn More: https://github.com/onflow/flow-js-sdk/blob/master/packages/fcl/src/exec/query.md

import {query} from "@onflow/fcl"

await query({
  cadence: `
    import Profile from 0xProfile
    
    pub fun main(address: Address): Profile.ReadOnly? {
       return Profile.read(address)
    }
  `,

  args: (arg, t) => [
    arg("0xba1132bc08f82fe2", t.Address),
  ],
})

The above example reads data from Flow, in particular we are passing it my Flow Address, and using that Flow Address it is reading the Profile Resource I have in my storage via the Public Capability I have made publically available.

Authentication

https://github.com/onflow/fcl-js/blob/master/docs/reference/authentication.mdx

<html>
<body>
  <button id="login">Log In</button>
  <button id="signup">Sign Up</button>
  <button id="logout">Log Out</button>
  <script src="./app.js"></script>
</body>
</html>
// app.js
import {currentUser, authenticate, unauthenticate} from "@onflow/fcl"

const unsub = currentUser()
  .subscribe(function (user) {
    console.log("USER STATE CHANGED", user)
  })

document.getElementById("login").addEventListener(() => authenticate())
document.getElementById("signup").addEventListener(() => authenticate())
document.getElementById("logout").addEventListener(() => unauthenticate())

In the above code example, we are subscribing a callback function to the current user. Anytime the user authenticates or unauthenticates that callback is going to be called with the current users info. Clicking the buttons will trigger the appropriate flow in FCL.

Mutating Flow

Learn More: https://github.com/onflow/flow-js-sdk/blob/master/packages/fcl/src/exec/mutate.md

import {mutate, tx} from "@onflow/fcl"

// initialize current users account with a profile
var txidForInit = await mutate({
  cadence: `
    import Profile from 0xProfile

    transaction {
      prepare(currentUser: AuthAccount) {
        if !Profile.check(currentUser.address) {
          currentUser.save(<- Profile.new(), to: Profile.privatePath)
          currentUser.link<&Profile.Base{Profile.Public}>(Profile.publicPath, target: Profile.privatePath)
        }
      }
    }
  `
})


var initTxStatus = await tx(txidForInit).onceSealed()

The above example:

  • initializes a new Profile Resource
  • saves that Profile Resource into the current users storage
  • makes the Public interface for the Resource publically available using a Public Capability

I find it helpful to think about my Flow Accounts storage like I think about my home. I don't want you to put random things in my home, or touch my stuff without my express permission. Just as I don't want your application to touch the stuff in my storage, or to put stuff in my storage without my express permission. True ownership is scary, Flow enables digital ownership in a way, and to an extent, you most likely have never seen before. My stuff is my stuff, your stuff is your stuff, when I give you stuff it stops being my stuff, when you give me stuff it stops being your stuff. Those statements seem so obvious for physical things, but I can almost guarantee you will run into problems because you will forget those very same rules now apply to these digital things on Flow. For everyone out there, these are not the rules that digital things have followed, you are most likely not used to thinking about things in this way. Think of Cadence Resources as actual physical things, the storage as someones home, and private keys as physical keys that unlock the front door to that home. Good Luck, Have Fun.

Many people assume you can just put a Resource in a Flow Accounts storage automatically when a user logs into your application. If I was to liken that assumption to the real world I would say that is sort of like us shaking hands and then you putting a couch in my living room. I also sort of like to think about it like location information on a phone, you need to ask permission to know my location. You need to ask permission to put a couch in my house, you need to ask permission to put a resource in my storage. Authentication does not equal permission.

// update name on profile

var txidForUpdate = await mutate({
  cadence: `
    import Profile from 0xProfile

    transaction(name: String) {
      prepare(acct: AuthAccount) {
        acct
          .borrow<&{Profile.Owner}>(from: Profile.privatePath)!
          .setName(name)
      }
    }
  `,
  args: (arg, t) => [
    arg("qvvg", t.String)
  ],
})

var updateTxStatus = await tx(txidForUpdate).onceSealed()

The above example sets the name on a Profile Resource stored in the current users storage. That resource needs to be there for the transaction to be able to update the name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment