Offramp Flow

To offramp to a Holyheld account the following steps should be completed:

  1. Get settings and ensure that offramping is available using getServerSettings method.
  2. Check that selected wallet or holytag can transact using methods: validateAddress for a wallet or getTagInfo for a $holytag.
  3. Provide two parameters: token and amount.
  4. Get binary data to pass as swap parameters using convertTokenToEUR or convertEURToToken methods.
  5. Call the topup method to execute the transaction.

🔔 Please note! If multiple wallet interactions are required (e.g. signing allowance and executing a transaction, or permit signing and executing a transaction) they will be processed automatically.

  1. Wait for the transaction hash of the operation to be received.

There are six functions available to use:

  • getServerSettings - to get server settings.
  • validateAddress - to get wallet information.
  • getTagInfo - to get holytag information.
  • convertTokenToEUR - to get a EUR quote for a specific amount of tokens.
  • convertEURToToken - to get a a token quote for a specific amount of EUR.
  • topup - to execute offramp.

getServerSettings Get settings

This method gets current state/settings when interacting with the service. Please always use this method to check:

  • if the feature is available;
  • the minimum and maximum allowed amounts to offramp.

🔔 Please note! Financial values are provided and consumed as strings to avoid floating point conversion problems.

(async () => {
  const data = await holyheldSDK.getServerSettings();
})();

Types:

type ServerExternalSettings = {
  external: {
    isTopupEnabled: boolean; // indicates if offramp is available at the moment
    isOnRampEnabled: true; // indicates if onramp is available at the moment
    maxTopUpAmountInEUR: string; // maximum amount (equivalent in EUR) that is allowed to be processed, for example: '1000'
    minTopUpAmountInEUR: string; // minimum amount (equivalent in EUR) that is allowed to be processed, for example '5'
    maxOnRampAmountInEUR: string; // maximum amount in EUR that is allowed to be processed, for example: '1000'
    minOnRampAmountInEUR: string; // minimum amount in EUR that is allowed to be processed, for example: '5'
  };
  common: {
    topUpFeePercent: string; // fee (in percent) that is deducted when making an offramp operation, for example: '0.75'
  };
}

validateAddress Get wallet information

User wallet address is a unique identifier which can have account, card and a $holytag bound to it. It is alphanumeric string. Wallet address must be a valid EVM (0x..., 42 chars) or Solana (Base58, ~32–44 chars) address.

🔔 Please note! Ethereum Name Service (ENS) and Solana Name Service (SNS) domains are not supported.

(async () => {
  const data = await holyheldSDK.validateAddress('0x000000000000000000000000000000000000dEaD'); // a wallet address could be preset or have to be entered by user, depends on what user flow you as a developer want to set
})();

Types:

type ValidateAddressResult = {
  isTopupAllowed: boolean;
  isOnRampAllowed: boolean;
}

getTagInfo Get tag information

$holytag is a unique identifier which can have account, card and multiple Ethereum and/or Solana addresses bound to it. $holytag is alphanumeric string with a few special characters allowed. A valid holytag can be as short as one (1) and as long as thirty one (31) characters.

🔔 Please note! The only two special characters allowed are: dash - and underscore _.

$holytag is usually displayed prepended with a $ prefix, for example: $JohnSmith holytag is JohnSmith or $PEPE holytag PEPE, etc.

Tags are stored case-sensitive for display, but not case-sensitive for search, and are not allowed to have multiple case variants registered. It means that if there is a holytag $ToTheMoon registered, other case sensitive variations are not allowed (e.g. $toTHEmoon).

Test $holytag

You can use SDKTEST as a test $holytag. All transactions to this $holytag will NOT trigger an actual fiat-corresponding transaction, but will return a fully valid response. There is no minimum amount set for the test $holytag. SDKTEST test $holytag works across all supported networks and tokens.

🔔 Please note! Funds from test transactions to SDKTEST can NOT be retrieved. Do not initiate large test transactions.

(async () => {
  // a tag name could be preset by the developer (you) or inputted by the user. It depends on the user flow you want to have
  const data = await holyheldSDK.getTagInfo('SDKTEST');
})();

Types:

type TagInfo = {
  found: boolean; // if the holytag exists and active
  tag?: string; // holytag name (case sensitive, as registered), for example: 'SDKTEST'
  avatarSrc?: string; // if set, a link to avatar image, for example example: 'https://holyheld.com/static/avatar.png'
}

getWalletBalances Get wallet balances

You can use getWalletBalances method to retrieve all tokens on the connected user wallet address. The full list of supported networks is here.

EVM Networks

(async () => {
  const data = await holyheldSDK.evm.getWalletBalances(
    '0x...', // user wallet address
  );
})();

Types:

import { Network } from '@holyheld/sdk';

