All pages
Powered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

Supplemental Guides

Architecture

This guide is intended for Platforms to learn about how they can integrate the Liquid Collective protocol with the support of Alluvial's APIs.

To help illustrate how a Platform utilizes the Alluvial APIs to support its Liquid Collective protocol integration, this guide introduces a few fictional characters:

  • Acme Custodian Corp: Financial institution that is a Platform for the Liquid Collective protocol and will offer its customers the ability to participate in the Liquid Collective protocol by staking ETH.

  • Alice: user of Acme Custodian Corp

  • Bob: user of Acme Custodian Corp

Custodial vs. Non-Custodial wallets

A custodial wallet's private key(s) are held by either a 3rd party or by the financial institution that is servicing the wallet account on behalf of the customer. In this example, Alice and Bob have a custodial account with Acme Corp and Acme Corp owns / has access to the private key associated with Alice's and Bob's wallet addresses.

Below is an example of the flow of funds, showing how the staking action by Alice and Bob will flow through Acme's architecture and the Alluvial APIs.

Architectural components in the Acme Infrastructure box are for illustrative purposes.

Non-custodial (or self-managed) wallets are those in which the customer holds the private keys. In this example, Alice is the only one with access to the private key for her wallet.

The largest differences between the two flow of funds diagrams are the wallet ownership and actions (i.e. signing transactions).

Architectural components in the Acme Infrastructure box are for illustrative purposes.

Segregated vs. Omnibus Accounts

Segregated account(s)

A segregated account structure is one where each customer at the financial institution (ex. Acme Corp) has their own unique, separate account. In this example, both Alice and Bob have their own unique Ethereum address associated with their individual accounts at Acme Corp.

Based on the example segregated account structure, you can see how Acme Corp will map its internal accounts to the technical resources (Organization, Depositor, Wallet) created when interacting with the Alluvial API.

Omnibus account(s)

An omnibus account is an account structure in which all users' accounts are grouped into a single wallet address.

Below is an image of how to map Acme's architecture to resources (Organization, Depositor, and Wallet) created via Alluvial's API.

Alluvial isn't aware of, and at no time has control over, the underlying account structure Acme implements. Acme will create a Depositor object that represents all of the internal customers.

Review the Depositor states as applying these states will impact all underlying customers.

(deny, pause, reactivate, and remove)
Custodial flow of funds
Non custodial flow of funds
Segregated accounts
Segregated accounts mapping
Omnibus accounts
Omnibus accounts mapping

Redemptions

Goals

This guide is a technical guide with examples of how to implement Liquid Collective’s redemption workflow into a Platform's workflow. For more information on Liquid Collective’s ETH staking withdrawal architecture, check out Liquid Collective’s LsETH redemption documentation

Pre-read

Review the for the Alluvial API.

Implementation

The guide will using the Hoodi network for smart contract calls.

For this example, the implementation will use both smart contract calls and offchain calls via the Redemption API.

The Redemption API is an offchain API that exposes:

  • The list of redeem requests for an owner (wallet) including their redemption IDs

  • The status information about redeem requests (including id, redeemed amount, satisfaction status, claim status, etc.)

  • The heights of the withdrawal stack (ETH supplied) and the redeem queue (LsETH queued to redeem). Learn more on different queues & stacks.

  • Time estimates for redemptions (projected and fulfilled)

For Contract Addresses: view this . Make sure you use the Contract Address when sending txs.

For ABIs: This guide uses Hoodi testnet and interacts with two contracts: and . RedeemManger contract is only needed if listening to RedeemManager contract events.

Create Redemption Request

The LsETH contract exposes a function called .

As an Platform, you allow users to submit (or to request that you submit on their behalf) the amount of LsETH they wish to redeem.

Note: In some cases, the recipient may be different from the message sender. For example: if using an omnibus model, you may have LsETH in a wallet separate from the user’s ETH wallet. In this case, the LsETH wallet will make the requestRedeem, however, the recipient of ETH will be the ETH wallet.

After successfully calling requestRedeem function an ID will be generated. Keep this redeemRequestId as you will use it later on.

Below is a code snippet that will call the requestRedeem function.

Code uses public RPC as its provider to interact with smart contracts and uses Ethers.js

Contract.json is the ABI associated with the LsETH contract

RequestedRedeem event

When creating a requestRedeem, the LsETH contract will emit a RequestedRedeem event. The event contains the redeem request IDs. Additionally, you can use the Redemption API to retrieve redeem request IDs associated with an owner, which is demonstrated later on in this guide.

Below is a code snippet for listening to the RequestedRedeem event via websocket connection.

Request:

Code uses public RPC as its provider to interact with smart contracts and uses Ethers.js

Contract.json is the ABI associated with the LsETH contract

run in terminal

Response:

As you can see in the response below, the redeem request ID is 18. This is the request ID associated with the request earlier in the “Create Redeem Request” section of this guide.

The full lifecycle method visual can be seen .

eth/v0/redeems

You can use the Redemption API to list the redeemRequests for a given recipient wallet.

You can get a list of all redemptions associated with a wallet using the curl request below.

Request

Response:

You can see information related to the status of the redemption. Later on, you will see the status update as the redeemRequest becomes satisfied and can be claimed.

For all status combinations check out this

Timing projections

Now that you have a redeem ID, use the projections endpoint to get an estimate of how long it might take for the redeemRequest to be satisfied and become claimable.

Request:

Response:

The response below displays when the redeemRequest is likely to be redeemable. Once redeemable, you can use resolveRedeemRequests to get the matching withdrawal event ID.

Reported withdrawal event

A withdrawal event is triggered at the time of the protocol’s Oracle report. Oracles report every 24 hours.

Withdrawal event IDs are not generated instantly, even if there is enough supply. Withdrawal event IDs will be generated at the next Oracle report after redeem request is submitted. This is by design to ensure a fair redemption process for all Liquid Collective participants.

Below is an example event.

Request:

This event will emit the newly created withdrawal ID, as you can see below:

