npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@byzantine/debt-fund-sdk

v2.0.3

Published

Byzantine Vault SDK for creating and managing debt funds vaults

Downloads

883

Readme

Debt Fund SDK

npm version npm downloads GitHub

TypeScript SDK for interacting with the Byzantine Debt Fund ecosystem — a wrapper around the Morpho Vault V2 protocol. Create vaults, configure them, deposit/withdraw, and bundle dozens of admin operations into a single transaction via the on-chain multicall.

📦 npm package · 🐙 GitHub repo · 🐛 Issues

What's new in v2

The SDK has been rewritten around three primitives:

  1. Vault class — every per-vault operation lives here, with method names matching the contract (vault.owner(), vault.totalAssets(), vault.addAdapter(...)).
  2. Actions namespace — pure calldata builders grouped by role (Actions.owner.*, Actions.curator.*, Actions.allocator.*, Actions.user.*).
  3. vault.multicall([...]) — bundle any number of actions into a single atomic transaction. Setting up a brand-new vault now takes 1 tx instead of 12+.

Breaking change vs v1: every client.X(vaultAddress, ...) call became vault.X(...) (where vault = client.vault(addr)), and several methods were renamed to match the contract directly (e.g. getOwnerowner(), addAdapterAfterTimelockaddAdapter).

v2.0.1 adds on-chain live-state reads for each adapter type — utilization, free liquidity and (for Morpho V1 markets and Compound V3) instantaneous supply APY — plus a universal getAdapterId(). See Live state reads.

v2.0.2 fixes the encoding of idData("this/marketParams", ...) (was bytes-wrapped, now inline-tuple) so its keccak256 actually matches the bucket id the Morpho V1 adapter exposes. The signature now takes a MarketParams struct directly. A new idHash(type, ...) helper returns the hashed bucket id in one call.

Supported networks

  • Ethereum Mainnet (chain ID 1)
  • Base Mainnet (chain ID 8453) — please use small amounts only

Vault V2 protocol addresses come from the official Morpho documentation. The ERC4626Merkl and CompoundV3 adapter factories are Byzantine-deployed.

Installation

npm install @byzantine/debt-fund-sdk

Setup

Create a .env:

RPC_URL=https://base-mainnet.infura.io/v3/your_api_key_here
MNEMONIC=your_wallet_mnemonic

Initialize:

import { ByzantineClient } from "@byzantine/debt-fund-sdk";
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = ethers.Wallet.fromPhrase(process.env.MNEMONIC).connect(provider);
const client = new ByzantineClient(provider, wallet);

Quick start — create + configure a vault in 1 multicall

import {
  Actions,
  ByzantineClient,
  idData,
  parseAnnualRate,
  parsePercent,
} from "@byzantine/debt-fund-sdk";
import { ethers, parseUnits } from "ethers";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = ethers.Wallet.fromPhrase(process.env.MNEMONIC).connect(provider);
const client = new ByzantineClient(provider, wallet);
const me = await wallet.getAddress();

// 1. Deploy the vault (separate tx — factory is its own contract).
const cfg = await client.getNetworkConfig();
const create = await client.createVault(
  me,
  cfg.USDCaddress,                          // pre-resolved per the active chain
  ethers.hexlify(ethers.randomBytes(32)),
);
await create.wait();
const vault = create.vault;

// 2. Deploy an adapter (also its own factory contract).
const deploy = await client.deployAdapter(
  "erc4626",
  vault.address,
  "0x616a4E1db48e22028f6bbf20444Cd3b8e3273738", // some ERC4626 vault
);
await deploy.wait();
const adapter = deploy.adapterAddress;

