Alluvial
  • πŸš€Who is Alluvial?
  • πŸ—ΊοΈGuides
    • Mint and Redeem support
    • Secondary Interaction support
    • Reporting
    • Supplemental Guides
      • Architecture
      • Authentication
      • Redemptions
      • Staking
  • πŸ“–APIs
    • Authentication API
    • Ethereum Data API
    • Allowlisting API
    • Redemption API
    • Reporting API
    • Discounting API
    • Public APIs
  • 🌎Third Party Integration Guides
    • Fireblocks
  • Changelog
Powered by GitBook
On this page
  • Architecture
  • Implementation
  • Creating Depositor and Allowlisting
  • Stake ETH
  • Redeem LsETH

Was this helpful?

Export as PDF
  1. Third Party Integration Guides

Fireblocks

PreviousThird Party Integration GuidesNextChangelog

Last updated 2 months ago

Was this helpful?

Liquid Collective Platforms can use to custody digital assets.

Fireblocks is an easy-to-use platform to create new blockchain-based products and manage day-to-day digital asset operations. Fireblocks provides an MPC-based wallet allowing users to store their digital assets.

This guide will provide Platforms with a step-by-step process for using Fireblocks to interact with the Liquid Collective protocol.

Fireblocks' offerings are third-party products that are not offered by or in partnership or affiliation with Alluvial. Products and services offered by Fireblocks and other third parties are subject to separate terms and conditions. Please visit for more information. Any links provided are for your convenience and informational purposes only. Inclusion of any link does not constitute an endorsement or an approval of any such third-party products by Alluvial or any other Liquid Collective protocol service provider.

Architecture

Below is an example architecture for a Platform that uses Fireblocks as a custodian when the Platform's users stake ETH and/or redeem LsETH.

Implementation

This guide uses the Goerli network and the Alluvial Staging API.

Dependencies

Create a new Javascript file, such as index.js.

Define dependencies at top of file.

index.js
const fs = require('fs');
const path = require('path');
const { FireblocksSDK } = require('fireblocks-sdk');
const { inspect } = require('util');

Vault & wallet

Create or use an existing Fireblocks vault.

index.js
const createVault = async () => {
  const name = 'LsETH blog';

  const vaultAccount = await fireblocks.createVaultAccount(name);

  console.log(inspect(vaultAccount, false, null, true));
};
createVault();

Response:

index.js
{
  "id": "6",
  "name": "LsETH blog",
  "hiddenOnUI": false,
  "assets": [],
  "autoFuel": false
}

Next, put digital assets into the Fireblocks account.

Define the contract address for Goerli:

index.js
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;

Call the supported digital assets in Fireblocks.

index.js

const getAssets = async() => {

    const supportedAssets = await fireblocks.getSupportedAssets();

    supportedAssets.forEach((asset, index, array) => {
        if (asset.contractAddress == CONTRACT_ADDRESS) {
            console.log(JSON.stringify(asset))
)
        }
    })
}
getAssets();

Response:

index.js
{
  "id": "LSETH_ETH_TEST3_4E2A",
  "name": "Liquid Staked ETH",
  "type": "ERC20",
  "contractAddress": "0x3ecCAdA3e11c1Cc3e9B5a53176A67cc3ABDD3E46",
  "nativeAsset": "ETH_TEST3",
  "decimals": 18
}

Add the LsETH to the newly created vault.

Request:

index.js
const addAssetToVault = async () => {
  const vaultWallet = await fireblocks.createVaultAsset(
    6,
    'LSETH_ETH_TEST3_4E2A'
  );

  console.log(inspect(vaultWallet, false, null, true));
};
addAssetToVault();

Response:

index.js
{
  "id": "6",
  "address": "<FIREBLOCKS ADDRESS>",
  "legacyAddress": "",
  "tag": ""
}

Creating Depositor and Allowlisting

Now that you have an address associated with your Fireblocks account, you will create a Depositor object and add the wallet address to the Liquid Collective protocol Allowlist via the Alluvial API.

