Skip to content

Instantly share code, notes, and snippets.

@drichar
Created August 5, 2023 16:53
Show Gist options
  • Save drichar/5df7c7478afa5d78197e9ed3e3f0b8cc to your computer and use it in GitHub Desktop.
Save drichar/5df7c7478afa5d78197e9ed3e3f0b8cc to your computer and use it in GitHub Desktop.
An example of how to use the NFDomains API to send assets to and from an NFD's vault

NFDomains Vault Example

In this guide we will provide an example of how to use the NFDomains API to send assets to and from an NFD's vault, as a reference for adding Vault support in your own application.

Overview

A vault is an Algorand account controlled by an NFD's smart contract that can automatically opt-in to assets it receives.

Vaults are locked by default, meaning only the NFD's owner can send assets to it. When the owner unlocks the vault, anyone can send assets to it. The owner can lock or unlock the vault at any time.

Setup

In this example we will create two very basic React components that will prompt the user to sign transactions for sending an asset to and from an NFD's vault.

We will use Axios for handling HTTP requests to fetch the transactions, and the @txnlab/use-wallet library to sign and send the transactions. Make sure these libraries are installed in your project:

npm install axios @txnlab/use-wallet

Note: @txnlab/use-wallet is not required, but it is used in this example to simplify the process of signing and sending transactions. See https://github.com/TxnLab/use-wallet for full setup instructions.

API

First we define functions for making requests to the NFD Vaults API using Axios. The sendToVault and sendFromVault functions will make POST requests to the /nfd/vault/sendTo/{name} and /nfd/vault/sendFrom/{name} endpoints, respectively.

// src/api.ts

import axios from 'axios'

const API_BASE_URL = 'https://api.testnet.nf.domains'

interface SendToVaultRequestBody {
  /* Base amount (in base units of specified asset - so decimals must be considered) of asset to
  send. If multiple assets specified, amount is ignored and ALL of each are sent */
  amount: number
  /* Algorand ASA IDs to transfer (and opt-in inside vault if necessary) - use asset 0 to send
  ALGO. Specifying multiple assets means ALL of each are sent and amount is ignored. 13 is max
  assets that can be specified if they're being sent (2 for MBR payments, 2 for opt-in txns (8+4
  asset opt-ins), 12 asset transfers). If opt-in only then 64 is maximum (1 MBR per 8 assets, 8
  assets per txn * 8 txns) */
  assets: number[]
  /* Optional note to include in asset send transaction */
  note?: string
  /* Whether to only opt-in to the asset, instead of including asset transfer txn */
  optInOnly: boolean
  /* Sender of transaction, an Algorand account */
  sender: string
}

export function sendToVault(name: string, data: SendToVaultRequestBody) {
  return axios<string>({
    url: `${API_BASE_URL}/nfd/vault/sendTo/${name}`,
    method: 'post',
    headers: { 'Content-Type': 'application/json' },
    data
  })
}

interface SendFromVaultRequestBody {
  amount: number
  /* Algorand ASA IDs to transfer FROM vault - use asset 0 to send ALGO. Specifying multiple assets
  means ALL of each are sent and amount is ignored. If destination is vault and needs to opt-in,
  then need MBR/opt-in pairs (5 pairs - 8 opt-ins each - 40 assets), then 6 send calls of 7 assets
  w/ 5 at end for total of 40. If destination is already opted-in, then 112 (7 per txn, 16 tnxs) is
  max. */
  assets: number[]
  /* Optional note to include in asset send transaction */
  note?: string
  /* Algorand account or NFD Name (if vault destination) the asset(s) should be sent to */
  receiver: string
  /* Specifies that the receiver account is something the caller can sign for. If specified, then
  opt-in transactions it signs may be included */
  receiverCanSign?: boolean
  /* Account or NFD Vault the asset should be sent to (if allowed) */
  receiverType?: 'account' | 'nfdVault'
  /* Sender of transaction, must be NFD owner */
  sender: string
}

