Flow

Flow

#Overview

Flow is an L1 blockchain featuring a distinctive multi-role architecture designed to increase throughput and efficiency without resorting to sharding. It adopts Cadence as its smart contract language and FCL (Flow Client Library) as the primary protocol for dapp, wallet, and user interactions with the chain.

As of January 31, 2024, Magic supports FCL JS compatibility out of the box, without the need for an extension, making it the recommended approach for integration with Flow. When using FCL compatibility, gas is subsidized for all transactions. ⁠ ⁠For users previously utilizing the magic-ext/flow package, migrating your authentication and authorization functions to FCL is advised for future maintenance, access to new features, interoperable wallet standards, and more. Refer to the appropriate section below based on your current implementation.

For reference, see our demo and its code.

#FCL Compatability

Magic supports most FCL services, including authentication, pre-authorization, authorization, and user signatures. You will need to adjust the FCL configuration depending on the authentication method you wish to use.

note

To use FCL with Magic, no installation or package is necessary. Simply configure FCL's wallet by setting discovery.wallet to https://fcl.magic.link, granting access to Magic's services. The core Magic SDK originally facilitated opening a secure context within an iframe or new tab. Now, FCL assumes this role, ensuring that Magic security and behavior remain consistent with other chains.

#Installation

Magic interacts with the Flow blockchain via FCL. To get started, install FCL:

NPM
Yarn
01npm install @onflow/fcl

#Configuration

Based on the authentication method you would like to use for your users, set up your FCL configuration as follows:

Widget UI Login Form- Default

Typescript
01fcl.config().put(
02  'discovery.wallet',
03  `${FCL_BASE_URL}/authn?${new URLSearchParams({
04    apiKey,
05  })}`,
06)
07fcl.config().put('discovery.wallet.method', 'IFRAME/RPC')

Email OTP

Typescript
01fcl.config().put(
02  'discovery.wallet', 
03  `https://fcl.magic.link/authn?${new URLSearchParams({
04		apiKey: <yourKey>,
05		method: 'email-otp',
06		email: <your-email>
07	})}`,
08)
09fcl.config().put('discovery.wallet.method', 'IFRAME/RPC')

SMS

Typescript
01fcl.config().put(
02  'discovery.wallet',
03  `${FCL_BASE_URL}/authn?${new URLSearchParams({
04    apiKey,
05    method,
06    phoneNumber,
07  })}`,
08)
09fcl.config().put('discovery.wallet.method', 'IFRAME/RPC')

Social Providers SSO

Typescript
01fcl.config().put(
02  'discovery.wallet',
03  `https://fcl.magic.link/authn?${new URLSearchParams({
04    apiKey: <your-key>,
05    method: 'oauth',
06    provider: <your-provider>, // 'google'
07  })}`,
08)
09fcl.config().put('discovery.wallet.method', 'TAB/RPC')

OIDC

Typescript
01fcl.config().put(
02  'discovery.wallet',
03  `https://fcl.magic.link/authn?${new URLSearchParams({
04    method: 'oidc',
05    jwt: <your-jwt>,
06    providerId: <your-provider-id>,
07  })}`,
08)
09fcl.config().put('discovery.wallet.method', 'IFRAME/RPC')

You can also utilize multiple authentication methods by dynamically setting the FCL configuration based on user input.

#Login

Once you have configured FCL with at least one of the authentication methods above, you can then log in your user. The following demonstrates an example of using email OTP with a simple form handler to submit the user input:

Typescript
01import * as fcl from '@onflow/fcl';
02
03const FLOW_NETWORK = 'testnet'; // or 'mainnet'
04const MAGIC_API_KEY = 'your-magic-api-key-here'; // Adjusted to MAGIC_API_KEY
05const FCL_BASE_URL = 'https://fcl.magic.link';
06
07const onSubmit = async (email) => {
08  try {
09    // Ensure email is provided
10    if (!email) throw new Error('Email is required');
11
12    // Configure FCL for the network and set up for email OTP authentication
13    fcl.config()
14      .put('flow.network', FLOW_NETWORK)
15      .put('accessNode.api', FLOW_NETWORK === 'mainnet' ? 'https://rest-mainnet.onflow.org' : 'https://access-testnet.onflow.org')
16      .put('discovery.wallet', `${FCL_BASE_URL}/authn?${new URLSearchParams({ apiKey: MAGIC_API_KEY, method: 'email-otp', email })}`)
17      .put('discovery.wallet.method', 'IFRAME/RPC');
18
19    // Attempt to authenticate the user
20    const user = await fcl.authenticate();
21    if (!user.loggedIn) throw new Error("You're not logged in");
22
23    // Log the success to console or proceed further as needed
24    console.log('User successfully logged in:', user);
25  } catch (e) {
26    console.error('Authentication error:', e.message);
27  }
28};

#Common Methods

#Send Transaction

Getting Test Flow

Before you can send transaction on the Flow blockchain, you'll need to acquire some test Flow (Flow's native cryptocurrency for test network).

  1. Go to our Flow Example application
  2. Login with any method
  3. Copy your Flow public address
  4. Go to the Flow Faucet
  5. Fill in the form and paste your copied Flow public address in the address field
  6. You can receive 1000 test Flow
  7. Now you can use your test Flow in our example app

Use FCL

For any authorization required from the user after authentication, simply use FCL as you would with any other Flow wallet. Magic will subsidze the cost of all gas used for transactions as the payer role.

Typescript
01import * as fcl from '@onflow/fcl';
02
03const handleMutate = async () => {
04  const user = await fcl.currentUser().snapshot()
05  console.log({ user })
06
07  const transactionId = await fcl.mutate({
08    cadence: `
09    transaction(a: Int, b: Int, c: Address) {
10      prepare(acct: AuthAccount) {
11        log(acct)
12        log(a)
13        log(b)
14        log(c)
15      }
16    }
17  `,
18    args: (arg: any, t: any) => [
19      arg(6, t.Int),
20      arg(7, t.Int),
21      arg(user.addr, t.Address),
22    ],
23  })
24
25  console.log({ transactionId })
26
27  return transactionId
28}

#Widget UI

Once your user is authenticated, you can use our Widget UI to pull up your users' wallet. This requires you to install Magic, and the Flow extension to do so - see the instructions below for detail.

Typescript
01import { Magic } from 'magic-sdk';
02import { FlowExtension } from '@magic-ext/flow';
03
04export default function WalletUIButton() {
05  // Initialize Magic with the Flow extension
06  const magic = new Magic('YOUR_API_KEY', {
07    extensions: [
08      new FlowExtension({
09        rpcUrl: 'https://rest-testnet.onflow.org',
10        network: 'testnet',
11      }),
12    ],
13  });
14
15  return (
16    <div>
17      <h2>Magic FCL Wallet</h2>
18      <button onClick={() => magic.wallet.showUI()}>View Wallet</button> // Assumes user is logged in
19    </div>
20  );
21}

#Magic Extension

This section will cover how to use our previous Flow integration via a custom extension that can be used alongside the core Magic SDK. You can use this extension to access Magic functionality alongside your FCL integration, however you do not need to use the authorization function shown here.

#Installation

Magic interacts with the Flow blockchain via Magic's extension NPM package @magic-ext/flow. The Flow extension also lets you interact with the blockchain using methods from Flow's Javascript SDK.

To get started, install the following dependencies for your project:

NPM
Yarn
01npm install @magic-ext/flow magic-sdk

#Initialization

The Magic class is the entry-point to the Magic SDK. It must be instantiated with a Magic publishable key.

note

If this is your first time using Magic with Flow, you may need to wait up to 30 seconds after clicking the magic link before your login completes because Magic has to wait for the Flow blockchain to confirm a transaction that creates your account.

ES Modules/TypeScript