Response:

Resolve redemption request

In order to see if a redemption request can be satisfied, use the function call.

Request:

Response:

You receive a redeem ID of 10. 10 is the specific withdrawal event ID that is produced when the Liquid Collective Protocol has enough ETH to satisfy (either partial or full) the redemption. This means that the redeemRequest has been satisfied and the corresponding withdrawal event ID was returned.

This withdrawal event may fully satisfy or partially satisfy the redeem request ID. To ensure redemption requests are fully satisfied prior to claiming, use the /eth/v0/redeems/{idx} endpoint.

A response of…

  • -1 means that the request is not satisfied yet

  • -2 means that the request is out of bounds

An alternative method to getting the Withdrawal event ID is using the /eth/v0/redeems/{id} endpoint.

Request:

Response:

The response below indicates that this redemption can be fully satisfied, meaning there is enough supply in the withdrawal stack to fulfill this redemption. This is noted via the "status_satisfaction": "FULLY_SATISFIED" and also the claimable_amount_lseth is equal to the total_amount_lseth.

For other potential states, check out the at the bottom of the doc.

Claim redemption requests

With both a request redeem ID and withdrawal event ID you can submit a claim for the redemption calling the function.

Before claiming, ensure you have correct Withdrawal event ID. Withdrawal events IDs will change based on redemptions happening on the protocol.

Request:

After making a claim there will be two events triggered, which will be inspected next in this guide.

ClaimedRedeemRequest event

On successfully claiming a redeemRequest, a ClaimedRedeemRequest event is emitted. This event indicates the amount of LsETH claimed, the amount of ETH sent to the recipient, and the remaining LsETH amount that has been satisfied but not yet claimed.

A remaining LsETH amount of 0 means the request has been fully claimed. If the amount is greater than 0, the request has been partially claimed and another claim will be required to fully claim the request.

Request:

Response:

SatisfiedRedeemRequest event

On successfully claiming a redemption request one or more SatisfiedRedeemRequest events are emitted.

Those events provide details about the withdrawal events that have satisfied a redemption request.

Request:

Response:

eth/v0/redeems

An alternative approach is to use the Alluvial API to call the /redeems endpoint, as it will return the status of both satisfaction and claim status.

Request:

Non-finalized blocks are returned in the Alluvial API.

Response:

Below the endpoint returns that this request redeem has been fully satisfied and claimed.

You are now able to fully implement the redemption process for the Liquid Collective protocol!

Redemption Information

The Alluvial API exposes information about both the.

Redemption height

Platforms who want visibility into the current supply (amount of withdrawals) and demand (amount of redeems) can use the /redeems_info endpoint.

Request:

Response:

Below you can see that the redeem queue is greater than the withdrawal stack. This indicates that more supply is needed to fulfill the redeem requests. Additional supply can enter via more deposits and/or exiting a validator.

amounts returned are denominated in LsETH

In addition to exposing the Withdrawal stack and Redeem queue, Alluvial exposes an estimated time when redeems will be fulfilled.

Request:

Response:

The current projections timestamp logic is:

  • If the timestamp is in the past, it means that there is enough supply to fulfill demand and Alluvial returns the timestamp associated with the latest redeem request.

  • If the timestamp is in the future, it means there isn't enough supply to fulfill demand and time is needed to add supply into the Withdrawal stack.

This is an estimated time and actual times may vary depending on frequency of deposits and redemptions on the Liquid Collective protocol.

Appendix

Full Code

Full code for making deposit transaction

Code is meant for testing purposes and should not be used directly for production workloads.

/redeems response payloads

The redemption process is very dynamic. You can see a matrix of all the redeem statuses . Below will show the difference between response payloads when requesting from either:

  • eth/v0/redeems/18

  • eth/v0/redeems?owner=

NOT_CLAIMED & PENDING_SATISFACTION

Why would this state happen?

  • This is the initial state for all redeem requests.

NOT_CLAIMED & PARTIALLY_SATISFIED

Why would this state happen?

  • This happens when the redemption request can be partially satisfied and the owner has not made a against the redeem request.

  • Notice that the claimable_amount_lseth is greater than 0 but less than the total_amount_lseth.

NOT_CLAIMED & FULLY_SATISFIED

Why would this state happen?

  • The amount of supply can fully satisfy the amount of LsETH being redeemed in this request.

  • Notice that total_amount_lseth == claimable_amount_lseth

PARTIALLY_CLAIMED & PENDING_SATISFACTION

Why would this state happen?

  • The owner has claimed part of redeem request before the redeem request was fully satisfied. More supply will need to be added, hence the withdrawal_event_id is == -1, whereas before there would have been a positive integer associated with the withdrawal_event_id (ex. 0, 1, 2, etc...).

  • Note that claimed_amount_lseth is equal to previously claimed amount.

PARTIALLY_CLAIMED & PARTIALLY_SATISFIED

Why would this state happen?

  • The owner has claimed part of redeem request before the redeem request was fully satisfied. More supply was found, however not enough to fully satisfy this redeem request.

  • In the example below you can see that total_amount_lseth > claimed_amount_lseth + claimable_amount_lseth. There is 40000 LsETH still left to fully satisfy this request.

PARTIALLY_CLAIMED & FULLY_SATISFIED

Why would this state happen?

  • The owner has claimed part of redeem request before the redeem request was fully satisfied.

  • In the example below you can see that total_amount_lseth = claimed_amount_lseth + claimable_amount_lseth.

FULLY_CLAIMED & FULLY_SATISFIED

Why would this state happen?

  • This is final state for all redeem requests. This indicates that the redeem request has been fully satisfied.

  • total_amount_lseth == claimed_amount_lseth and claimable_amount_lseth == 0

  • withdrawal_event_id reflects this state with the -3

-3 means that the request has already been claimed

