@funkit/connect
Version:
Funkit Connect SDK elevates DeFi apps via web2 sign-ins and one-click checkouts.
188 lines (184 loc) • 6.17 kB
JavaScript
"use client";
// src/clients/aave.tsx
import {
encodeFunctionData,
erc20Abi,
getAddress,
maxUint256
} from "viem";
import { mainnet as mainnet2 } from "viem/chains";
// src/utils/isMainnetUsdt.ts
import { isTokenEquivalent } from "@funkit/utils";
import { mainnet } from "viem/chains";
var USDT_MAINNET_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
function isMainnetUsdt(chainId, address) {
return isTokenEquivalent({
firstTokenAddress: address,
firstTokenChainId: chainId.toString(),
secondTokenAddress: USDT_MAINNET_ADDRESS,
secondTokenChainId: mainnet.id.toString()
});
}
// src/clients/aave.tsx
var VAULT_DEPOSITOR_BY_CHAIN_ID = {
[mainnet2.id]: getAddress("0x40b72bB73B1E9380629B205e7880Fa409ae7fcc9")
};
var AAVE_SUPPLY_ACTION_TYPE = "AAVE_SUPPLY";
function isAaveSupplySupported(chainId) {
return chainId in VAULT_DEPOSITOR_BY_CHAIN_ID;
}
var AMOUNT_PLACEHOLDER = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffdeadbeefn;
var AAVE_POOL_ABI = [
{
name: "supply",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "asset", type: "address" },
{ name: "amount", type: "uint256" },
{ name: "onBehalfOf", type: "address" },
{ name: "referralCode", type: "uint16" }
],
outputs: []
}
];
var VAULT_DEPOSITOR_ABI = [
{
name: "deposit",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "token", type: "address" },
{ name: "vault", type: "address" },
{ name: "callData", type: "bytes" },
{ name: "minAmountOut", type: "uint256" }
],
outputs: [{ name: "returnData", type: "bytes" }]
}
];
function createAaveSupplyActions(config) {
const {
onBehalfOf,
underlyingAsset,
poolAddress,
chainId,
referralCode = 0
} = config;
const vaultDepositor = VAULT_DEPOSITOR_BY_CHAIN_ID[chainId];
return async () => {
if (!vaultDepositor) {
throw new Error(
`Aave supply is not yet supported on chain ${chainId}. Supported chain IDs: ${Object.keys(VAULT_DEPOSITOR_BY_CHAIN_ID).join(", ")}`
);
}
if (!onBehalfOf) {
throw new Error(
"Please connect your wallet before starting an Aave supply"
);
}
const asset = getAddress(underlyingAsset);
const supplyCalldata = encodeFunctionData({
abi: AAVE_POOL_ABI,
functionName: "supply",
args: [asset, AMOUNT_PLACEHOLDER, onBehalfOf, referralCode]
});
const needsApprovalReset = isMainnetUsdt(chainId, asset);
const resetApproval = {
contractAbi: erc20Abi,
contractAddress: asset,
functionName: "approve",
functionArgs: [vaultDepositor, 0n]
};
const maxApproval = {
contractAbi: erc20Abi,
contractAddress: asset,
functionName: "approve",
functionArgs: [vaultDepositor, maxUint256]
};
const vaultDeposit = {
contractAbi: VAULT_DEPOSITOR_ABI,
contractAddress: vaultDepositor,
functionName: "deposit",
functionArgs: [asset, poolAddress, supplyCalldata, 0n]
};
return needsApprovalReset ? [resetApproval, maxApproval, vaultDeposit] : [maxApproval, vaultDeposit];
};
}
function buildSupplyModalTitleMeta(display) {
const parts = [];
if (display?.supplyAPY !== void 0) {
parts.push(`${display.supplyAPY}% APY`);
}
if (display?.collateralizationEnabled !== void 0) {
parts.push(
`Collateralization ${display.collateralizationEnabled ? "enabled" : "disabled"}`
);
}
return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
}
function createAaveSupplyCheckoutConfig(input) {
const {
underlyingAsset,
poolAddress,
chainId,
walletAddress,
display,
referralCode,
receiptToken,
resolveHealthFactor
} = input;
if (!isAaveSupplySupported(chainId)) {
return void 0;
}
const symbol = display?.symbol ?? "asset";
const receiptTicker = receiptToken?.symbol ?? `a${symbol}`;
return {
modalTitle: `Supply ${symbol}`,
modalTitleMeta: buildSupplyModalTitleMeta(display),
// Same integrator-provided signals as the subtitle, but structured so the
// confirmation screen renders them as rows (DestinationYieldRows). No extra
// fetch — one resolved snapshot, two surfaces. Left undefined when neither
// signal is present so the field stays falsy (no empty-but-truthy object).
destinationYieldInfo: display?.supplyAPY !== void 0 || display?.collateralizationEnabled !== void 0 ? {
supplyApy: display?.supplyAPY,
collateralizationEnabled: display?.collateralizationEnabled
} : void 0,
targetChain: chainId.toString(),
targetAsset: underlyingAsset,
qrcodeActionType: AAVE_SUPPLY_ACTION_TYPE,
// Integrator-owned HF math (live position data); the SDK only renders it.
resolveHealthFactor,
targetAssetTicker: receiptTicker,
checkoutItemTitle: receiptTicker,
// Prefer the aToken's icon (e.g. Aave's aEthUSDC artwork) when known so
// the You-receive row / modal title show the receipt token's branding,
// not the underlying's. Falls back to the integrator-supplied underlying
// icon when reserve metadata isn't available.
iconSrc: receiptToken?.iconSrc ?? display?.iconSrc,
generateActionsParams: createAaveSupplyActions({
onBehalfOf: walletAddress,
underlyingAsset,
poolAddress,
chainId,
referralCode
}),
// The checkout delivers the underlying (USDT); the supply mints the aToken
// (aEthUSDT) to the wallet. Offer adding the aToken — not `targetAsset` —
// so the wallet tracks what the user actually holds. All fields come
// straight from Aave reserve data so the wallet label / icon / decimals
// match what users see on-chain and in explorers.
addToWalletToken: receiptToken ? {
tokenAddress: receiptToken.address,
tokenChainId: chainId.toString(),
tokenSymbol: receiptToken.symbol,
decimals: receiptToken.decimals,
iconSrc: receiptToken.iconSrc
} : void 0
};
}
export {
AAVE_SUPPLY_ACTION_TYPE,
createAaveSupplyActions,
createAaveSupplyCheckoutConfig,
isAaveSupplySupported
};