// 3. Bundle all configuration into ONE tx.
await vault.multicall([
  Actions.owner.setName("Byzantine USDC"),
  Actions.owner.setSymbol("byzUSDC"),
  Actions.curator.instantSetIsAllocator(me, true),
  Actions.curator.instantSetPerformanceFeeRecipient(me),
  Actions.curator.instantSetPerformanceFee(parsePercent("5")),    // 5 %
  Actions.curator.instantSetManagementFee(parseAnnualRate("1")),  // 1 %/year
  Actions.curator.instantAddAdapter(adapter),
  Actions.curator.instantIncreaseAbsoluteCap(idData("this", adapter), parseUnits("1000", 6)),
  Actions.curator.instantIncreaseRelativeCap(idData("this", adapter), parsePercent("100")),
  Actions.allocator.setLiquidityAdapterAndData(adapter, "0x"),
  Actions.allocator.setMaxRate(parseAnnualRate("200")),
]);

Core concepts

Vault instance

client.vault(address) returns a Vault — the single object you call for any per-vault operation.

const vault = client.vault("0x...");

// Reads — names match the contract
await vault.totalAssets();
await vault.owner();
await vault.balanceOf(user);
await vault.previewDeposit(parseUnits("100", 6));

// Writes
await vault.deposit(parseUnits("100", 6), user);
await vault.withdraw(parseUnits("50", 6), user, user);

Three-tier timelock pattern (curator functions)

Most curator setters are timelocked. Each comes as a triplet:

| Verb | Method | Description | |---|---|---| | Schedule | submitX(...) | Starts the timelock | | Execute | X(...) (matches contract) | Runs after the delay | | Instant | instantX(...) | submit + execute in one multicall (only if timelock(X) === 0) |

// e.g. addAdapter — three valid flows
await vault.submitAddAdapter(adapter);          // schedule
// ... wait for timelock ...
await vault.addAdapter(adapter);                // execute (= old "addAdapterAfterTimelock")

// or, if timelock is 0:
await vault.instantAddAdapter(adapter);         // both, in one tx

Actions namespace + multicall

Each Actions.role.X(...) returns the encoded calldata for that operation. Pass any number to vault.multicall([...]).

import { Actions } from "@byzantine/debt-fund-sdk";

// Bulk-update caps for 10 markets in one tx
await vault.multicall(
  markets.map(m => Actions.curator.instantIncreaseAbsoluteCap(m.idData, m.cap)),
);

// Atomic config rotation
await vault.multicall([
  Actions.curator.revoke(oldData),
  Actions.curator.submit(Actions.curator.increaseAbsoluteCap(id, newCap)),
]);

The instantX actions return string[] (the [submit, execute] pair). multicall flattens automatically — you can mix single calldatas and instant pairs freely.

Conversion helpers

The SDK exposes lossless format/parse pairs (bigint ↔ human strings) for the three encodings the contract uses:

import {
  formatAmount, parseAmount,           // tokens (decimals)
  formatPercent, parsePercent,         // WAD (1e18 = 100 %)
  formatAnnualRate, parseAnnualRate,   // WAD/sec ↔ annual %
} from "@byzantine/debt-fund-sdk";

parseAmount("1.5", 6);                  // 1_500_000n
formatPercent(5n * 10n ** 16n);         // "5"
parseAnnualRate("5");                   // ≈ 1_585_489_599n  (per second WAD)

All four implementations are bigint-only — parseX(formatX(v)) === v for representable inputs.

API reference

Vault factory & adapter factories

// Create a vault — returns the tx augmented with `vaultAddress` and a ready Vault.
const { vault, vaultAddress } = await client.createVault(owner, asset, salt);

// Deploy an adapter — `cometRewards` only required for compoundV3.
// For `morphoMarketV1` the `underlying` arg is ignored (the morpho address
// is fixed by the factory's constructor — V2 ABI).
await client.deployAdapter(type, parentVault, underlying, cometRewards?);

// Find an existing adapter (any type if `type` omitted).
// For `morphoMarketV1` there is exactly one adapter per parentVault (the
// `underlying` arg is ignored).
await client.findAdapter(parentVault, underlying, { type?, cometRewards? });