ID.
Authentication Guide
here
page
LsETH
RedeemManager
requestRedeem
here
Redeem Status Matrix
resolveRedeemRequests
appendix
claimRedeemRequests
Withdrawal stack and Redeem queue
here
claimRedeemRequest
index.js
const { ethers } = require('ethers');
const Contract = require('./Contract.json');

(async () => {
  const nodeUrl = 'https://ethereum-hoodi-rpc.publicnode.com';
  const provider = new ethers.providers.JsonRpcProvider(nodeUrl);

  const signer = new ethers.Wallet('<INSERT WALLET SECRET>', provider);

  const LsETHContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    signer
  );

  const walletAddress = '<INSERT WALLET ADDRESS>';

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

  //Get estimate for gas limit
  const redeem_estimation = await LsETHContract.estimateGas.requestRedeem(
    value,
    walletAddress,
    { gasLimit: 1 }
  );

  // Create Redeem Request
  let tx = await LsETHContract.requestRedeem(value, walletAddress, {
    gasLimit: redeem_estimation,
  });
  let receipt = await tx.wait();
  console.log(tx);
})();
websocket.js
const ethers = require('ethers');
const Contract = require('./RedeemManager.json');

async function main() {
  const provider = new ethers.providers.WebSocketProvider(
    'wss://ethereum-hoodi-rpc.publicnode.com',
    17000
  );

  const redeemManagerContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    provider
  );

  redeemManagerContract.on(
    'RequestedRedeem',
    (owner, height, amount, maxRedeemableEth, id) => {
      console.log('RequestedRedeem event');
      console.log(`Owner of redeem ${owner}`);
      console.log(`Height ${height.toString()}`);
      console.log(`Amount of LsETH to redeem ${amount.toString()}`);
      console.log(
        `Maximum amount of ETH to redeem ${maxRedeemableEth.toString()}`
      );
      console.log(`Request Redeem ID ${id}`);
    }
  );
}
main();
node websocket.js
websocket.js
RequestedRedeem event
Owner of redeem <WALLET ADDRESS>
Height 4980971589444881813
Amount of LsETH to redeem 10000000
Maximum amount of ETH to redeem 10212074
Request Redeem ID 18
index.sh
curl -X 'GET' \
  'https://api.staging.alluvial.finance/eth/v0/redeems?owner=<WALLET ADDRESS>' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer e...jk'
[
  {
    "id": 18,
    "withdrawal_event_id": -1,
    "total_amount_lseth": "10000000",
    "claimed_amount_lseth": "0",
    "claimable_amount_lseth": "0",
    "max_redeemable_amount_eth": "10209790",
    "owner": "<WALLET ADDRESS>",
    "height": "4980971589354881813",
    "status_claim": "NOT_CLAIMED",
    "status_satisfaction": "PENDING_SATISFACTION",
    "requested_at": 9017542
  }
]
index.sh
curl 'https://api.staging.alluvial.finance/eth/v0/redeems/18/projection' \
  -H 'Authorization: Bearer e...jk'
{
  "projected_redeemable_at": "2023-05-19T19:16:24Z"
}
websocket.js
redeemManagerContract.on(
  'ReportedWithdrawal',
  (height, amount, ethAmount, id) => {
    console.log('ReportedWithdrawal event');
    console.log(`Height ${height.toString()}`);
    console.log(`Amount of LsETH to redeem ${amount.toString()}`);
    console.log(`ETH amount being withdrawn ${ethAmount.toString()}`);
    console.log(`Withdrawal event ID ${id}`);
  }
);
websocket.js
ReportedWithdrawal event
Height 4980971589444881813
Amount of LsETH to redeem 10000000
ETH amount being withdrawn 10212074
Withdrawal event ID 10
index.js
const { ethers } = require('ethers');
const Contract = require('./Contract.json');

(async () => {
  const LsETHContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    provider
  );

  let arrRequestId = [18];

  let resolveRedeem = await LsETHContract.resolveRedeemRequests(arrRequestId);
  console.log(resolveRedeem.toString());
})();
10
index.sh
curl 'https://api.staging.alluvial.finance/eth/v0/redeems/18' \
  -H 'Authorization: Bearer e...jk'
[
  {
    "id": 18,
    "withdrawal_event_id": 10,
    "total_amount_lseth": "10000000",
    "claimed_amount_lseth": "0",
    "claimable_amount_lseth": "10000000",
    "max_redeemable_amount_eth": "10209790",
    "owner": "<WALLET ADDRESS>",
    "height": "4980971589354881813",
    "status_claim": "NOT_CLAIMED",
    "status_satisfaction": "FULLY_SATISFIED",
    "requested_at": 9017542
  }
]
index.js
const arrRequestId = [18];
const arrWithdrawalId = [10];