type WalletTokenEVM = {
  name: string; // name of the token, for example: 'USD Coin'
  address: string; // smart contract address of the token, for example: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
  symbol: string; // token symbol, for example: 'USDC'
  decimals: number; // token decimal digits (as in ERC20 implementation), for example: 6
  network: Network; // network (blockchain), on which this token resides
  networkKind: NetworkKind // blockchain type
  iconURL: string; // logo (icon) for the token
  priceUSD: string; // current estimated price (in USD), for example: '1796.55'
  balance: string; // amount of token in the user's wallet, for example: '0.0000099999999'
  priceInEURForTopUp: string; // token price converted from USD to EUR, for example: '1726.53'
  meta: {
    permitData: {
      hasPermit: boolean; // if the token supports permit signatures
      permitType?: string; // type of permit signature, 'erc2612' is expected
      permitVersion?: string; // permit version (currently, if permit supported, '1' is expected)
    }
  }
};

type WalletBalancesEVM = {
  tokens: WalletTokenEVM[];
}

Solana Network

(async () => {
  const data = await holyheldSDK.solana.getWalletBalances(
    '...', // user wallet address
  );
})();

Types:

import { Network } from '@holyheld/sdk';

type WalletTokenSolana = {
  name: string; // name of the token, for example: 'Solana'
  decimals: number; // token decimal digits, for example: '9'
  address: string; // smart contract address of the token, for example: 'So11111111111111111111111111111111111111112'
  symbol: string; // token symbol, for example: 'SOL'
  network: SolanaNetwork; // network (blockchain), on which this token resides
  networkKind: NetworkKind.Solana // blockchain type
  iconURL: string; // logo (icon) for the token
  priceUSD: string; // example: '1796.55'  // current estimated price (in USD)
  balance: string; // amount of token in the user's wallet, for example: '0.0000099999999'
  priceInEURForTopUp: string; // token price converted from USD to EUR, for example: '1726.53'
  meta: {
    tokenProgramId?: string; // SPL token program address (usually the standard TokenkegQfeZyi...)
  }
};

type WalletBalancesSolana = {
  tokens: WalletTokenSolana[];
}

convertTokenToEUR token to EUR quote

This method is used to estimate a token value in EUR to proceed with the offramping. convertTokenToEUR method can also be used in some scenarios/apps where token to be sent is preset and not selectable.

This method also returns transferData — a hexadecimal string which can contain token-specific transfer, unwrap or swap data that is passed in an offramp transaction.

EVM Networks

import { Network } from '@holyheld/sdk';

(async () => {
  const data = await holyheldSDK.evm.offRamp.convertTokenToEUR({
    walletAddress: '0x...', // wallet address
    tokenAddress: '0x...', // token address
    tokenDecimals: 6, // token decimals
    amount: '9.99', // token amount
    network: Network.ethereum, // token network
  });
})();

Types:

import type { TransferDataEVM } from '@holyheld/sdk';

type ConvertTopUpDataEVM = {
  tokenAmount: string; // amount of token that was passed to query, for example: '1.99'
  EURAmount: string; // EUR valuation of the token amount provided, for example: '314.25'
  transferData?: TransferDataEVM; // data to be passed in sending transaction for this specific token (and amount)
}

Solana Network

import { SolanaNetwork } from '@holyheld/sdk';

(async () => {
  const data = await holyheldSDK.solana.offRamp.convertTokenToEUR({
    walletAddress: '...', // wallet address
    tokenAddress: '...', // token address
    tokenDecimals: 6, // token decimals
    amount: '9.99', // token amount
    network: SolanaNetwork.Mainnet, // token network
  });
})();

Types:

import type { TransferDataSolana } from '@holyheld/sdk';

type ConvertTopUpDataSolana = {
  tokenAmount: string; // amount of token that was passed to query, for example: '1.99'
  EURAmount: string; // EUR valuation of the token amount provided, for example: '314.25'
  transferData?: TransferDataSolana; // data to be passed in sending transaction for this specific token (and amount)
}

convertEURToToken EUR to token quote

convertEURToToken method returns a calculated token amount to match provided (expected) EUR amount.

This method also returns transferData, that is hexadecimal string which could contain token-specific transfer, unwrap or swap data that is passed in sending to tag transaction.

EVM Networks

import { Network } from '@holyheld/sdk';

(async () => {
  const data = await holyheldSDK.evm.offRamp.convertEURToToken({
    walletAddress: '0x...', // wallet address
    tokenAddress: '0x...', // token address
    tokenDecimals: 6, // token decimals
    amount: '9.99', // token amount
    network: Network.ethereum, // token network
  });
})();

Types:

import type { TransferDataEVM } from '@holyheld/sdk';

type ConvertTopUpDataEVM = {
  EURAmount: string; // amount (in EUR) that was passed to query, for example: '30.00'
  tokenAmount: string; // token amount to match expected valuation, for example: '4.18'
  transferData?: TransferDataEVM; // data to be passed in sending transaction for this specific token (and amount)
}