// Adapter introspection
await client.isAdapter(type, address);
await client.getAdapterType(address);                // returns the AdapterType
await client.getAdapterFactoryAddress(address);
await client.getAdapterId(address, type);             // bytes32 stored on the adapter
await client.getIdsERC4626(address);
await client.getIdsERC4626Merkl(address);
await client.getIdsCompoundV3(address);
await client.getIdsMarketV1(address, marketParams);
await client.getUnderlyingERC4626(address);
await client.getUnderlyingERC4626Merkl(address);
await client.getUnderlyingCompoundV3(address);
await client.getUnderlyingMarketV1(address);
await client.getMarketIdsLength(address);
await client.getMarketId(address, index);              // returns bytes32

// Live state reads — pull utilization, liquidity, and (where on-chain)
// supply rate from the underlying protocol. Useful for UIs that mirror
// the vault's exposure dashboard. See `### Live state reads` below.
await client.getMarketState(address, id);              // morphoMarketV1
await client.getCometState(address);                   // compoundV3
await client.getVaultStateERC4626(address);            // erc4626
await client.getVaultStateERC4626Merkl(address);       // erc4626Merkl

// Per-adapter admin surface — see `### Adapter admin writes` below.
const adapter = client.adapter(address, type);

// Network
await client.getNetworkConfig();
await client.getChainId();
await client.getVaultFactoryContract();
client.useSigner(newSigner);

Reads (vault.X())

All reads match the contract function names directly.

// State
await vault.asset();
await vault.decimals();
await vault.name();
await vault.symbol();
await vault.totalAssets();
await vault.totalSupply();
await vault.virtualShares();
await vault.maxRate();
await vault.lastUpdate();

// Roles
await vault.owner();
await vault.curator();
await vault.isSentinel(account);
await vault.isAllocator(account);

// ERC20 (shares)
await vault.balanceOf(account);
await vault.allowance(owner, spender);

// Previews
await vault.previewDeposit(assets);
await vault.previewMint(shares);
await vault.previewWithdraw(assets);
await vault.previewRedeem(shares);
await vault.convertToShares(assets);
await vault.convertToAssets(shares);

// Adapters
await vault.adaptersLength();
await vault.adapter(index);                 // adapters[index]
await vault.isAdapter(account);
await vault.adapterRegistry();

// Caps & allocations
await vault.absoluteCap(id);
await vault.relativeCap(id);
await vault.allocation(id);

// Gates
await vault.receiveSharesGate();
await vault.sendSharesGate();
await vault.receiveAssetsGate();
await vault.sendAssetsGate();

// Fees
await vault.performanceFee();
await vault.performanceFeeRecipient();
await vault.managementFee();
await vault.managementFeeRecipient();
await vault.forceDeallocatePenalty(adapter);

// Liquidity adapter
await vault.liquidityAdapter();
await vault.liquidityData();

// Timelock
await vault.timelock(fnName);                // bigint seconds
await vault.executableAt(data);              // unix timestamp
await vault.abdicated(fnName);

// Asset-side helpers
await vault.assetBalance(account);
await vault.assetAllowance(owner);
await vault.idleBalance();                   // asset balance held idle by the vault
vault.idData("this", adapterAddress);        // helper for cap idData
vault.idData("collateralToken", token);
vault.idData("this/marketParams", adapterAddress, marketParams);

The standalone idData(type, ...) helper is also exported from the package. A companion idHash(type, ...) returns the bytes32 the vault stores caps under — i.e. keccak256(idData(...)). Useful for matching a vault id against a known bucket without rebuilding the encoding by hand. Note that idData("this/marketParams", ...) takes the MarketParams struct directly — the SDK encodes it inline, matching the on-chain adapter so that keccak256(idData(...)) lines up byte-for-byte with the bucket id returned by MorphoMarketV1AdapterV2.ids(marketParams).

Owner writes (instant — no timelock)

await vault.setName(newName);
await vault.setSymbol(newSymbol);
await vault.setNameAndSymbol(newName, newSymbol);   // multicall convenience
await vault.setOwner(newOwner);
await vault.setCurator(newCurator);
await vault.setIsSentinel(account, true);

Curator writes — every timelocked setter is exposed as a triplet

For each timelocked operation, the SDK provides three methods, in this order:

  • submitX(...) — schedule the call (starts the timelock)
  • X(...) — execute after the delay (name matches the contract)
  • instantX(...)submit + execute in one multicall (only valid when timelock(X) === 0)
