Deposit Addresses: Creating via API

Continuing from the Deposit Addresses: Introduction, this guide walks you through creating Deposit Addresses programmatically as part of a Levain Wallet.

What are Deposit Addresses?

In Levain, Deposit Addresses act as forwarders, and are instrumental in helping you create a digital assets management platform for your own use cases.

They enable you to issue blockchain addresses to your users, where your users can transfer their digital assets to these Deposit Addresses. These deposits can then be forwarded to a main omnibus wallet, in which the Deposit Addresses are created from.

Here are some common use cases for Deposit Addresses:

  • Crypto platform and exchanges. To receive crypto deposits from your users across multiple chains, you will need a deposit address system to allocate deposit addresses, receive digital assets and forward them to a main wallet.
  • Crypto payments processor. To help your merchants accept crypto deposits at point-of-sale systems with unique deposit addresses for every transaction.
  • Remittance and cross-border payments. To receive digital assets from your users and forward them to a main wallet for liquidity management and settlement, and integration with traditional financial systems to enable cross-border payments.

How Deposit Addresses work

Every wallet created in Levain has the following structure:

  • The first blockchain address will always be for your Main Wallet.
  • The subsequent blockchain addresses generated for a wallet will always be Deposit Addresses, that act as forwarders to the Main Wallet.

Deposit addresses has its own nuances and behaviors depending on the blockchain it is created on. Refer to Deposit Addresses: Introduction for more details.

For EVM-based blockchains, deposit addresses are contract-based and self-custodial, meaning we will never be able to access your users' deposits. We use minimal proxy contracts along with the CREATE2 opcode, allowing you to know the blockchain addresses ahead of time, even before the forwarder contract is deployed to that address.

To optimize costs, Levain will deploy the actual forwarder contract to the pre-determined Deposit Address only when your users initiate their first deposits.

Creating Deposit Addresses

You can easily generate a deposit address from a single wallet using the following code snippet.

SDKTypeScript
import { LevainGraphClient } from '@levain/wallet-sdk';

const client = new LevainGraphClient({
  // Get your personal access token from https://app.levain.tech/account/tokens
  accessToken: '<Your access token>',
});

// The address can be used to receive deposits, which can be forwarded to the
// main wallet later whenever you deem appropriate.
const { walletDepositAddressId, address } = await client.createWalletDepositAddress({
  walletId: '<wallet id>',
  label: 'My deposit address',

  // Optional hexadecimal salt to generate deterministic addresses
  salt: '0x...',
});

The salt parameter is optional, and is used to generate deterministic addresses. Note that the parent wallet's address also influences the generated address. On EVM chains, a deposit address is generated roughly as follows:

parent_address  = create2(<invariants>, salt)
deposit_address = create2(<invariants>, keccak256(parent_address, salt))

Issuing the Deposit Address to users in your own services

With the deposit addresses generated, you can be sure that the Deposit Addresses will always remain the same in a specific wallet, in a particular order. For example, a wallet's deposit address at index 1 will always be 0x123...abc once generated, and will not change.

You can call our API within your own service, for you to permanently tag the Deposit Address to a specific user within your own database.

Forwarding digital assets from Deposit Addresses

Digital assets sitting on your users' Deposit Addresses are meant to be forwarded to a main omnibus wallet.

This is meant for you to consolidate customer funds in a single wallet for streamlined operations and liquidity management.

We recommend forwarding funds periodically from users' Deposit Addresses to their associated parent wallets when gas prices are low, or when the Deposit Address has accumulated assets above your threshold.

Maintaining your Gas Tank

The Gas Tank is meant to be funded by you, in the blockchain's native tokens, for a smoother experience by consolidating the funds for any network fees.

Currently, in Levain, each blockchain has its own Gas Tank, which needs to be funded for the creation and use of deposit addresses. While generating deposit addresses are free, there is a cost when:

  • Your user makes the first deposit to a Deposit Address issued to them, as the actual forwarder contract will then be deployed.
  • Digital assets are subsequently forwarded from the Deposit Address to the Main Wallet.

