Skip to main content
Every SDK operation that touches a specific user requires an accountId. You get that by calling client.safe.account() first. This is the entry point for your integration.
Call client.safe.account(userEoa) before anything else. Deposit quotes, balance reads, position reads, and withdrawals all require the accountId it returns. Nothing works without it.
The Blend SDK and API must be called from your backend server. Never expose your API key in frontend code. Embedding the SDK in a browser or mobile client leaks your credentials and compromises your integration.

Bootstrap the account

client.safe.account(address)

This is the first call you make for every user session. It fetches or creates the Blend account for a user EOA and returns everything you need to drive the rest of the SDK.
const account = await client.safe.account(userEoa);
// account.accountId   → pass this to every subsequent call
// account.safeAddress → the user's deterministic Safe address
// account.chainsDeployed → chains where the Safe is already live
Returns SafeAccountResponse:
FieldTypeDescription
accountIdstringBlend account UUID. Required by balance, positions, returns, and deposit APIs
safeAddressHexDeterministic Safe address
chainsDeployednumber[]Chain IDs where the Safe is already deployed

How accountId flows through the SDK

Once you have the accountId, it unlocks every other call:
// 1. Bootstrap
const account = await client.safe.account(userEoa);

// 2. Read
const balance = await client.balance.get(account.accountId);
const positions = await client.positions.get(account.accountId);
const returns = await client.returns.get(account.accountId);

// 3. Deposit
const quote = await client.deposit.getQuote({
  accountId: account.accountId,
  chainId: 8453,
  inputAssetAddress: usdcAddress,
  eoa: userEoa,
  amount: "1000000",
});

// 4. Withdraw
const calldata = await client.withdraw.getCalldata({
  accountId: account.accountId,
  chainId: 8453,
  amount: "1000000",
});
Store the accountId against your internal user record. You only need to call client.safe.account() once per user to get it, then reuse it for all future requests.

Chain-specific Safe operations

client.safe.resolve(accountId, chainId)

Checks Safe deployment state for a specific chain. Returns a discriminated union keyed on status:
StatusMeaning
"validated"Safe is deployed and live. Includes accountId, userAddress, safeAddress, chainId
"not-deployed"Safe address is known but not yet deployed on this chain
"invalid"Safe address does not match expected derivation
"disconnected"Safe exists but the role-modifier module is not enabled
const resolved = await client.safe.resolve(account.accountId, 8453);

if (resolved.status === "validated") {
  // resolved.accountId, resolved.safeAddress, resolved.chainId
}

client.safe.request(accountId, chainId)

Requests Safe deployment on a specific chain.
await client.safe.request(account.accountId, 8453);

Account-scoped reads

All reads in this section are routed through the Blend backend API. The client does not need an RPC connection, a viem provider, or a connected wallet to fetch these values.

client.balance.get(accountId)

const balance = await client.balance.get(account.accountId);

client.balance.getHistory(accountId, params?)

const history = await client.balance.getHistory(account.accountId, {
  startDate: "2026-03-01",
  endDate: "2026-03-09",
});

client.positions.get(accountId)

const positions = await client.positions.get(account.accountId);

client.returns.get(accountId)

const returns = await client.returns.get(account.accountId);

client.yield.get()

const yieldInfo = await client.yield.get();

Next steps

Last modified on March 20, 2026