Stake ETH

Now that you have successfully added your wallets to the Allowlist you can continue with staking.

In order to interact with the Liquid Collective protocol you will need to invoke Smart Contract functions.

In the index.js file add new dependencies

index.js
const {
  FireblocksWeb3Provider,
  ChainId,
} = require('@fireblocks/fireblocks-web3-provider');
const ethers = require('ethers');

The Liquid Collective uses a TUPProxy architecture. Below are the details about the Proxy address and implementation contract.

Ethereum Network
Proxy
Implementation

Goerli

0x3ecCAdA3e11c1Cc3e9B5a53176A67cc3ABDD3E46

0xF32fC26C9604a380c311e7eC0c5E545917e7934f

Mainnet

0x8c1BEd5b9a0928467c9B1341Da1D7BD5e10b6549

0x48D93d8C45Fb25125F13cdd40529BbeaA97A6565

Create a separate file called Contract.json.

In the file, add the ABI for the Liquid Collective protocol:

Contract.json
{
  "abi": [ { INSERT ABI FILE HERE } ],
}

Define the ABI address for Goerli.

index.js
const ABI = require('./Contract.json').abi;

Define the EIP-1193 Provider.

index.js
const eip1193Provider = new FireblocksWeb3Provider({
  privateKey: process.env.FIREBLOCKS_API_PRIVATE_KEY_PATH,
  apiKey: process.env.FIREBLOCKS_API_KEY,
  vaultAccountIds: process.env.FIREBLOCKS_VAULT_ACCOUNT_IDS,
  chainId: ChainId.GOERLI,
  // apiBaseUrl: ApiBaseUrl.Sandbox // If using a sandbox workspace
});

Create a function that calls the deposit function.

You will need to have ETH in your Fireblocks vault to fund the deposit & gas fee.

Request:

index.js
const createDeposit = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const gasPrice = await provider.getGasPrice();
  const deposit_estimation = await LsETHContract.estimateGas.deposit({
    from: FIREBLOCKS_ADDRESS,
    value: ethers.utils.parseUnits('0.00000001', 'ether'),
    gasLimit: ethers.utils.hexlify(1),
    nonce: provider.getTransactionCount(FIREBLOCKS_ADDRESS, 'latest'),
  });

  let tx = await LsETHContract.deposit({
    from: FIREBLOCKS_ADDRESS,
    value: ethers.utils.parseUnits('0.000000001', 'ether'),
    gasPrice: gasPrice,
    gasLimit: deposit_estimation,
    nonce: provider.getTransactionCount(FIREBLOCKS_ADDRESS, 'latest'),
  });
  let receipt = await tx.wait();
  console.log(receipt);
};
createDeposit();

Submitting transactions will involve the Fireblocks TAP policy.

You've successfully staked ETH and should see LsETH returned in your Fireblocks vault.

index.js
const getTx = async () => {
  const transactions = await fireblocks.getTransactions({
    txHash: '<INSERT TX HASH>',
  });

  console.log(JSON.stringify(transactions));
};
getTx();

Response:

index.js
{
  "id": "LSETH_ETH_TEST3_4E2A",
  "total": "0.000000000974731883",
  "balance": "0.000000000974731883",
  "lockedAmount": "0",
  "available": "0.000000000974731883",
  "pending": "0",
  "frozen": "0",
  "staked": "0",
  "blockHeight": "9212224",
  "blockHash": "0xc1d94ab995a5db95ddaa10b85ef47b24a38b9ea7249ac7c64bb46a1288fa33bc"
}

Now you can implement the LsETH redemption flow, providing your users the ability to redeem their LsETH for ETH.

Redeem LsETH

The next step is to allow the allowlisted wallets the ability to redeem their LsETH for ETH, thereby burning their LsETH.

There is a two-step process to receive the redeemed ETH. First, you will create a RedeemRequest that results in a redemption ID being returned. Once the redemption has been satisfied (full or partial) you can make a claimRedeemRequest call.

Create a redemption request

Request:

index.js
const createRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const value = ethers.utils.parseEther('0.000001');

  const redeem_estimation = await LsETHContract.estimateGas.requestRedeem(
    value,
    FIREBLOCKS_ADDRESS,
    { gasLimit: 1 }
  );

  const tx = await LsETHContract.requestRedeem(value, FIREBLOCKS_ADDRESS, {
    gasLimit: redeem_estimation,
  });

  let receipt = await tx.wait();
  console.log(receipt);
};
createRedeemRequest();

A request redeem ID will be generated.

Resolve redeem request

The example below will show invoking the resolveRedeemRequest function.

Request:

index.js
const resolveRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const value = ethers.utils.parseEther('0.000001');

  const arrRequestId = [53];

  const resolveRedeem = await LsETHContract.resolveRedeemRequests(arrRequestId);
  console.log(resolveRedeem.toString());
};
resolveRedeemRequest();

Response:

111

You can proceed to make a claim now that you have both your redemption ID (53) and Withdrawal Event ID (111).

Create a claim request

To make a claim request provide both the redemption request ID and the Withdrawal Event ID in an array.

Submitting transactions will involve the Fireblocks TAP policy.

Request:

index.js
const createClaimRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const arrRequestId = [53];
  const arrWithdrawalId = [111];

  const claim_estimation = await LsETHContract.estimateGas.claimRedeemRequests(
    arrRequestId,
    arrWithdrawalId,
    { gasLimit: 1 }
  );
  const claimRedeemRequests = await LsETHContract.claimRedeemRequests(
    arrRequestId,
    arrWithdrawalId,
    { gasLimit: claim_estimation }
  );
  const receipt = await claimRedeemRequests.wait();
  console.log(claimRedeemRequests);
};
createClaimRedeemRequest();

After the claim is processed you will see the the deposit of ETH into your Fireblocks wallet.

Congratulations! You have now implemented the ETH staking and LsETH redemption flow using a Fireblocks account and SDK.

Appendix:

Below is the full code from the snippets above:

To call a function uncomment the function declaration.

index.js
//General dependencies
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const { inspect } = require('util');

//Fireblocks SDKs
const { FireblocksSDK } = require('fireblocks-sdk');
const {
  FireblocksWeb3Provider,
  ChainId,
} = require('@fireblocks/fireblocks-web3-provider');

const ethers = require('ethers');

const baseUrl = 'https://api.fireblocks.io';
const apiSecret = fs.readFileSync(path.resolve('./fb.key'), 'utf8');
const apiKey = process.env.API_KEY;

const ABI = require('./Contract.json').abi;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS;
const FIREBLOCKS_ADDRESS = process.env.FIREBLOCKS_ADDRESS;

const fireblocks = new FireblocksSDK(apiSecret, apiKey, baseUrl);

const eip1193Provider = new FireblocksWeb3Provider({
  apiBaseUrl: baseUrl,
  privateKey: apiSecret,
  apiKey: apiKey,
  vaultAccountIds: 3,
  chainId: ChainId.GOERLI,
});

const createVault = async () => {
  const name = 'LsETH blog';

  const vaultAccount = await fireblocks.createVaultAccount(name);

  console.log(inspect(vaultAccount, false, null, true));
};

//createVault()

const getAssets = async () => {
  const supportedAssets = await fireblocks.getSupportedAssets();

  supportedAssets.forEach((asset, index, array) => {
    if (asset.contractAddress == CONTRACT_ADDRESS) {
      console.log(JSON.stringify(asset));
    }
  });
};

//getAssets()

const addAssetToVault = async () => {
  const vaultWallet = await fireblocks.createVaultAsset(
    6,
    'LSETH_ETH_TEST3_4E2A'
  );

  console.log(inspect(vaultWallet, false, null, true));
};
//addAssetToVault()