//Get estimate for gas limit
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);
websocket.js
redeemManagerContract.on(
  'ClaimedRedeemRequest',
  (
    redeemRequestId,
    recipient,
    ethAmount,
    lsEthAmount,
    remainingLsEthAmount
  ) => {
    console.log('ClaimedRedeemRequest event');
    console.log(`Redeem Request ID ${redeemRequestId}`);
    console.log(`Recipient of redeem request ${recipient}`);
    console.log(`Amount of ETH ${ethAmount.toString()}`);
    console.log(`Amount of LsETH ${lsEthAmount.toString()}`);
    console.log(`Amount of remaining LsETH ${remainingLsEthAmount.toString()}`);
  }
);
websocket.js
ClaimedRedeemRequest event
Redeem Request ID 18
Recipient of redeem request <WALLET ADDRESS>
Amount of ETH 10209790
Amount of LsETH 10000000
Amount of remaining LsETH 0
websocket.js
redeemManagerContract.on(
  'SatisfiedRedeemRequest',
  (
    redeemRequestId,
    withdrawalEventId,
    lsEthAmountSatisfied,
    ethAmountSatisfied,
    lsEthAmountRemaining,
    ethAmountExceeding
  ) => {
    console.log('SatisfiedRedeemRequest event');
    console.log(`Redeem Request ID ${redeemRequestId}`);
    console.log(`Withdrawal ID ${withdrawalEventId}`);
    console.log(`Amount of LsETH satisfied ${lsEthAmountSatisfied.toString()}`);
    console.log(`Amount of ETH satisfied  ${ethAmountSatisfied.toString()}`);
    console.log(
      `Amount of LsETH left to satisfy ${lsEthAmountRemaining.toString()}`
    );
    console.log(
      `Amount of ETH added to buffer  ${ethAmountExceeding.toString()}`
    );
  }
);
websocket.js
SatisfiedRedeemRequest event
Redeem Request ID 18
Withdrawal ID 10
Amount of LsETH satisfied 10000000
Amount of ETH satisfied  10209790
Amount of LsETH left to satisfy 0
Amount of ETH added to buffer  163
index.sh
curl -X 'GET' \
  'https://api.staging.alluvial.finance/eth/v0/redeems?owner=<WALLET ADDRESS>' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer e...jk'
{
  "id": 18,
  "withdrawal_event_id": -3,
  "total_amount_lseth": "10000000",
  "claimed_amount_lseth": "10000000",
  "claimable_amount_lseth": "0",
  "max_redeemable_amount_eth": "10209790",
  "owner": "<WALLET ADDRESS>",
  "height": "4980971589354881813",
  "status_claim": "FULLY_CLAIMED",
  "status_satisfaction": "FULLY_SATISFIED",
  "requested_at": 9017542
}
index.sh
curl -X 'GET' \
  'https://api.staging.alluvial.finance/eth/v0/redeems_info' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer e...jk'
index.sh
{
  "total_amount_withdrawal_stack_lseth": "204462016832064499274",
  "total_amount_redeem_queue_lseth": "237456822758541259805"
}
index.sh
curl -X 'GET' \
  'https://api.staging.alluvial.finance/eth/v0/redeems_info/projection'\
  -H 'accept: application/json' \
  -H 'Authorization: Bearer e...jk'
index.sh
{
  "projected_fulfilled_at": "2023-06-15T11:02:24Z"
}
index.js
const { ethers } = require('ethers');
const Contract = require('../Contract.json');
const walletAddress = '0xbe79ff177a8F6a0D9656cF47D8687f43666a4d1e';

(async () => {
  const nodeUrl = 'https://ethereum-hoodi-rpc.publicnode.com';
  const provider = new ethers.providers.JsonRpcProvider(nodeUrl);

  const gasPrice = await provider.getGasPrice();

  const signer = new ethers.Wallet('<INSERT WALLET PRIVATE KEY>', provider);

  const riverContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    signer
  );

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

  //Get estimate for gas limit
  const redeem_estimation = await riverContract.estimateGas.requestRedeem(
    value,
    walletAddress,
    { gasLimit: 1 }
  );

  // Create Redeem Request
  let tx = await riverContract.requestRedeem(value, walletAddress, {
    gasLimit: redeem_estimation,
  });

  // ***** Start resolve redeem function block *****
  // Uncomment when running claimRedeemRequests()
  //let arrRequestId = [18];

  //let resolveRedeem = await LsETHContract.resolveRedeemRequests(arrRequestId);
  // console.log(resolveRedeem.toString())
  // ***** Stop resolve redeem function block *****

  // ***** Start claiming function block *****
  // Get estimate for gas limit
  // const claim_estimation = await LsETHContract.estimateGas.claimRedeemRequests(arrRequestId, arrWithdrawalId, { gasLimit: 1});

  //let arrRequestId = [18];
  //const arrWithdrawalId = [10]

  //const claimRedeemRequests = await LsETHContract.claimRedeemRequests(arrRequestId, arrWithdrawalId, { gasLimit: claim_estimation});
  //const receipt = await claimRedeemRequests.wait();
  //console.log(claimRedeemRequests);
  // ***** end claiming function block *****
})();
index.sh
{
  "id": 1,
  "withdrawal_event_id": -1,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "0",
  "claimable_amount_lseth": "0",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "NOT_CLAIMED",
  "status_satisfaction": "PENDING_SATISFACTION",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": 1,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "0",
  "claimable_amount_lseth": "900000",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "NOT_CLAIMED",
  "status_satisfaction": "PARTIALLY_SATISFIED",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": 1,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "0",
  "claimable_amount_lseth": "1000000",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "NOT_CLAIMED",
  "status_satisfaction": "FULLY_SATISFIED",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": -1,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "900000",
  "claimable_amount_lseth": "0",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "PARTIALLY_CLAIMED",
  "status_satisfaction": "PENDING_SATISFACTION",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": 2,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "900000",
  "claimable_amount_lseth": "60000",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "PARTIALLY_CLAIMED",
  "status_satisfaction": "PARTIALLY_SATISFIED",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": 2,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "900000",
  "claimable_amount_lseth": "100000",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "PARTIALLY_CLAIMED",
  "status_satisfaction": "FULLY_SATISFIED",
  "requested_at": 9134158
}
index.sh
{
  "id": 1,
  "withdrawal_event_id": -3,
  "total_amount_lseth": "1000000",
  "claimed_amount_lseth": "1000000",
  "claimable_amount_lseth": "0",
  "max_redeemable_amount_eth": "1023507",
  "owner": "<WALLET ADDRESS>",
  "height": "201956822758540259700",
  "status_claim": "FULLY_CLAIMED",
  "status_satisfaction": "FULLY_SATISFIED",
  "requested_at": 9134158
}

Authentication

This guide walks users through how to use authenticate requests using the Alluvial APIs.

Client Credentials Flow

Below is a ladder diagram showing the flow to create an access token.

This flow involves 3 parties:

  • Platform Server: the client looking to access the Alluvial API and which has previously been given a Client ID and Client Secret credentials.

  • Alluvial Authorization Server: responsible for validating credentials and generating JWT Access Token.

  • Alluvial API: the target resource to be accessed.

To obtain a Client ID and Client Secret reach out to your Alluvial representative.

Getting Access Token