Typescript
01import { Magic } from 'magic-sdk';
02import { FlowExtension } from '@magic-ext/flow';
03
04const magic = new Magic('YOUR_API_KEY', {
05  extensions: [
06    new FlowExtension({
07      // testnet or mainnet to connect different network
08      rpcUrl: 'https://rest-testnet.onflow.org',
09      network: 'testnet'
10    }),
11  ],
12});

#Login

You can use magic.flow.getAccount() method to let users login.

Typescript
01import * as fcl from '@onflow/fcl';
02
03import { Magic } from 'magic-sdk';
04import { FlowExtension } from '@magic-ext/flow';
05
06const magic = new Magic('YOUR_API_KEY', {
07  extensions: [
08    new FlowExtension({
09      rpcUrl: 'https://rest-testnet.onflow.org',
10      network: 'testnet' // testnet or mainnet to connect different network
11    }),
12  ],
13});
14
15const login = async () => {
16    const account = await magic.flow.getAccount();
17    console.log(account)
18}
19
20login()

#Common Methods

#Send Transaction

Getting Test Flow

Before you can send transaction on the Flow blockchain, you'll need to acquire some test Flow (Flow's native cryptocurrency for test network).

  1. Go to our Flow Example application
  2. Login with your email address
  3. Copy your Flow public address
  4. Go to the Flow Faucet
  5. Fill in the form and paste your copied Flow public address in the Address field
  6. You can receive 1000 test Flow
  7. Now you can use your test Flow in our example app

Call Extension Method

Note that the Magic Flow extension follows the method names and conventions of Flow's Javascript SDK. You can use the magic.flow.authorization() method to replace the fcl.authenticate().

ES Modules/TypeScript

Typescript
01import { Magic } from 'magic-sdk';
02import { FlowExtension } from '@magic-ext/flow';
03import * as fcl from '@onflow/fcl';
04
05const magic = new Magic('YOUR_API_KEY', {
06  extensions: [
07    new FlowExtension({
08      // testnet or mainnet to connect different network
09      rpcUrl: 'https://rest-testnet.onflow.org',
10      network: 'testnet'
11    }),
12  ],
13});
14
15// CONFIGURE ACCESS NODE
16fcl.config().put('accessNode.api', 'https://rest-testnet.onflow.org');
17
18// CONFIGURE WALLET
19// replace with your own wallets configuration
20// Below is the local environment configuration for the dev-wallet
21fcl.config().put('challenge.handshake', 'http://access-001.devnet9.nodes.onflow.org:8000');
22
23const AUTHORIZATION_FUNCTION = magic.flow.authorization;
24
25const verify = async () => {
26  try {
27    const getReferenceBlock = async () => {
28      const response = await fcl.send([fcl.getBlock()]);
29      const data = await fcl.decode(response);
30      return data.id;
31    };
32
33    console.log('SENDING TRANSACTION');
34    var response = await fcl.send([
35      fcl.transaction`
36      transaction {
37        var acct: AuthAccount
38
39        prepare(acct: AuthAccount) {
40          self.acct = acct
41        }
42
43        execute {
44          log(self.acct.address)
45        }
46      }
47    `,
48      fcl.ref(await getReferenceBlock()),
49      fcl.proposer(AUTHORIZATION_FUNCTION),
50      fcl.authorizations([AUTHORIZATION_FUNCTION]),
51      fcl.payer(AUTHORIZATION_FUNCTION),
52    ]);
53    console.log('TRANSACTION SENT');
54    console.log('TRANSACTION RESPONSE', response);
55
56    console.log('WAITING FOR TRANSACTION TO BE SEALED');
57    var data = await fcl.tx(response).onceSealed();
58    console.log('TRANSACTION SEALED', data);
59
60    if (data.status === 4 && data.statusCode === 0) {
61      console.log('Congrats!!! I Think It Works');
62    } else {
63      console.log(`Oh No: ${data.errorMessage}`);
64    }
65  } catch (error) {
66    console.error('FAILED TRANSACTION', error);
67  }
68};

#Resources