export function sendFromVault(name: string, data: SendFromVaultRequestBody) {
  return axios<string>({
    url: `${API_BASE_URL}/nfd/vault/sendFrom/${name}`,
    method: 'post',
    headers: { 'Content-Type': 'application/json' },
    data
  })
}

Send To Vault

Using the sendToVault function, we create a component that prompts the user to sign and send transactions for sending an asset to an NFD's vault.

  • If the sender is not the owner, the receiving vault must be unlocked to auto opt-in and receive assets
  • If the vault is locked, only the NFD owner can send assets to it
  • The NFD must be upgraded to smart contract version 2.6 or higher to receive vault assets
// src/sendToVault.ts

import React from 'react'
import { useWallet, encodeNFDTransactionsArray, TransactionsArray } from '@txnlab/use-wallet'
import { sendToVault } from './api'

export default function SendToVaultDemo() {
  const wallet = useWallet()
  const { activeAddress } = wallet

  async function signAndSendTransactions() {
    const NFD_NAME = 'doug.algo'
    const ASSET_ID = 212389838
    const AMOUNT = 1

    try {
      if (!activeAddress) {
        throw new Error('No account connected')
      }

      const response = await sendToVault(NFD_NAME, {
        sender: activeAddress,
        assets: [ASSET_ID],
        amount: AMOUNT,
        optInOnly: false
      })

      if (typeof response.data !== 'string') {
        throw new Error('Failed to fetch transactions')
      }

      const transactionsArray = JSON.parse(response.data) as TransactionsArray
      const signedTransactions = await wallet.signTransactions(
        encodeNFDTransactionsArray(transactionsArray)
      )

      const { id } = await wallet.sendTransactions(signedTransactions)

      console.log(
        `Successfully sent asset ${ASSET_ID} to ${NFD_NAME}'s vault! Transaction ID: ${id}`
      )
    } catch (error) {
      console.error(`Send to vault failed`, error)
    }
  }

  if (!activeAddress) {
    return <p>Connect an account first.</p>
  }

  return (
    <div>
      <button type="button" onClick={signAndSendTransactions}>
        Sign and send transactions
      </button>
    </div>
  )
}

Send From Vault

Using the sendFromVault function, we create a component that prompts the user to sign and send transactions for sending an asset from the vault of an NFD they own.

  • The transactions must be signed by the NFD's owner account
  • The receiver can be either an Algorand account or another NFD's vault (same rules described above apply to a receiving vault)
  • The NFD must be upgraded to smart contract version 2.6 or higher
// src/sendFromVault.ts

import React from 'react'
import { useWallet, encodeNFDTransactionsArray, TransactionsArray } from '@txnlab/use-wallet'
import { sendFromVault } from './api'

export default function SendFromVaultDemo() {
  const wallet = useWallet()
  const { activeAddress } = wallet // must match the NFD's `owner` account

  async function signAndSendTransactions() {
    const NFD_NAME = 'doug.algo'
    const ASSET_ID = 212389838
    const AMOUNT = 1
    const RECEIVER = 'receiver-address' // replace with valid Algorand account

    try {
      if (!activeAddress) {
        throw new Error('No account connected')
      }

      const response = await sendFromVault(NFD_NAME, {
        assets: [ASSET_ID],
        amount: AMOUNT,
        receiver: RECEIVER,
        receiverType: 'account'
      })

      if (typeof response.data !== 'string') {
        throw new Error('Failed to fetch transactions')
      }

      const transactionsArray = JSON.parse(response.data) as TransactionsArray
      const signedTransactions = await wallet.signTransactions(
        encodeNFDTransactionsArray(transactionsArray)
      )

      const { id } = await wallet.sendTransactions(signedTransactions)

      console.log(
        `Successfully sent asset ${ASSET_ID} from ${NFD_NAME}'s vault! Transaction ID: ${id}`
      )
    } catch (error) {
      console.error(`Send from vault failed`, error)
    }
  }

  if (!activeAddress) {
    return <p>Connect an account first.</p>
  }

  return (
    <div>
      <button type="button" onClick={signAndSendTransactions}>
        Sign and send transactions
      </button>
    </div>
  )
}

Resources

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