// Adapters
await vault.submitAddAdapter(addr);
await vault.addAdapter(addr);
await vault.instantAddAdapter(addr);

await vault.submitRemoveAdapter(addr);
await vault.removeAdapter(addr);
await vault.instantRemoveAdapter(addr);

// Caps — increases are timelocked
await vault.submitIncreaseAbsoluteCap(idData, cap);
await vault.increaseAbsoluteCap(idData, cap);
await vault.instantIncreaseAbsoluteCap(idData, cap);

await vault.submitIncreaseRelativeCap(idData, cap);
await vault.increaseRelativeCap(idData, cap);
await vault.instantIncreaseRelativeCap(idData, cap);

// Cap decreases are direct (curator OR sentinel, no timelock)
await vault.decreaseAbsoluteCap(idData, cap);
await vault.decreaseRelativeCap(idData, cap);

// Allocator role
await vault.submitSetIsAllocator(addr, true);
await vault.setIsAllocator(addr, true);
await vault.instantSetIsAllocator(addr, true);

// Gates — 4 gates × 3 verbs each
await vault.submitSetReceiveSharesGate(g);
await vault.setReceiveSharesGate(g);
await vault.instantSetReceiveSharesGate(g);

await vault.submitSetSendSharesGate(g);
await vault.setSendSharesGate(g);
await vault.instantSetSendSharesGate(g);

await vault.submitSetReceiveAssetsGate(g);
await vault.setReceiveAssetsGate(g);
await vault.instantSetReceiveAssetsGate(g);

await vault.submitSetSendAssetsGate(g);
await vault.setSendAssetsGate(g);
await vault.instantSetSendAssetsGate(g);

// Adapter registry
await vault.submitSetAdapterRegistry(reg);
await vault.setAdapterRegistry(reg);
await vault.instantSetAdapterRegistry(reg);

// Fees
await vault.submitSetPerformanceFee(f);
await vault.setPerformanceFee(f);
await vault.instantSetPerformanceFee(f);

await vault.submitSetManagementFee(f);
await vault.setManagementFee(f);
await vault.instantSetManagementFee(f);

await vault.submitSetPerformanceFeeRecipient(r);
await vault.setPerformanceFeeRecipient(r);
await vault.instantSetPerformanceFeeRecipient(r);

await vault.submitSetManagementFeeRecipient(r);
await vault.setManagementFeeRecipient(r);
await vault.instantSetManagementFeeRecipient(r);

await vault.submitSetForceDeallocatePenalty(adapter, penalty);
await vault.setForceDeallocatePenalty(adapter, penalty);
await vault.instantSetForceDeallocatePenalty(adapter, penalty);

// Timelock management
await vault.submitIncreaseTimelock(fn, duration);
await vault.increaseTimelock(fn, duration);
await vault.instantIncreaseTimelock(fn, duration);

await vault.submitDecreaseTimelock(fn, duration);
await vault.decreaseTimelock(fn, duration);
await vault.instantDecreaseTimelock(fn, duration);

await vault.submitAbdicate(fn);
await vault.abdicate(fn);
// (no instant — abdication is permanent)

// Generic timelock primitives
await vault.submit(rawCalldata);   // schedule any pre-encoded call
await vault.revoke(rawCalldata);   // cancel a pending submission

Allocator writes

await vault.allocate(adapter, data, assets);
await vault.deallocate(adapter, data, assets);
await vault.setLiquidityAdapterAndData(adapter, data);
await vault.setMaxRate(rate);

Adapter admin writes (client.adapter(addr, type))

These calls target the adapter contract directly, not the parent vault — so they cannot be bundled into vault.multicall(...). Each is a standalone tx.

const adapter = client.adapter(adapterAddress, type);

Common to every adapter type (erc4626, erc4626Merkl, compoundV3, morphoMarketV1):

await adapter.getSkimRecipient();
await adapter.setSkimRecipient(newRecipient);   // V2 morpho: timelocked — submit first
await adapter.skim(token);                      // pull `token` to the skim recipient