Solana Network

import { Network } from '@holyheld/sdk';

(async () => {
  const data = await holyheldSDK.solana.offRamp.convertEURToToken({
    walletAddress: '0x...', // wallet address
    tokenAddress: '0x...', // token address
    tokenDecimals: 6, // token decimals
    amount: '9.99', // token amount
    network: SolanaNetwork.Mainnet, // token network
  });
})();

Types:

import type { TransferDataSolana } from '@holyheld/sdk';

type ConvertTopUpDataSolana = {
  tokenAmount: string; // amount of token that was passed to query, for example: '1.99'
  EURAmount: string; // EUR valuation of the token amount provided, for example: '314.25'
  transferData?: TransferDataSolana; // data to be passed in sending transaction for this specific token (and amount)
}

Offramp

This is the 'main' method to call that executes offramping to the user card. Parameter values should be retrieved using methods described above, such as transferData matching token and token amount provided.

EVM Networks

🚨 Please note! Some wallets like Metamask are single-network handled. It means that while Holyheld can return/accept transaction on any supported network, user must switch to the correct network in the wallet for the transaction to be processed.

import { Network } from '@holyheld/sdk';
import * as chains from 'viem/chains';
import { createPublicClient, createWalletClient, custom, http } from 'viem';

const provider; // current provider in your app (see examples below)
const transferData; // transfer data from conversion methods or undefined
const eventConfig; // callbacks
const chainId; // token chain id

const chain = Object.values(chains).find((item) => item.id === chainId); // get chain entity from viem

const publicClient = createPublicClient({ // create viem public client - https://viem.sh/docs/clients/public.html
  chain,
  transport: http(),
});

const walletClient = createWalletClient({ // wrap your provider in viem wallet client - https://viem.sh/docs/clients/wallet.html
  chain,
  transport: custom(provider), // current provider in your app (see examples below)
  account: '0x...', // wallet address
});

(async () => {
  await holyheldSDK.evm.offRamp.topup({
    publicClient: publicClient,
    walletClient: walletClient,
    walletAddress: '0x...',
    tokenAddress: '0x...',
    tokenNetwork: Network.ethereum,
    tokenAmount: '5.25',
    transferData: transferData, // if was provided by 'convertTokenToEUR' and/or 'convertEURToToken'
    holytag: 'SDKTEST', // funds recipient tag
    supportsSignTypedDataV4: true, // true if connected wallet supports eth_signTypedData_v4 (default: false)
    supportsRawTransactionsSigning: true, // true if connected wallet supports raw transactions signing via "eth_sign" or "eth_signTransaction"
    eventConfig: eventConfig, // callbacks (see below)
  });
})();

Types:

enum TopUpStep {
  Confirming = 'confirming', // user is confirming action
  Approving = 'approving', // a request was sent to wallet for approval or permit signature
  Sending = 'sending', // a request was sent to wallet for executing sending funds to a tag
}

interface TopUpCallbackConfig {
  onHashGenerate?: (hash: string) => void;
  onStepChange?: (step: TopUpStep) => void;
}

Solana

import { Connection, clusterApiUrl } from '@solana/web3.js';
import { PhantomWalletAdapter } from '@solana/wallet-adapter-phantom';
import { SolanaNetwork, createSolanaWalletClientFromAdapter } from '@holyheld/sdk';


const transferData; // transfer data from conversion methods or undefined
const eventConfig; // callbacks

const networkInfo = holyheldSDK.solana.getNetwork(SolanaNetwork.Mainnet);

const connection = new Connection(networkInfo.httpRpcURL, {
  commitment: 'confirmed',
  wsEndpoint: networkInfo.wsRpcURL ?? clusterApiUrl(networkInfo.cluster, true)
});

const walletAdapter = new PhantomWalletAdapter();

const walletClient = createSolanaWalletClientFromAdapter(walletAdapter, connection);

(async () => {
  await holyheldSDK.solana.offRamp.topup({
    connection: connection,
    walletClient: walletClient,
    walletAddress: '...',
    tokenAddress: '...',
    tokenNetwork: SolanaNetwork.Mainnet,
    tokenAmount: '5.25',
    transferData: transferData, // if was provided by 'convertTokenToEUR' and/or 'convertEURToToken'
    holytag: 'SDKTEST', // funds recipient tag
    eventConfig: {}, // callbacks (see below)
  });
})();

Types:

enum TopUpStep {
  Confirming = 'confirming', // user is confirming action
  Sending = 'sending', // a request was sent to wallet for executing sending funds to a tag
}

interface TopUpCallbackConfig {
  onHashGenerate?: (hash: string) => void;
  onStepChange?: (step: TopUpStep) => void;
}