We suggest that you monitor your Gas Tank balance on a daily basis as you start. Alternatively, reach out to the Levain Product Team know if you require assistance on best practices to manage your Gas Tank.

DepositAddresses are blockchain addresses that are generated for your users to deposit digital assets into your wallet. Within Levain, every main wallet created will always have the ability to generate deposit addresses. Visually, they are represented as a list of addresses within a wallet.

graph TD ; U1[User 1] -->|Transfer| A1[Deposit Address 1\n0xAbc...123\nLevain] U2[User 2] -->|Transfer| A2[Deposit Address 2\n0xBcd...a1D\nLevain] U3[User 3] -->|Transfer| A3[Deposit Address 3\n0xEfa...01C\nLevain] Un[User n] -->|Transfer| An[Deposit Address n\n0xBcd...2ad\nLevain] A1 --->|Consolidate| B[Customer Funds Wallet\n0xC1a...882\nLevain] A2 --->|Consolidate| B A3 --->|Consolidate| B An --->|Consolidate| B B -->|Overflow| C[Cold Wallet\n0xD2b...7ab\nLevain] B -->|Withdraw| D[Withdrawal Addresses] subgraph "Levain Omnibus Structure" A1 A2 A3 An B C end

Blockchain-specific Examples

The following examples show how to create deposit addresses for different blockchains using the Levain SDK.

Ethereum

SDKTypeScript
// 1. Create deposit address
const { walletDepositAddressId, address } = await client.createWalletDepositAddress({
  walletId: '22cf4347-405a-47df-8267-26bfecac7b11', // an EVM wallet
  label: 'My deposit address',
});

// 2. Deploy and activate deposit address
//    These additional steps are required for EVM chains, as the deposit address is a smart contract that must be deployed
//    If this step is not required for other chains, it will be a no-op. Hence you can safely call this for all chains.
await client.deployWalletDepositAddress({ walletDepositAddressId });
await client.activateWalletDepositAddress({ walletDepositAddressId });

// 3. Flush deposit address when appropriate
const { requestId } = await client.batchFlushWalletDepositAddresses({
  walletId,
  walletDepositAddressIds: [walletDepositAddressId],
  tokenCaip19Uris: [
    'caip19:eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // ethereum mainnet wETH
    'caip19:eip155:1/erc20:0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // ethereum mainnet wBTC
    // ...
  ],
});

Bitcoin

SDKTypeScript
// 1. Create deposit address
const { walletDepositAddressId, address } = await client.createWalletDepositAddress({
  walletId, // a Bitcoin wallet
  label: 'My deposit address',
});

// 2. Flush deposit address when appropriate
const { requestId } = await client.batchFlushWalletDepositAddresses({
  walletId,
  walletDepositAddressIds: [walletDepositAddressId],
  tokenCaip19Uris: [
    // 'caip19:bip122:00069578d7a76f82b2c7117c1334c7ef/slip:0', // bitcoin mainnet UTXO
    'caip19:bip122:000000000933ea01ad0ee984209779ba/slip:1', // bitcoin testnet UTXO
  ],
});

// 3. Execute transaction
//    This step is necessary for UTXO chains
const executedTransaction = await client.executeTransaction({
  walletId,
  requestId,
  walletPassword: '...',
});

Tron

Batch-flushing of Tron deposit addresses is not supported at the moment. We're working on it!

SDKTypeScript
// 1. Create deposit address
const { walletDepositAddressId } = await client.createWalletDepositAddress({
  walletId: '22cf4347-405a-47df-8267-26bfecac7b11', // a Tron wallet
  label: 'My deposit address',
});

// 2. Activate deposit address
await client.deployWalletDepositAddress({ walletDepositAddressId });
await client.activateWalletDepositAddress({ walletDepositAddressId });

// 3. Flush deposit address when appropriate
const { transactionHash } = await client.flushWalletDepositAddress({
  walletDepositAddressId: '...',
  erc20Address: tokenAddress,
});