const createDeposit = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const gasPrice = await provider.getGasPrice();
  const deposit_estimation = await LsETHContract.estimateGas.deposit({
    from: FIREBLOCKS_ADDRESS,
    value: ethers.utils.parseUnits('0.00000001', 'ether'),
    gasLimit: ethers.utils.hexlify(1),
    nonce: provider.getTransactionCount(FIREBLOCKS_ADDRESS, 'latest'),
  });

  let tx = await LsETHContract.deposit({
    from: FIREBLOCKS_ADDRESS,
    value: ethers.utils.parseUnits('0.000000001', 'ether'),
    gasPrice: gasPrice,
    gasLimit: deposit_estimation,
    nonce: provider.getTransactionCount(FIREBLOCKS_ADDRESS, 'latest'),
  });
  let receipt = await tx.wait();
  console.log(receipt);
};
//createDeposit()

const getTx = async () => {
  const transactions = await fireblocks.getTransactions({
    txHash:
      '0x124e9ef06e38ab0e3ff78d066f8f2eea54b18210390a229c251a0d578853ae20',
  });

  console.log(JSON.stringify(transactions));
};
//getTx()

const getBalance = async () => {
  const vaultAsset = await fireblocks.getVaultAccountAsset(
    3,
    'LSETH_ETH_TEST3_4E2A'
  );
  console.log(JSON.stringify(vaultAsset));
};
//getBalance()

const createRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const value = ethers.utils.parseEther('0.000001');

  const redeem_estimation = await LsETHContract.estimateGas.requestRedeem(
    value,
    FIREBLOCKS_ADDRESS,
    { gasLimit: 1 }
  );

  const tx = await LsETHContract.requestRedeem(value, FIREBLOCKS_ADDRESS, {
    gasLimit: redeem_estimation,
  });

  let receipt = await tx.wait();
  console.log(receipt);
};
//createRedeemRequest();

const resolveRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const value = ethers.utils.parseEther('0.000001');

  const arrRequestId = [53];

  const resolveRedeem = await LsETHContract.resolveRedeemRequests(arrRequestId);
  console.log(resolveRedeem.toString());
};
//resolveRedeemRequest();

const createClaimRedeemRequest = async () => {
  const provider = new ethers.providers.Web3Provider(eip1193Provider);
  const LsETHContract = new ethers.Contract(
    CONTRACT_ADDRESS,
    ABI,
    provider.getSigner()
  );

  const arrRequestId = [53];
  const arrWithdrawalId = [111];

  const claim_estimation = await LsETHContract.estimateGas.claimRedeemRequests(
    arrRequestId,
    arrWithdrawalId,
    { gasLimit: 1 }
  );
  const claimRedeemRequests = await LsETHContract.claimRedeemRequests(
    arrRequestId,
    arrWithdrawalId,
    { gasLimit: claim_estimation }
  );
  const receipt = await claimRedeemRequests.wait();
  console.log(claimRedeemRequests);
};
//createClaimRedeemRequest();

This implementation will take advantage of the Fireblocks SDK, specifically the Javascript SDK. Please follow the to install the appropriate dependencies.

Admin permissions are needed to add ERC-20 digital assets to a Fireblocks account. Please review this information on .

The steps for onboarding and adding to the Allowlist can be found in the . Come back to this guide once your wallet(s) have been successfully added to the Allowlist.

This guide will use the Fireblocks Ethers.js provider, created by the Fireblocks team. To install read the .

Goerli can be found

Mainnet can be found

For more information check out .

The first function you will call is the .

You can retrieve the request redeem ID either via the Alluvial API or by listening the the Redeem Manager contract events. More information can be found .

In order to get the Withdrawal Event ID, invoke the or call via the .

The resolveRedeemRequest function returns the status of the Withdrawal Event ID. More can be found .

To see the status of the claim you can either listen for events or use the Alluvial API. More information on the claiming process can be found .

🌎
Fireblocks Javascript Guide
the Fireblocks website
staking guide
Fireblocks documentation
here
here
Liquid Collective's redemption documentation
requestRedeem
here
resolveRedeemRequest
Alluvial API
here
here
Fireblocks
https://www.fireblocks.com/
Architecture