To obtain an access token, use the a request below using your client id and secret.

Request:

Make sure you are using the correct audience URL. Staging: Production:

Response:

Refreshing Access Token

Access Token should be reused for every request until it expires, in which case they should go through the Client Credential Flow again to obtain a fresh Access Token.

Sample request

To use the access token, pass it via the HTTP header Authorization: Bearer

If you receive a 2xx response, you are now able to make fully authenticated requests.

If you receive a 4xx response, check if your access token is expired.

You are now ready to make requests! Please check out our guides on and .

https://api.staging.alluvial.finance
https://api.alluvial.finance
staking
redemptions
curl  'https://auth.alluvial.finance/oauth/token' \
--header 'content-type: application/json' \
--data '{
 "audience": "https://api.staging.alluvial.finance",
 "grant_type": "client_credentials",
 "client_id": "<YOUR_CLIENT_ID>",
 "client_secret": "<YOUR_CLIENT_SECRET>"
}'
{
  "access_token": "eyJhbGci…CVm5g",
  "scope": "read:eth-oracle read:eth-contracts read:eth-operators",
  "expires_in": 86400,
  "token_type": "Bearer"
}
curl 'https://api.staging.alluvial.finance/v0/wallets/0x2B7ff5d4C14A9Da8d5C9354c7A52aB40DdC1C01e' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer eyJh...b'

Staking

Goals

This guide shows you how to Stake ETH using the Liquid Collective Protocol and receive LsETH in return.

By the end of this guide you will:

  • Have an understanding of the Liquid Collective protocol

  • Stake ETH

Pre-read

Review the for the Alluvial API.

Dependencies

  • : You will use Ether.js, a popular library for interacting with Smart Contracts to query blockchain information and send transactions.

  • Application Binary Interface (ABI): The ABI for the River smart contract will be used to READ /WRITE to the contract.

  • Hoodi ETH: You will be accessing the Hoodi testnet, ensure you have a wallet with HoodiETH.

After reading both guides, come back to continue the implementation of the staking workflow.

Implementation

Import libraries

In the backend (Node.js) application you will need to import one library.

app.js

Full code implementation can be see in Appendix, at bottom of the guide.

Next, add information about the RPC node Provider being used. This example uses this example you are using , but there are several options for platforms to host an RPC node.

Staking transaction

You can now create the transaction object. In the example below, you are transferring 0.0000001 from address 0xbe79ff177a8F6a0D9656cF47D8687f43666a4d1e to the River address.

Gas Limit uses a built in Ethers.js function. You may need to manually adjust when network has high demand. The code uses the estimateGas function.

Great job! You have successfully staked ETH. You can use the transaction hash returned to view more details.

Congratulations! You've now staked ETH, received LsETH, and retrieved your balance. As a next step, review the guide on how to implement .

Appendix

Full Code

Full code for making deposit transaction

Code is meant for testing purposes and should not be used directly for production workloads.

Authentication Guide
Ethers.js
Chainstack
redemptions
const { ethers } = require('ethers');
const Contract = require('./Contract.json');
(async () => {
  const nodeUrl = 'https://ethereum-hoodi-rpc.publicnode.com';
  const provider = new ethers.providers.JsonRpcProvider(nodeUrl);

  const gasPrice = await provider.getGasPrice();

  const signer = new ethers.Wallet('<INSERT WALLET KEY>', provider);

  const LsETHContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    signer
  );

  const walletAddress = '<INSERT WALLET ADDRESS>';
})();
// Get estimate for gas limit
const deposit_estimation = await LsETHContract.estimateGas.deposit({
  from: walletAddress,
  value: ethers.utils.parseUnits('0.00000001', 'ether'),
  gasLimit: ethers.utils.hexlify(1),
  nonce: provider.getTransactionCount(walletAddress, 'latest'),
});

