Savings GHO (sGHO)
Savings GHO (sGHO) is a savings product for the GHO stablecoin, built as a native ERC-4626 vault and deployed on Ethereum mainnet. Users deposit GHO into the vault and receive sGHO shares that steadily increase in GHO value as yield accrues from an on-chain rate. Deposits and withdrawals are instant, with no cooldown period, no slashing risk, and no rehypothecation of deposited funds.
Contract: 0xE175…ca1d (Ethereum Mainnet)
sGHO replaces the legacy Merit-based sGHO contract. To earn the current rate, withdraw GHO from the legacy contract and deposit it into the new vault. See Legacy sGHO.
Vault State
Fetch the current state of the sGHO vault, including rates, totals, and an optional user position.
Use the use hook in React, the sgho action in TypeScript, or the sgho query in GraphQL to read vault data and an optional user position.
In React, the hook supports both a loading state and React Suspense.
const { data, loading, error } = useSghoVault({
chainId: chainId(1),
user: evmAddress("0x742d35cc…"), // optional
});
const { data } = useSghoVault({
chainId: chainId(1),
user: evmAddress("0x742d35cc…"),
suspense: true,
});
Then read the vault state.
import { useSghoVault, chainId, evmAddress } from "@aave/react";
import { useAccount } from "wagmi";
// …
const { address } = useAccount();
const { data, loading, error } = useSghoVault({
chainId: chainId(1),
user: address ? evmAddress(address) : undefined,
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
// data.totalAssets - total GHO locked in the vault
// data.targetRate - current fixed APR
// data.supplyCap - maximum GHO that can be deposited
// data.paused - whether deposits and withdrawals are blocked
// data.user?.shares - user's sGHO shares balance
// data.user?.balance - GHO value of the user's shares
// data.user?.maxDeposit - max GHO the user can deposit
// data.user?.maxWithdraw - max GHO the user can withdraw
import { sghoVault } from "@aave/client/actions";
import { chainId, evmAddress } from "@aave/client";
import { client } from "./client";
const result = await sghoVault(client, {
chainId: chainId(1),
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
});
if (result.isErr()) {
console.error(result.error);
} else {
const { totalAssets, targetRate, supplyCap, paused, user } = result.value;
console.log("Target APR:", targetRate);
console.log("User GHO balance:", user?.balance);
}
query {
sghoVault(
request: {
chainId: 1
user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234" # optional
}
) {
totalAssets {
amount {
value
}
}
totalSupply {
value
}
supplyCap {
amount {
value
}
}
targetRate {
value
}
paused
user {
shares {
amount {
value
}
}
balance {
amount {
value
}
}
maxDeposit {
amount {
value
}
}
maxWithdraw {
amount {
value
}
}
}
}
}
{
"data": {
"sghoVault": {
"totalAssets": { "amount": { "value": "50000000.0" } },
"totalSupply": { "value": "49800000.0" },
"supplyCap": { "amount": { "value": "400000000.0" } },
"targetRate": { "value": "0.0425" },
"paused": false,
"user": {
"shares": { "amount": { "value": "995.0" } },
"balance": { "amount": { "value": "1000.5" } },
"maxDeposit": { "amount": { "value": "5000.0" } },
"maxWithdraw": { "amount": { "value": "1000.5" } }
}
}
}
}
Deposit GHO
Deposit GHO into the sGHO vault to start earning yield.
1. Preview the Deposit
Returns the number of sGHO shares that would be minted for a given GHO deposit amount.
In React, the use hook supports both a loading state and React Suspense.
const { data, loading, error } = useSghoVaultPreviewDeposit({
amount: "1000",
chainId: chainId(1),
});
const { data } = useSghoVaultPreviewDeposit({
amount: "1000",
chainId: chainId(1),
suspense: true,
});
Then preview the deposit.
import { useSghoVaultPreviewDeposit, chainId } from "@aave/react";
// …
const { data, loading, error } = useSghoVaultPreviewDeposit({
amount: "1000", // GHO to deposit
chainId: chainId(1),
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
// data: DecimalValue - sGHO shares to be minted
import { sghoVaultPreviewDeposit } from "@aave/client/actions";
import { chainId } from "@aave/client";
import { client } from "./client";
const result = await sghoVaultPreviewDeposit(client, {
amount: "1000",
chainId: chainId(1),
});
if (result.isOk()) {
console.log("sGHO shares to receive:", result.value);
}
query {
sghoVaultPreviewDeposit(request: { amount: "1000", chainId: 1 }) {
value
}
}
2. Prepare the Deposit Execution Plan
Use the use hook in React, the sgho action in TypeScript, or the sgho query in GraphQL to create the execution plan for depositing GHO.
import { useWalletClient } from "wagmi";
import { useSghoVaultDeposit, bigDecimal, evmAddress, chainId } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [deposit, depositing] = useSghoVaultDeposit();
const execute = async () => {
const result = await deposit({
amount: {
value: bigDecimal(1000), // 1000 GHO
},
depositor: evmAddress(walletClient!.account.address),
chainId: chainId(1),
// sharesRecipient: evmAddress("0x1234…"), // defaults to depositor
});
// …
};
import { sghoVaultDeposit } from "@aave/client/actions";
import { bigDecimal, chainId, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await sghoVaultDeposit(client, {
amount: {
value: bigDecimal(1000), // 1000 GHO
},
depositor: evmAddress(wallet.account.address),
chainId: chainId(1),
});
// …
query {
sghoVaultDeposit(
request: {
amount: { value: "1000" }
depositor: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
chainId: 1
# sharesRecipient: "0x…"
}
) {
__typename
... on TransactionRequest {
to
from
data
value
chainId
operation
}
... on ApprovalRequired {
reason
requiredAmount {
value
decimals
}
currentAllowance {
value
decimals
}
approval {
to
from
data
value
chainId
operation
}
originalTransaction {
to
from
data
value
chainId
operation
}
}
... on InsufficientBalanceError {
required {
value
decimals
}
available {
value
decimals
}
}
}
}
3. Process the Deposit Execution Plan
- React: Use the
usehook for the wallet library of your choice to send the transactions in the execution plan.Send Transaction - TypeScript: Use
sendfor your chosen wallet library.With
import { useWalletClient } from "wagmi";
import { errAsync, useSghoVaultDeposit } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [deposit, depositing] = useSghoVaultDeposit();
const [sendTransaction, sending] = useSendTransaction(walletClient);
const loading = depositing.loading || sending.loading;
const error = depositing.error || sending.error;
// …
const execute = async () => {
const result = await deposit({
// …
}).andThen((plan) => {
switch (plan.__typename) {
case "TransactionRequest":
return sendTransaction(plan);
case "ApprovalRequired":
return sendTransaction(plan.approval).andThen(() =>
sendTransaction(plan.originalTransaction)
);
case "InsufficientBalanceError":
return errAsync(
new Error(`Insufficient balance: ${plan.required.value} required.`)
);
}
});
if (result.isErr()) {
console.error("Deposit failed:", result.error);
} else {
console.log("Deposit successful:", result.value);
}
};
import { sghoVaultDeposit } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await sghoVaultDeposit(client, {
// …
}).andThen(sendWith(wallet));
if (result.isErr()) {
console.error("Deposit failed:", result.error);
} else {
console.log("Deposit successful:", result.value);
}
In GraphQL, if the response is a Transaction, send it directly using the wallet associated with the from address.
{
"data": {
"sghoVaultDeposit": {
"__typename": "TransactionRequest",
"to": "0x1234567890abcdef1234567890abcdef12345678",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x6e553f65000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"value": "0",
"chainId": 1,
"operation": "SGHO_VAULT_DEPOSIT"
}
}
}
If the response is an Approval, send the approval transaction first, then the original.
{
"data": {
"sghoVaultDeposit": {
"__typename": "ApprovalRequired",
"reason": "Insufficient allowance for ERC-20 token transfer",
"requiredAmount": { "value": "1000.0", "decimals": 18 },
"currentAllowance": { "value": "0.0", "decimals": 18 },
"approval": {
"to": "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x095ea7b3000000000000000000000000123456789…",
"value": "0",
"chainId": 1,
"operation": null
},
"originalTransaction": {
"to": "0x1234567890abcdef1234567890abcdef12345678",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x6e553f65000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"value": "0",
"chainId": 1,
"operation": "SGHO_VAULT_DEPOSIT"
}
}
}
}
If the response is an Insufficient, inform the user they don’t have enough GHO to deposit the requested amount.
{
"data": {
"sghoVaultDeposit": {
"__typename": "InsufficientBalanceError",
"required": { "value": "1000.0", "decimals": 18 },
"available": { "value": "750.0", "decimals": 18 }
}
}
}
Redeem sGHO
Redeem sGHO shares for GHO. Withdrawals are atomic with no cooldown period.
1. Preview the Redeem
Returns the GHO amount that would be received for redeeming a given number of sGHO shares.
In React, the use hook supports both a loading state and React Suspense.
const { data, loading, error } = useSghoVaultPreviewRedeem({
amount: "995",
chainId: chainId(1),
});
const { data } = useSghoVaultPreviewRedeem({
amount: "995",
chainId: chainId(1),
suspense: true,
});
Then preview the redemption.
import { useSghoVaultPreviewRedeem, chainId } from "@aave/react";
// …
const { data, loading, error } = useSghoVaultPreviewRedeem({
amount: "995", // sGHO shares to redeem
chainId: chainId(1),
});
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
// data: DecimalValue - GHO amount to be received
import { sghoVaultPreviewRedeem } from "@aave/client/actions";
import { chainId } from "@aave/client";
import { client } from "./client";
const result = await sghoVaultPreviewRedeem(client, {
amount: "995",
chainId: chainId(1),
});
if (result.isOk()) {
console.log("GHO to receive:", result.value);
}
query {
sghoVaultPreviewRedeem(request: { amount: "995", chainId: 1 }) {
value
}
}
2. Prepare the Redeem Execution Plan
Use the use hook in React, the sgho action in TypeScript, or the sgho query in GraphQL to create the execution plan. Pass max to redeem all shares, or shares for an exact amount.
import { useWalletClient } from "wagmi";
import {
useSghoVaultRedeemShares,
bigDecimal,
evmAddress,
chainId,
} from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [redeemShares, redeeming] = useSghoVaultRedeemShares();
const execute = async () => {
const result = await redeemShares({
amount: { maxRedeem: true }, // or: { shares: bigDecimal(1000) }
sharesOwner: evmAddress(walletClient!.account.address),
chainId: chainId(1),
// recipient: evmAddress("0x1234…"), // defaults to sharesOwner
});
// …
};
import { sghoVaultRedeemShares } from "@aave/client/actions";
import { bigDecimal, chainId, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await sghoVaultRedeemShares(client, {
amount: { maxRedeem: true }, // or: { shares: bigDecimal(1000) }
sharesOwner: evmAddress(wallet.account.address),
chainId: chainId(1),
});
// …
query {
sghoVaultRedeemShares(
request: {
amount: { maxRedeem: true }
sharesOwner: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
chainId: 1
# recipient: "0x…"
}
) {
__typename
... on TransactionRequest {
to
from
data
value
chainId
operation
}
... on InsufficientBalanceError {
required {
value
decimals
}
available {
value
decimals
}
}
}
}
3. Process the Redeem Execution Plan
- React: Use the
usehook for the wallet library of your choice to send the transaction.Send Transaction - TypeScript: Use
sendfor your chosen wallet library.With
import { useWalletClient } from "wagmi";
import { errAsync, useSghoVaultRedeemShares } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [redeemShares, redeeming] = useSghoVaultRedeemShares();
const [sendTransaction, sending] = useSendTransaction(walletClient);
const loading = redeeming.loading || sending.loading;
const error = redeeming.error || sending.error;
// …
const execute = async () => {
const result = await redeemShares({
// …
}).andThen((plan) => {
switch (plan.__typename) {
case "TransactionRequest":
return sendTransaction(plan);
case "InsufficientBalanceError":
return errAsync(
new Error(
`Insufficient balance: ${plan.required.value} is the maximum redemption allowed.`
)
);
}
});
if (result.isErr()) {
console.error("Redeem failed:", result.error);
} else {
console.log("Redeem successful:", result.value);
}
};
import { sghoVaultRedeemShares } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await sghoVaultRedeemShares(client, {
// …
}).andThen(sendWith(wallet));
if (result.isErr()) {
console.error("Redeem failed:", result.error);
} else {
console.log("Redeem successful:", result.value);
}
In GraphQL, if the response is a Transaction, send it directly using the wallet associated with the from address.
{
"data": {
"sghoVaultRedeemShares": {
"__typename": "TransactionRequest",
"to": "0x1234567890abcdef1234567890abcdef12345678",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0xba087652000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"value": "0",
"chainId": 1,
"operation": "SGHO_VAULT_REDEEM_SHARES"
}
}
}
If the response is an Insufficient, inform the user they don’t have enough sGHO shares to redeem.
{
"data": {
"sghoVaultRedeemShares": {
"__typename": "InsufficientBalanceError",
"required": { "value": "1000.0", "decimals": 18 },
"available": { "value": "750.0", "decimals": 18 }
}
}
}
Legacy sGHO
The Merit-based sGHO contract is being phased out, and its reward rate steps down to 0%. Withdraw GHO and deposit into the new ERC-4626 sGHO vault.
The following API covers the legacy sGHO contract (Merit-based reward distribution via Merkl).
Balance
Retrieve the balance of a user’s sGHO. Use the use hook in React, the savings action in TypeScript, or the savings query in GraphQL to fetch the balance of a user’s sGHO.
In React, the hook supports both a loading state and React Suspense.
const { data, loading, error } = useSavingsGhoBalance({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
});
const { data } = useSavingsGhoBalance({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
suspense: true,
});
Then fetch the balance of a user’s sGHO.
import { useSavingsGhoBalance, evmAddress } from "@aave/react";
// …
const { data, loading, error } = useSavingsGhoBalance({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
});
if (loading) {
return <p>Loading sGHO balance…</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
// data: TokenAmount
import { savingsGhoBalance } from "@aave/client/actions";
import { evmAddress } from "@aave/client";
import { client } from "./client";
const result = await savingsGhoBalance(client, {
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
});
if (result.isErr()) {
console.error(result.error);
} else {
// result.value: TokenAmount
console.log(result.value);
}
query {
savingsGhoBalance(
request: { user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234" }
) {
usdPerToken
amount {
raw
value
decimals
}
usd
}
}
{
"data": {
"savingsGhoBalance": {
"usdPerToken": "1.0",
"amount": {
"raw": "100000000000000000000",
"decimals": 18,
"value": "100.0"
},
"usd": "100.00"
}
}
}
Withdraw GHO
Withdraw sGHO to GHO instantly with no cooldown period. You can still claim any previous rewards.
Follow these steps to withdraw sGHO to GHO:
1. Prepare the Withdraw Execution Plan
First, prepare the execution plan for the withdrawal operation. Use the use hook in React, the savings action in TypeScript, or the savings query in GraphQL to create the execution plan for withdrawing sGHO to GHO.
import { useWalletClient } from "wagmi";
import { useSavingsGhoWithdraw, bigDecimal, evmAddress } from "@aave/react";
// …
const { data: walletClient } = useWalletClient();
const [withdraw, withdrawing] = useSavingsGhoWithdraw();
const execute = async () => {
const result = await withdraw({
amount: {
exact: bigDecimal(1000), // 1000 sGHO
},
sharesOwner: evmAddress(walletClient!.account.address),
// recipient: evmAddress("0x1234…"), if different from sharesOwner
});
// …
};
import { savingsGhoWithdraw } from "@aave/client/actions";
import { bigDecimal, evmAddress } from "@aave/client";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await savingsGhoWithdraw(client, {
amount: {
exact: bigDecimal(1000), // 1000 sGHO
},
sharesOwner: evmAddress(wallet.account.address),
// recipient: evmAddress("0x1234…"), if different from sharesOwner
});
// …
query {
savingsGhoWithdraw(
request: {
amount: { exact: "1000" }
sharesOwner: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
# Optional: defaults to the sharesOwner
# recipient: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
}
) {
__typename
... on TransactionRequest {
to
from
data
value
chainId
operation
}
... on ApprovalRequired {
reason
requiredAmount {
value
decimals
}
currentAllowance {
value
decimals
}
approval {
to
from
data
value
chainId
operation
}
originalTransaction {
to
from
data
value
chainId
operation
}
}
... on InsufficientBalanceError {
required {
value
decimals
}
available {
value
decimals
}
}
}
}
2. Process the Withdraw Execution Plan
Finally, handle the execution plan.
- React: Use the
usehook for the wallet library of your choice to send the transaction.Send Transaction - TypeScript: Use
sendfor your chosen wallet library to initiate the transaction that’s part of the execution plan.With
import { useWalletClient } from "wagmi";
import { errAsync, useSavingsGhoWithdraw } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const [withdraw, withdrawing] = useSavingsGhoWithdraw();
const [sendTransaction, sending] = useSendTransaction(walletClient);
const loading = withdrawing.loading || sending.loading;
const error = withdrawing.error || sending.error;
// …
const execute = async () => {
const result = await withdraw({
// …
}).andThen((plan) => {
switch (plan.__typename) {
case "TransactionRequest":
return sendTransaction(plan);
case "ApprovalRequired":
return sendTransaction(plan.approval).andThen(() =>
sendTransaction(plan.originalTransaction)
);
case "InsufficientBalanceError":
return errAsync(
new Error(
`Insufficient balance to withdraw: ${plan.required.value} is the maximum withdrawal allowed.`
)
);
}
});
if (result.isErr()) {
console.error("Withdraw failed:", result.error);
} else {
console.log("Withdraw successful:", result.value);
}
};
import { savingsGhoWithdraw } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const result = await savingsGhoWithdraw(client, {
// …
}).andThen(sendWith(wallet));
if (result.isErr()) {
console.error("Withdrawal failed:", result.error);
} else {
console.log("Withdrawal successful:", result.value);
}
In GraphQL, if the response is a Transaction, you can send it directly using the wallet associated with the from address.
{
"data": {
"savingsGhoWithdraw": {
"__typename": "TransactionRequest",
"to": "0x1234567890abcdef1234567890abcdef12345678",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x6e553f65000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"value": "0",
"chainId": 1,
"operation": "SAVINGS_GHO_WITHDRAW"
}
}
}
If the response is an Approval, you must first send the approval transaction. Once it succeeds, you can then send the original.
{
"data": {
"savingsGhoWithdraw": {
"__typename": "ApprovalRequired",
"reason": "Insufficient allowance for ERC-20 token transfer",
"requiredAmount": {
"value": "1000.0",
"decimals": 18
},
"currentAllowance": {
"value": "0.0",
"decimals": 18
},
"approval": {
"to": "0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x095ea7b3000000000000000000000000123456789…",
"value": "0",
"chainId": 1,
"operation": null
},
"originalTransaction": {
"to": "0x1234567890abcdef1234567890abcdef12345678",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x6e553f65000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"value": "0",
"chainId": 1,
"operation": "SAVINGS_GHO_WITHDRAW"
}
}
}
}
If the response is an Insufficient, inform the user that they don’t have enough balance to withdraw the requested amount.
{
"data": {
"savingsGhoWithdraw": {
"__typename": "InsufficientBalanceError",
"required": {
"value": "1000.0",
"decimals": 18
},
"available": {
"value": "750.0",
"decimals": 18
}
}
}
}
Claim Incentives
Users who deposited into the legacy sGHO become eligible for rewards distributed through Merkl.
Claim these rewards separately. They are not added to the sGHO balance.
Users can claim rewards in two ways:
- Through the ACI Merit UI for a simple interface to view and claim rewards
- Using the SDK
Follow these steps to claim rewards using the SDK:
1. Fetch the Claimable Rewards
First, determine if a user has claimable rewards. Use the use hook in React, the user action in TypeScript, or the user query in GraphQL to fetch the user’s sGHO claimable rewards and the transaction to claim them.
In React, the hook supports both a loading state and React Suspense.
const { data, loading, error } = useUserMeritRewards({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
chainId: chainId(1),
});
const { data } = useUserMeritRewards({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
chainId: chainId(1),
suspense: true,
});
Then fetch the user’s claimable rewards and the transaction to claim them.
import { useUserMeritRewards, evmAddress, chainId } from "@aave/react";
// …
const sGHO_ADDRESS = evmAddress("0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d");
const { data, loading, error } = useUserMeritRewards({
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
chainId: chainId(1),
filter: {
tokens: [sGHO_ADDRESS],
},
});
if (loading) {
return <p>Loading claimable rewards…</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
// data: UserMeritRewards | null
import { userMeritRewards } from "@aave/client/actions";
import { evmAddress, chainId } from "@aave/client";
import { client } from "./client";
const sGHO_ADDRESS = evmAddress("0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d");
const result = await userMeritRewards(client, {
user: evmAddress("0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"),
chainId: chainId(1),
filter: {
tokens: [sGHO_ADDRESS],
},
});
if (result.isErr()) {
console.error("sGHO claim rewards error:", result.error);
} else {
// result.value: UserMeritRewards | null
console.log(result.value);
}
query {
userMeritRewards(
request: {
user: "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234"
chainId: 1
filter: { tokens: ["0x1a88Df1cFe15Af22B3c4c783D4e6F7F9e0C1885d"] } # sGHO Address
}
) {
chain
claimable {
amount {
usdPerToken
usd
amount {
value
decimals
raw
}
}
currency {
address
imageUrl
name
symbol
decimals
chainId
}
}
transaction {
to
from
data
value
chainId
operation
}
}
}
If data is null, the user has no sGHO claimable rewards.
2. Claim Rewards
Finally, if the user has claimable rewards, they can claim them by sending the transaction.
- React: Use the
usehook for the wallet library of your choice to send the transactions in the execution plan.Send Transaction - TypeScript: Use
sendfor your chosen wallet library.With - GraphQL: Send the
transactionrequest directly using the wallet associated with the from address.
import { useWalletClient } from "wagmi";
import { useUserMeritRewards } from "@aave/react";
import { useSendTransaction } from "@aave/react/viem";
// …
const { data: walletClient } = useWalletClient();
const { data } = useUserMeritRewards({
// …
suspense: true,
});
const [sendTransaction, sending] = useSendTransaction(walletClient);
// …
const execute = async () => {
if (data !== null) {
const result = await sendTransaction(data.transaction);
if (result.isErr()) {
console.error(result.error.message);
} else {
console.log("sGHO claim rewards successful with hash:", result.value);
}
}
};
import { userMeritRewards } from "@aave/client/actions";
import { sendWith } from "@aave/client/viem";
import { client } from "./client";
import { wallet } from "./wallet";
const sGhoRewards = await userMeritRewards(client, {
// …
});
if (sGhoRewards.isErr()) {
console.error(sGhoRewards.error);
process.exit(1); // or eager return if in a function
}
if (!sGhoRewards.value) {
console.log("No rewards to claim");
process.exit(0); // or eager return if in a function
}
const result = await sendWith(wallet)(sGhoRewards.value.transaction);
if (result.isErr()) {
console.error("Claiming sGHO rewards failed:", result.error);
} else {
console.log("sGHO rewards claimed successfully:", result.value);
}
{
"data": {
"userMeritRewards": {
"chain": 1,
"claimable": [
{
"amount": {
"usdPerToken": "1.000000000000000000",
"usd": "1.00"
// …
},
"currency": {
"address": "0x000000000000000000000000000000000000000A",
"name": "Example Token",
"symbol": "EXAMPLE"
// …
}
}
],
"transaction": {
"to": "0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae",
"from": "0x742d35cc6e5c4ce3b69a2a8c7c8e5f7e9a0b1234",
"data": "0x71ee95c0…",
"value": "0",
"chainId": 1,
"operation": "USER_MERIT_REWARDS"
}
}
}
}
For more details, see the GHO Savings Upgrade forum post.