compoundV3 and erc4626Merkl — rewards surface:

await adapter.getClaimer();
await adapter.setClaimer(newClaimer);
await adapter.claim(swapData);                  // pulls rewards (COMP / Merkl-distributed)

// compoundV3-only
await adapter.getCometRewards();

// erc4626Merkl-only
await adapter.getMerklDistributor();

morphoMarketV1 — per-adapter timelock + abdicate machinery (parallel to the vault's, on a different contract):

await adapter.getTimelock(selector);            // current delay (seconds) for `selector`
await adapter.getAbdicated(selector);           // permanently disabled?
await adapter.getExecutableAt(data);            // when a submitted `data` becomes executable

await adapter.submit(data);                     // schedule any timelocked call
await adapter.revoke(data);                     // cancel a pending submission
await adapter.abdicate(selector);               // permanent, irreversible
await adapter.increaseTimelock(selector, duration);  // not itself timelocked
await adapter.decreaseTimelock(selector, duration);  // itself timelocked — submit first

Live state reads

Each adapter type exposes a "state" helper that reaches into the underlying protocol to surface the data a dashboard would want — TVL, free liquidity, utilization, and (where the protocol publishes it on-chain) the per-second supply rate. APYs are intentionally derived from on-chain reads only — no historical sampling, no off-chain APIs.

// MorphoMarketV1 — per market `id` (bytes32). Resolves marketParams
// from `morpho.idToMarketParams`, pulls the market struct, and computes
// the supply rate via the IRM's `borrowRateView`.
const m = await client.getMarketState(adapter, id);
// → { marketParams, totalSupplyAssets, totalBorrowAssets, lastUpdate, fee,
//     utilization, liquidity, supplyRatePerSec }

// CompoundV3 — reads the underlying Comet directly.
const c = await client.getCometState(adapter);
// → { cometAddress, totalSupply, totalBorrow, liquidity, utilization,
//     adapterBalance, supplyRatePerSec }

// ERC4626 + ERC4626Merkl — `totalAssets` + `maxWithdraw` on the
// underlying vault. No supply rate (would require historical share-
// price drift, off-chain).
const v = await client.getVaultStateERC4626(adapter);
const v2 = await client.getVaultStateERC4626Merkl(adapter);
// → { underlyingAddress, totalAssets, totalSupply, maxWithdraw }

supplyRatePerSec is a WAD-per-second rate — annualize via formatAnnualRate(rate) to get a percent string, the same way maxRate and managementFee are displayed elsewhere in the SDK.

The same reads are exposed on the AdapterInstance returned by client.adapter(address, type), so you can chain them with the admin surface above without re-typing the address:

const adapter = client.adapter(addr, "morphoMarketV1");
await adapter.getAdapterId();
await adapter.getMarketState(id);

User writes

await vault.deposit(assets, onBehalf);
await vault.mint(shares, onBehalf);
await vault.withdraw(assets, receiver, onBehalf);
await vault.redeem(shares, receiver, onBehalf);
await vault.transfer(to, shares);
await vault.transferFrom(from, to, shares);
await vault.approve(spender, shares);
await vault.permit(owner, spender, shares, deadline, v, r, s);
await vault.forceDeallocate(adapter, data, assets, onBehalf);
await vault.accrueInterest();
await vault.approveAsset(amount);   // approves the vault to spend the underlying

Adapter types

| Adapter type | Use for | Example underlyings | |---|---|---| | erc4626 | Any ERC4626 vault (Morpho V1 vaults, Spark, Aave stata, …) | Morpho Earn, Spark deployments, Aave stata | | erc4626Merkl | ERC4626 vault with automated Merkl rewards claiming | Same as erc4626 if the protocol distributes rewards via Merkl | | compoundV3 | Compound V3 markets (Comet) — requires cometRewards | Compound markets | | morphoMarketV1 | Morpho V1 peer-to-peer lending markets | Morpho Markets |

Network addresses

vaultV2Factory, morphoRegistry, erc4626AdapterFactory (= MorphoVaultV1AdapterFactory) and morphoMarketV1AdapterV2Factory (= MorphoMarketV1AdapterV2Factory) come from the official Morpho documentation. erc4626MerklAdapterFactory and compoundV3AdapterFactory are Byzantine-deployed.

Ethereum Mainnet (chain 1)

| Contract | Address | |---|---| | vaultV2Factory | 0xA1D94F746dEfa1928926b84fB2596c06926C0405 | | morphoRegistry | 0x3696c5eAe4a7Ffd04Ea163564571E9CD8Ed9364e | | erc4626AdapterFactory | 0xD1B8E2dee25c2b89DCD2f98448a7ce87d6F63394 | | morphoMarketV1AdapterV2Factory | 0x32BB1c0D48D8b1B3363e86eeB9A0300BAd61ccc1 | | erc4626MerklAdapterFactory | 0x576136011496367C7FEF780445349060646C7cC1 | | compoundV3AdapterFactory | 0x60a91D7F17046FB1B1C9360E1C5D68b7E94E5959 |

Base Mainnet (chain 8453)

| Contract | Address | |---|---| | vaultV2Factory | 0x4501125508079A99ebBebCE205DeC9593C2b5857 | | morphoRegistry | 0x5C2531Cbd2cf112Cf687da3Cd536708aDd7DB10a | | erc4626AdapterFactory | 0xF42D9c36b34c9c2CF3Bc30eD2a52a90eEB604642 | | morphoMarketV1AdapterV2Factory | 0x9a1B378C43BA535cDB89934230F0D3890c51C0EB | | erc4626MerklAdapterFactory | 0xdF311B93f922867A686abA9b233Fd7C65d66f83d | | compoundV3AdapterFactory | 0xA4dF9668EE53A896BdF40A7AeAC1364129F3c168 |

Examples

A full set of runnable examples lives under example/:

| File | What it shows | |---|---| | multicall-showcase.ts | Full vault setup (12+ ops) in one transaction | | create-vault-simple.ts | Minimal vault creation | | create-vault.ts | End-to-end create + configure with role swaps | | users-deposit.ts | Deposit / mint / withdraw / redeem — also a good demo of fullReading with live per-id state (utilization, supply APY, liquidity) | | owners-settings.ts | Owner-side admin (name, symbol, sentinels, …) | | curators-settings.ts | Curator-side config (allocators, fees, adapters, caps) | | allocators-settings.ts | Allocator ops (allocate, deallocate, force-deallocate) | | morpho-adapters.ts | Adapter deployment + introspection | | set-cap-adapter.ts | Set absolute + relative caps in one tx |

Run any example with:

npx tsx example/<filename>.ts

Testing

The SDK ships with three test tiers, all driven by vitest:

npm test                         # unit tests only — no RPC, ~1s
npm run test:integration:read    # read-only RPC checks — needs RPC_URL
npm run test:integration:write   # full e2e — needs RPC_URL + MNEMONIC + anvil
npm run test:all                 # everything
npm run test:watch               # vitest in watch mode

test:integration:write requires Foundry's anvil on PATH — each write test forks a local Anvil from RPC_URL and runs in isolation, so you don't need any real on-chain funds. See test/README.md for the full test infrastructure.

integration-read uses TEST_VAULT_ADDRESS if set to inspect a specific vault; otherwise vault-state tests are skipped. A live Vault V2 you can point it at:

TEST_VAULT_ADDRESS=0x30cacd22f178c9e57b0b010e1f9432881aa530c4   # Ethereum Mainnet — READ-ONLY

⚠️ The address above is read-only. Write-side integration tests always deploy a fresh vault on a per-test Anvil fork and operate on that vault — they never touch TEST_VAULT_ADDRESS.

Set DEBUG=1 to make every write tx in the integration suites print its hash, block, gas used and total cost in native:

DEBUG=1 npm run test:integration:write
#   ⛽ deposit: tx 0x...
#      block 12345 | gas 184302 @ 0.012 gwei | cost 0.00000022 native

License

ISC