// Deposit tx
let tx = await LsETHContract.deposit({
  from: walletAddress,
  value: ethers.utils.parseUnits('0.00000001', 'ether'),
  gasPrice: gasPrice,
  gasLimit: deposit_estimation,
  nonce: provider.getTransactionCount(walletAddress, 'latest'),
});
let receipt = await tx.wait();
console.log(receipt);
index.js
const { ethers } = require('ethers');
const Contract = require('./Contract.json');
const walletAddress = '0xbe79ff177a8F6a0D9656cF47D8687f43666a4d1e'(async () => {
  const nodeUrl = 'https://ethereum-hoodi-rpc.publicnode.com';
  const provider = new ethers.providers.JsonRpcProvider(nodeUrl);

  const gasPrice = await provider.getGasPrice();

  const signer = new ethers.Wallet('<INSERT WALLET KEY>', provider);

  const LsETHContract = new ethers.Contract(
    Contract.address,
    Contract.abi,
    signer
  );

  const walletAddress = '<INSERT WALLET ADDRESS>';

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

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

  let tx = await LsETHContract.deposit({
    from: walletAddress,
    value: ethers.utils.parseUnits('0.00000001', 'ether'),
    gasPrice: gasPrice,
    gasLimit: deposit_estimation,
    nonce: provider.getTransactionCount(walletAddress, 'latest'),
  });
  let receipt = await tx.wait();
  console.log(receipt);
})();
Contract.json
{
  "address": "0x1d8b30cC38Dba8aBce1ac29Ea27d9cFd05379A09",
  "abi": [
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_from",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_operator",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_allowance",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "_value",
          "type": "uint256"
        }
      ],
      "name": "AllowanceTooLow",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "BalanceTooLow",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "account",
          "type": "address"
        }
      ],
      "name": "Denied",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "EmptyDeposit",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "ErrorOnDeposit",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InconsistentPublicKeys",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InconsistentSignatures",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidArgument",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidCall",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "currentValidatorsExitedBalance",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "newValidatorsExitedBalance",
          "type": "uint256"
        }
      ],
      "name": "InvalidDecreasingValidatorsExitedBalance",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "currentValidatorsSkimmedBalance",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "newValidatorsSkimmedBalance",
          "type": "uint256"
        }
      ],
      "name": "InvalidDecreasingValidatorsSkimmedBalance",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidEmptyString",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "epoch",
          "type": "uint256"
        }
      ],
      "name": "InvalidEpoch",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidFee",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "version",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "expectedVersion",
          "type": "uint256"
        }
      ],
      "name": "InvalidInitialization",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidPublicKeyCount",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "requested",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "received",
          "type": "uint256"
        }
      ],
      "name": "InvalidPulledClFundsAmount",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidSignatureCount",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "providedValidatorCount",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "depositedValidatorCount",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "lastReportedValidatorCount",
          "type": "uint256"
        }
      ],
      "name": "InvalidValidatorCountReport",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidWithdrawalCredentials",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "InvalidZeroAddress",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "NoAvailableValidatorKeys",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "NotEnoughFunds",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "NullTransfer",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "SliceOutOfBounds",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "SliceOverflow",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "prevTotalEthIncludingExited",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "postTotalEthIncludingExited",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "timeElapsed",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "relativeLowerBound",
          "type": "uint256"
        }
      ],
      "name": "TotalValidatorBalanceDecreaseOutOfBound",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "prevTotalEthIncludingExited",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "postTotalEthIncludingExited",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "timeElapsed",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "annualAprUpperBound",
          "type": "uint256"
        }
      ],
      "name": "TotalValidatorBalanceIncreaseOutOfBound",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "caller",
          "type": "address"
        }
      ],
      "name": "Unauthorized",
      "type": "error"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_from",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_to",
          "type": "address"
        }
      ],
      "name": "UnauthorizedTransfer",
      "type": "error"
    },
    {
      "inputs": [],
      "name": "ZeroMintedShares",
      "type": "error"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "owner",
          "type": "address"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "spender",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "value",
          "type": "uint256"
        }
      ],
      "name": "Approval",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "validatorCount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "validatorTotalBalance",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "bytes32",
          "name": "roundId",
          "type": "bytes32"
        }
      ],
      "name": "ConsensusLayerDataUpdate",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "version",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "bytes",
          "name": "cdata",
          "type": "bytes"
        }
      ],
      "name": "Initialize",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "epoch",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsSkimmedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitingBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint32",
              "name": "validatorsCount",
              "type": "uint32"
            },
            {
              "internalType": "uint32[]",
              "name": "stoppedValidatorCountPerOperator",
              "type": "uint32[]"
            },
            {
              "internalType": "bool",
              "name": "rebalanceDepositToRedeemMode",
              "type": "bool"
            },
            {
              "internalType": "bool",
              "name": "slashingContainmentMode",
              "type": "bool"
            }
          ],
          "indexed": false,
          "internalType": "struct IOracleManagerV1.ConsensusLayerReport",
          "name": "report",
          "type": "tuple"
        },
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "rewards",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "pulledELFees",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "pulledRedeemManagerExceedingEthBuffer",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "pulledCoverageFunds",
              "type": "uint256"
            }
          ],
          "indexed": false,
          "internalType": "struct IOracleManagerV1.ConsensusLayerDataReportingTrace",
          "name": "trace",
          "type": "tuple"
        }
      ],
      "name": "ProcessedConsensusLayerReport",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "pulledSkimmedEthAmount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "pullExitedEthAmount",
          "type": "uint256"
        }
      ],
      "name": "PulledCLFunds",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        }
      ],
      "name": "PulledCoverageFunds",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        }
      ],
      "name": "PulledELFees",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        }
      ],
      "name": "PulledRedeemManagerExceedingEth",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "redeemManagerDemand",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "suppliedRedeemManagerDemand",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "suppliedRedeemManagerDemandInEth",
          "type": "uint256"
        }
      ],
      "name": "ReportedRedeemManager",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "_collector",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "_oldTotalUnderlyingBalance",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "_oldTotalSupply",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "_newTotalUnderlyingBalance",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "_newTotalSupply",
          "type": "uint256"
        }
      ],
      "name": "RewardsEarned",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "admin",
          "type": "address"
        }
      ],
      "name": "SetAdmin",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "allowlist",
          "type": "address"
        }
      ],
      "name": "SetAllowlist",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "oldAmount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "newAmount",
          "type": "uint256"
        }
      ],
      "name": "SetBalanceCommittedToDeposit",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "oldAmount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "newAmount",
          "type": "uint256"
        }
      ],
      "name": "SetBalanceToDeposit",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "oldAmount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "newAmount",
          "type": "uint256"
        }
      ],
      "name": "SetBalanceToRedeem",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "annualAprUpperBound",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "relativeLowerBound",
          "type": "uint256"
        }
      ],
      "name": "SetBounds",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "collector",
          "type": "address"
        }
      ],
      "name": "SetCollector",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "coverageFund",
          "type": "address"
        }
      ],
      "name": "SetCoverageFund",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "depositContract",
          "type": "address"
        }
      ],
      "name": "SetDepositContractAddress",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "oldDepositedValidatorCount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "newDepositedValidatorCount",
          "type": "uint256"
        }
      ],
      "name": "SetDepositedValidatorCount",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "elFeeRecipient",
          "type": "address"
        }
      ],
      "name": "SetELFeeRecipient",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "fee",
          "type": "uint256"
        }
      ],
      "name": "SetGlobalFee",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "minNetAmount",
          "type": "uint256"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "maxRelativeAmount",
          "type": "uint256"
        }
      ],
      "name": "SetMaxDailyCommittableAmounts",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "string",
          "name": "metadataURI",
          "type": "string"
        }
      ],
      "name": "SetMetadataURI",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "operatorRegistry",
          "type": "address"
        }
      ],
      "name": "SetOperatorsRegistry",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "oracleAddress",
          "type": "address"
        }
      ],
      "name": "SetOracle",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "pendingAdmin",
          "type": "address"
        }
      ],
      "name": "SetPendingAdmin",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "address",
          "name": "redeemManager",
          "type": "address"
        }
      ],
      "name": "SetRedeemManager",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint64",
          "name": "epochsPerFrame",
          "type": "uint64"
        },
        {
          "indexed": false,
          "internalType": "uint64",
          "name": "slotsPerEpoch",
          "type": "uint64"
        },
        {
          "indexed": false,
          "internalType": "uint64",
          "name": "secondsPerSlot",
          "type": "uint64"
        },
        {
          "indexed": false,
          "internalType": "uint64",
          "name": "genesisTime",
          "type": "uint64"
        },
        {
          "indexed": false,
          "internalType": "uint64",
          "name": "epochsToAssumedFinality",
          "type": "uint64"
        }
      ],
      "name": "SetSpec",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "totalSupply",
          "type": "uint256"
        }
      ],
      "name": "SetTotalSupply",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "bytes32",
          "name": "withdrawalCredentials",
          "type": "bytes32"
        }
      ],
      "name": "SetWithdrawalCredentials",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "from",
          "type": "address"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "to",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "value",
          "type": "uint256"
        }
      ],
      "name": "Transfer",
      "type": "event"
    },
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": true,
          "internalType": "address",
          "name": "depositor",
          "type": "address"
        },
        {
          "indexed": true,
          "internalType": "address",
          "name": "recipient",
          "type": "address"
        },
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "amount",
          "type": "uint256"
        }
      ],
      "name": "UserDeposit",
      "type": "event"
    },
    {
      "stateMutability": "payable",
      "type": "fallback"
    },
    {
      "inputs": [],
      "name": "DEPOSIT_SIZE",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "PUBLIC_KEY_LENGTH",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "SIGNATURE_LENGTH",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "_DEPOSIT_SIZE",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "acceptAdmin",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_owner",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_spender",
          "type": "address"
        }
      ],
      "name": "allowance",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_spender",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_value",
          "type": "uint256"
        }
      ],
      "name": "approve",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_owner",
          "type": "address"
        }
      ],
      "name": "balanceOf",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_owner",
          "type": "address"
        }
      ],
      "name": "balanceOfUnderlying",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint32[]",
          "name": "_redeemRequestIds",
          "type": "uint32[]"
        },
        {
          "internalType": "uint32[]",
          "name": "_withdrawalEventIds",
          "type": "uint32[]"
        }
      ],
      "name": "claimRedeemRequests",
      "outputs": [
        {
          "internalType": "uint8[]",
          "name": "claimStatuses",
          "type": "uint8[]"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "decimals",
      "outputs": [
        {
          "internalType": "uint8",
          "name": "",
          "type": "uint8"
        }
      ],
      "stateMutability": "pure",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_spender",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_subtractableValue",
          "type": "uint256"
        }
      ],
      "name": "decreaseAllowance",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "deposit",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_recipient",
          "type": "address"
        }
      ],
      "name": "depositAndTransfer",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_maxCount",
          "type": "uint256"
        }
      ],
      "name": "depositToConsensusLayer",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getAdmin",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getAllowlist",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getBalanceToDeposit",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getBalanceToRedeem",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCLSpec",
      "outputs": [
        {
          "components": [
            {
              "internalType": "uint64",
              "name": "epochsPerFrame",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "slotsPerEpoch",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "secondsPerSlot",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "genesisTime",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "epochsToAssumedFinality",
              "type": "uint64"
            }
          ],
          "internalType": "struct CLSpec.CLSpecStruct",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCLValidatorCount",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCLValidatorTotalBalance",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCollector",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCommittedBalance",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCoverageFund",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCurrentEpochId",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getCurrentFrame",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "_startEpochId",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "_startTime",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "_endTime",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getDailyCommittableLimits",
      "outputs": [
        {
          "components": [
            {
              "internalType": "uint128",
              "name": "minDailyNetCommittableAmount",
              "type": "uint128"
            },
            {
              "internalType": "uint128",
              "name": "maxDailyRelativeCommittableAmount",
              "type": "uint128"
            }
          ],
          "internalType": "struct DailyCommittableLimits.DailyCommittableLimitsStruct",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getDepositedValidatorCount",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getELFeeRecipient",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getExpectedEpochId",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_epochId",
          "type": "uint256"
        }
      ],
      "name": "getFrameFirstEpochId",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getGlobalFee",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getLastCompletedEpochId",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getLastConsensusLayerReport",
      "outputs": [
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "epoch",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsSkimmedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitingBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint32",
              "name": "validatorsCount",
              "type": "uint32"
            },
            {
              "internalType": "bool",
              "name": "rebalanceDepositToRedeemMode",
              "type": "bool"
            },
            {
              "internalType": "bool",
              "name": "slashingContainmentMode",
              "type": "bool"
            }
          ],
          "internalType": "struct IOracleManagerV1.StoredConsensusLayerReport",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getMetadataURI",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getOperatorsRegistry",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getOracle",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getPendingAdmin",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getRedeemManager",
      "outputs": [
        {
          "internalType": "address",
          "name": "",
          "type": "address"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getReportBounds",
      "outputs": [
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "annualAprUpperBound",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "relativeLowerBound",
              "type": "uint256"
            }
          ],
          "internalType": "struct ReportBounds.ReportBoundsStruct",
          "name": "",
          "type": "tuple"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getTime",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "getWithdrawalCredentials",
      "outputs": [
        {
          "internalType": "bytes32",
          "name": "",
          "type": "bytes32"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_spender",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_additionalValue",
          "type": "uint256"
        }
      ],
      "name": "increaseAllowance",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_depositContractAddress",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_elFeeRecipientAddress",
          "type": "address"
        },
        {
          "internalType": "bytes32",
          "name": "_withdrawalCredentials",
          "type": "bytes32"
        },
        {
          "internalType": "address",
          "name": "_oracleAddress",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_systemAdministratorAddress",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_allowlistAddress",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_operatorRegistryAddress",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_collectorAddress",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_globalFee",
          "type": "uint256"
        }
      ],
      "name": "initRiverV1",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_redeemManager",
          "type": "address"
        },
        {
          "internalType": "uint64",
          "name": "_epochsPerFrame",
          "type": "uint64"
        },
        {
          "internalType": "uint64",
          "name": "_slotsPerEpoch",
          "type": "uint64"
        },
        {
          "internalType": "uint64",
          "name": "_secondsPerSlot",
          "type": "uint64"
        },
        {
          "internalType": "uint64",
          "name": "_genesisTime",
          "type": "uint64"
        },
        {
          "internalType": "uint64",
          "name": "_epochsToAssumedFinality",
          "type": "uint64"
        },
        {
          "internalType": "uint256",
          "name": "_annualAprUpperBound",
          "type": "uint256"
        },
        {
          "internalType": "uint256",
          "name": "_relativeLowerBound",
          "type": "uint256"
        },
        {
          "internalType": "uint128",
          "name": "_minDailyNetCommittableAmount_",
          "type": "uint128"
        },
        {
          "internalType": "uint128",
          "name": "_maxDailyRelativeCommittableAmount_",
          "type": "uint128"
        }
      ],
      "name": "initRiverV1_1",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "initRiverV1_2",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_epoch",
          "type": "uint256"
        }
      ],
      "name": "isValidEpoch",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "name",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "pure",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newAdmin",
          "type": "address"
        }
      ],
      "name": "proposeAdmin",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_lsETHAmount",
          "type": "uint256"
        },
        {
          "internalType": "address",
          "name": "_recipient",
          "type": "address"
        }
      ],
      "name": "requestRedeem",
      "outputs": [
        {
          "internalType": "uint32",
          "name": "_redeemRequestId",
          "type": "uint32"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint32[]",
          "name": "_redeemRequestIds",
          "type": "uint32[]"
        }
      ],
      "name": "resolveRedeemRequests",
      "outputs": [
        {
          "internalType": "int64[]",
          "name": "withdrawalEventIds",
          "type": "int64[]"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "sendCLFunds",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "sendCoverageFunds",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "sendELFees",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "sendRedeemManagerExceedingFunds",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newAllowlist",
          "type": "address"
        }
      ],
      "name": "setAllowlist",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "components": [
            {
              "internalType": "uint64",
              "name": "epochsPerFrame",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "slotsPerEpoch",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "secondsPerSlot",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "genesisTime",
              "type": "uint64"
            },
            {
              "internalType": "uint64",
              "name": "epochsToAssumedFinality",
              "type": "uint64"
            }
          ],
          "internalType": "struct CLSpec.CLSpecStruct",
          "name": "_newValue",
          "type": "tuple"
        }
      ],
      "name": "setCLSpec",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newCollector",
          "type": "address"
        }
      ],
      "name": "setCollector",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "epoch",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsSkimmedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitedBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "validatorsExitingBalance",
              "type": "uint256"
            },
            {
              "internalType": "uint32",
              "name": "validatorsCount",
              "type": "uint32"
            },
            {
              "internalType": "uint32[]",
              "name": "stoppedValidatorCountPerOperator",
              "type": "uint32[]"
            },
            {
              "internalType": "bool",
              "name": "rebalanceDepositToRedeemMode",
              "type": "bool"
            },
            {
              "internalType": "bool",
              "name": "slashingContainmentMode",
              "type": "bool"
            }
          ],
          "internalType": "struct IOracleManagerV1.ConsensusLayerReport",
          "name": "_report",
          "type": "tuple"
        }
      ],
      "name": "setConsensusLayerData",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newCoverageFund",
          "type": "address"
        }
      ],
      "name": "setCoverageFund",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "components": [
            {
              "internalType": "uint128",
              "name": "minDailyNetCommittableAmount",
              "type": "uint128"
            },
            {
              "internalType": "uint128",
              "name": "maxDailyRelativeCommittableAmount",
              "type": "uint128"
            }
          ],
          "internalType": "struct DailyCommittableLimits.DailyCommittableLimitsStruct",
          "name": "_dcl",
          "type": "tuple"
        }
      ],
      "name": "setDailyCommittableLimits",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_newELFeeRecipient",
          "type": "address"
        }
      ],
      "name": "setELFeeRecipient",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_newFee",
          "type": "uint256"
        }
      ],
      "name": "setGlobalFee",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "string",
          "name": "_metadataURI",
          "type": "string"
        }
      ],
      "name": "setMetadataURI",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_oracleAddress",
          "type": "address"
        }
      ],
      "name": "setOracle",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "components": [
            {
              "internalType": "uint256",
              "name": "annualAprUpperBound",
              "type": "uint256"
            },
            {
              "internalType": "uint256",
              "name": "relativeLowerBound",
              "type": "uint256"
            }
          ],
          "internalType": "struct ReportBounds.ReportBoundsStruct",
          "name": "_newValue",
          "type": "tuple"
        }
      ],
      "name": "setReportBounds",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_underlyingAssetAmount",
          "type": "uint256"
        }
      ],
      "name": "sharesFromUnderlyingBalance",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "symbol",
      "outputs": [
        {
          "internalType": "string",
          "name": "",
          "type": "string"
        }
      ],
      "stateMutability": "pure",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "totalSupply",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [],
      "name": "totalUnderlyingSupply",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_to",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_value",
          "type": "uint256"
        }
      ],
      "name": "transfer",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "_from",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "_to",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "_value",
          "type": "uint256"
        }
      ],
      "name": "transferFrom",
      "outputs": [
        {
          "internalType": "bool",
          "name": "",
          "type": "bool"
        }
      ],
      "stateMutability": "nonpayable",
      "type": "function"
    },
    {
      "inputs": [
        {
          "internalType": "uint256",
          "name": "_shares",
          "type": "uint256"
        }
      ],
      "name": "underlyingBalanceFromShares",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "stateMutability": "payable",
      "type": "receive"
    }
  ]
}