morphcredit-merchant-sdk
Version:
MorphCredit Merchant SDK for BNPL integration
804 lines (770 loc) • 27.3 kB
JavaScript
import { ethers } from 'ethers';
import { useState, useEffect, useCallback } from 'react';
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
// src/index.ts
// ../../apps/config/addresses.json
var addresses_default = {
scoreOracle: "0xd9Dc385246308FfBEBdEAc210F4c6B2E26Eb096d",
creditRegistry: "0x62179b92bD09Bfc6699646F3394A6595c1E12BB2",
lendingPool: "0x22D194Bb22f66731421C5F93163a7DFC05D2Ed5f",
bnplFactory: "0x50e43053510E8f25280d335F5c7F30b15CF13965"};
var MorphCreditButton = ({
amount,
userAddress,
onSuccess,
onError,
onOffersLoaded,
onWalletConnect,
disabled = false,
className = "",
style,
children = "Pay with MorphCredit",
variant = "primary",
size = "md",
loading = false,
showOffers = false
}) => {
const [sdk] = useState(() => new MorphCreditSDK({}, { enableLogging: true }));
const [isConnecting, setIsConnecting] = useState(false);
const [isProcessing, setIsProcessing] = useState(false);
const [offers, setOffers] = useState([]);
const [, setSelectedOffer] = useState(null);
const [showOfferSelector, setShowOfferSelector] = useState(false);
const [walletAddress, setWalletAddress] = useState(userAddress || null);
useEffect(() => {
sdk.onOffersLoaded((loadedOffers) => {
setOffers(loadedOffers);
onOffersLoaded?.(loadedOffers);
});
sdk.onAgreementCreated((result) => {
setIsProcessing(false);
onSuccess?.(result);
});
sdk.setErrorHandler((error) => {
setIsConnecting(false);
setIsProcessing(false);
onError?.(error);
});
return () => {
sdk.disconnectWallet();
};
}, [sdk, onOffersLoaded, onSuccess, onError]);
useEffect(() => {
setWalletAddress(userAddress || null);
}, [userAddress]);
const connectWallet = useCallback(async () => {
try {
setIsConnecting(true);
const address = await sdk.connectWallet();
setWalletAddress(address);
onWalletConnect?.(address);
} catch (error) {
console.error("Failed to connect wallet:", error);
onError?.(error);
} finally {
setIsConnecting(false);
}
}, [sdk, onWalletConnect, onError]);
const handleClick = useCallback(async () => {
if (disabled || loading || isConnecting || isProcessing) {
return;
}
try {
if (!walletAddress) {
await connectWallet();
return;
}
const userOffers = await sdk.getOffers({
address: walletAddress,
amount,
includeFeatures: true
});
if (userOffers.length === 0) {
throw new Error("No offers available for this amount");
}
if (showOffers && userOffers.length > 1) {
setOffers(userOffers);
setShowOfferSelector(true);
} else {
const firstOffer = userOffers[0];
await createAgreement(firstOffer.id);
}
} catch (error) {
console.error("Error in button click:", error);
onError?.(error);
}
}, [
disabled,
loading,
isConnecting,
isProcessing,
walletAddress,
amount,
showOffers,
sdk,
connectWallet,
onError
]);
const createAgreement = useCallback(async (offerId) => {
try {
setIsProcessing(true);
setShowOfferSelector(false);
await sdk.createAgreement(offerId);
} catch (error) {
console.error("Failed to create agreement:", error);
onError?.(error);
}
}, [sdk, onError]);
const handleOfferSelect = useCallback((offer) => {
setSelectedOffer(offer);
createAgreement(offer.id);
}, [createAgreement]);
const getButtonClasses = () => {
const baseClasses = "morphcredit-button";
const variantClasses = {
primary: "morphcredit-button--primary",
secondary: "morphcredit-button--secondary",
outline: "morphcredit-button--outline"
};
const sizeClasses = {
sm: "morphcredit-button--sm",
md: "morphcredit-button--md",
lg: "morphcredit-button--lg"
};
const stateClasses = {
disabled: disabled || loading || isConnecting || isProcessing ? "morphcredit-button--disabled" : "",
loading: loading || isConnecting || isProcessing ? "morphcredit-button--loading" : ""
};
return [
baseClasses,
variantClasses[variant],
sizeClasses[size],
stateClasses.disabled,
stateClasses.loading,
className
].filter(Boolean).join(" ");
};
const getButtonContent = () => {
if (loading || isConnecting) {
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx("span", { className: "morphcredit-button__spinner" }),
isConnecting ? "Connecting..." : "Loading..."
] });
}
if (isProcessing) {
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx("span", { className: "morphcredit-button__spinner" }),
"Processing..."
] });
}
return children;
};
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
"button",
{
className: getButtonClasses(),
style,
onClick: handleClick,
disabled: disabled || loading || isConnecting || isProcessing,
type: "button",
children: getButtonContent()
}
),
showOfferSelector && offers.length > 0 && /* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-selector", children: [
/* @__PURE__ */ jsx("div", { className: "morphcredit-offer-selector__overlay", onClick: () => setShowOfferSelector(false) }),
/* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-selector__modal", children: [
/* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-selector__header", children: [
/* @__PURE__ */ jsx("h3", { children: "Choose Your Payment Plan" }),
/* @__PURE__ */ jsx(
"button",
{
className: "morphcredit-offer-selector__close",
onClick: () => setShowOfferSelector(false),
children: "\xD7"
}
)
] }),
/* @__PURE__ */ jsx("div", { className: "morphcredit-offer-selector__content", children: offers.map((offer) => /* @__PURE__ */ jsxs(
"div",
{
className: "morphcredit-offer-option",
onClick: () => handleOfferSelect(offer),
children: [
/* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-option__header", children: [
/* @__PURE__ */ jsxs("h4", { children: [
"Tier ",
offer.tier,
" Plan"
] }),
/* @__PURE__ */ jsxs("span", { className: "morphcredit-offer-option__apr", children: [
(offer.apr * 100).toFixed(1),
"% APR"
] })
] }),
/* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-option__details", children: [
/* @__PURE__ */ jsxs("p", { children: [
offer.installments,
" payments of $",
(Number(offer.installmentAmount) / 1e6).toFixed(2)
] }),
/* @__PURE__ */ jsxs("p", { className: "morphcredit-offer-option__total", children: [
"Total: $",
(Number(offer.totalCost) / 1e6).toFixed(2)
] })
] }),
offer.features && /* @__PURE__ */ jsxs("div", { className: "morphcredit-offer-option__features", children: [
offer.features.noLateFees && /* @__PURE__ */ jsx("span", { className: "morphcredit-offer-option__feature", children: "No Late Fees" }),
offer.features.earlyPayoff && /* @__PURE__ */ jsx("span", { className: "morphcredit-offer-option__feature", children: "Early Payoff" }),
offer.features.autoRepay && /* @__PURE__ */ jsx("span", { className: "morphcredit-offer-option__feature", children: "Auto Repay" })
] })
]
},
offer.id
)) })
] })
] }),
/* @__PURE__ */ jsx("style", { children: `
.morphcredit-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
border: none;
border-radius: 8px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
position: relative;
overflow: hidden;
}
.morphcredit-button:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.morphcredit-button--primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.morphcredit-button--primary:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.morphcredit-button--secondary {
background: #f8f9fa;
color: #495057;
border: 2px solid #dee2e6;
}
.morphcredit-button--secondary:hover:not(:disabled) {
background: #e9ecef;
border-color: #adb5bd;
}
.morphcredit-button--outline {
background: transparent;
color: #667eea;
border: 2px solid #667eea;
}
.morphcredit-button--outline:hover:not(:disabled) {
background: #667eea;
color: white;
}
.morphcredit-button--sm {
padding: 8px 16px;
font-size: 14px;
min-height: 36px;
}
.morphcredit-button--md {
padding: 12px 24px;
font-size: 16px;
min-height: 44px;
}
.morphcredit-button--lg {
padding: 16px 32px;
font-size: 18px;
min-height: 52px;
}
.morphcredit-button__spinner {
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top: 2px solid currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.morphcredit-offer-selector {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.morphcredit-offer-selector__overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.morphcredit-offer-selector__modal {
position: relative;
background: white;
border-radius: 12px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.morphcredit-offer-selector__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px;
border-bottom: 1px solid #e9ecef;
}
.morphcredit-offer-selector__header h3 {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #212529;
}
.morphcredit-offer-selector__close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #6c757d;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.2s;
}
.morphcredit-offer-selector__close:hover {
background: #f8f9fa;
}
.morphcredit-offer-selector__content {
padding: 24px;
}
.morphcredit-offer-option {
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
cursor: pointer;
transition: all 0.2s ease;
}
.morphcredit-offer-option:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
.morphcredit-offer-option__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.morphcredit-offer-option__header h4 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #212529;
}
.morphcredit-offer-option__apr {
background: #e3f2fd;
color: #1976d2;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
}
.morphcredit-offer-option__details {
margin-bottom: 12px;
}
.morphcredit-offer-option__details p {
margin: 4px 0;
color: #495057;
}
.morphcredit-offer-option__total {
font-weight: 600;
color: #212529 !important;
}
.morphcredit-offer-option__features {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.morphcredit-offer-option__feature {
background: #f8f9fa;
color: #6c757d;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
` })
] });
};
var button_default = MorphCreditButton;
// src/index.ts
var ErrorCodes = /* @__PURE__ */ ((ErrorCodes2) => {
ErrorCodes2["WALLET_NOT_CONNECTED"] = "WALLET_NOT_CONNECTED";
ErrorCodes2["WALLET_CONNECTION_FAILED"] = "WALLET_CONNECTION_FAILED";
ErrorCodes2["WRONG_NETWORK"] = "WRONG_NETWORK";
ErrorCodes2["INSUFFICIENT_CREDIT"] = "INSUFFICIENT_CREDIT";
ErrorCodes2["SCORE_NOT_FOUND"] = "SCORE_NOT_FOUND";
ErrorCodes2["SCORE_EXPIRED"] = "SCORE_EXPIRED";
ErrorCodes2["AGREEMENT_FAILED"] = "AGREEMENT_FAILED";
ErrorCodes2["AGREEMENT_NOT_FOUND"] = "AGREEMENT_NOT_FOUND";
ErrorCodes2["AGREEMENT_EXPIRED"] = "AGREEMENT_EXPIRED";
ErrorCodes2["NETWORK_ERROR"] = "NETWORK_ERROR";
ErrorCodes2["RPC_ERROR"] = "RPC_ERROR";
ErrorCodes2["TIMEOUT"] = "TIMEOUT";
ErrorCodes2["USER_REJECTED"] = "USER_REJECTED";
ErrorCodes2["INSUFFICIENT_BALANCE"] = "INSUFFICIENT_BALANCE";
ErrorCodes2["INVALID_ADDRESS"] = "INVALID_ADDRESS";
ErrorCodes2["INVALID_AMOUNT"] = "INVALID_AMOUNT";
ErrorCodes2["INVALID_OFFER"] = "INVALID_OFFER";
return ErrorCodes2;
})(ErrorCodes || {});
var MorphCreditError = class extends Error {
constructor(code, message, details, retryable = false) {
super(message);
this.code = code;
this.details = details;
this.retryable = retryable;
this.name = "MorphCreditError";
}
};
var _MorphCreditSDK = class _MorphCreditSDK {
constructor(config = {}, options = {}) {
this.provider = null;
this.signer = null;
this.eventCallbacks = /* @__PURE__ */ new Map();
this.offersCache = /* @__PURE__ */ new Map();
const runtimeScoring = typeof globalThis !== "undefined" && globalThis.__MORPHCREDIT_SCORING_URL__ || typeof process !== "undefined" && process.env && process.env.MORPHCREDIT_SCORING_URL || "https://morphcredit.onrender.com";
this.config = {
rpcUrl: "https://rpc-holesky.morphl2.io",
contracts: {
scoreOracle: addresses_default.scoreOracle,
creditRegistry: addresses_default.creditRegistry,
lendingPool: addresses_default.lendingPool,
bnplFactory: addresses_default.bnplFactory
},
scoringService: runtimeScoring,
networkId: 2810,
gasLimit: 8e5,
confirmations: 1,
...config
};
this.options = {
autoConnect: false,
enableLogging: false,
retryAttempts: 3,
retryDelay: 1e3,
...options
};
this.initializeProviders();
}
async getSigner() {
if (!this.provider) throw new MorphCreditError("WALLET_CONNECTION_FAILED" /* WALLET_CONNECTION_FAILED */, "Provider not initialized");
if (!this.signer) {
this.signer = await this.provider.getSigner();
}
return this.signer;
}
initializeProviders() {
try {
if (typeof window !== "undefined" && window.ethereum) {
this.provider = new ethers.BrowserProvider(window.ethereum);
}
if (this.options.enableLogging) {
console.log("MorphCreditSDK initialized with config:", this.config);
}
} catch (error) {
console.error("Failed to initialize providers:", error);
}
}
async connectWallet() {
try {
if (!this.provider) {
throw new MorphCreditError(
"WALLET_CONNECTION_FAILED" /* WALLET_CONNECTION_FAILED */,
"Provider not initialized"
);
}
this.signer = await this.provider.getSigner();
const address = await this.signer.getAddress();
if (this.options.enableLogging) {
console.log("Wallet connected:", address);
}
this.triggerEvent("walletConnected", address);
return address;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
throw new MorphCreditError(
"WALLET_CONNECTION_FAILED" /* WALLET_CONNECTION_FAILED */,
`Failed to connect wallet: ${errorMessage}`,
error
);
}
}
disconnectWallet() {
this.signer = null;
this.triggerEvent("walletDisconnected");
}
getWalletAddress() {
return this.signer ? this.signer.address : null;
}
isWalletConnected() {
return this.signer !== null;
}
async getOffers(request) {
try {
if (!request.address) {
throw new MorphCreditError(
"INVALID_ADDRESS" /* INVALID_ADDRESS */,
"Address is required"
);
}
if (!request.amount || request.amount <= 0) {
throw new MorphCreditError(
"INVALID_AMOUNT" /* INVALID_AMOUNT */,
"Amount must be greater than 0"
);
}
const amountInWei = BigInt(Math.floor(request.amount * 1e6));
const res = await fetch(`${this.config.scoringService.replace(/\/$/, "")}/score`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ address: request.address })
});
if (!res.ok) {
throw new MorphCreditError("NETWORK_ERROR" /* NETWORK_ERROR */, `Scoring service error: ${res.status}`);
}
const json = await res.json();
if (!json?.success || !json?.data?.scoring) {
throw new MorphCreditError("SCORE_NOT_FOUND" /* SCORE_NOT_FOUND */, "Score response invalid");
}
const tier = json.data.scoring.tier;
const tierAprMap = {
A: [0.1, 0.14],
B: [0.14, 0.2],
C: [0.2, 0.28],
D: [0.28, 0.36],
E: [0.36, 0.5]
};
const [aprLow, aprHigh] = tierAprMap[tier] || [0.25, 0.35];
const aprChoices = [aprLow, (aprLow + aprHigh) / 2, aprHigh];
const now = Math.floor(Date.now() / 1e3);
const biweekly = 14 * 24 * 60 * 60;
const installments = 4;
const offers = aprChoices.map((apr, idx) => {
const totalCost = BigInt(Math.floor(Number(amountInWei) * (1 + apr)));
const installmentAmount = BigInt(Number(totalCost) / installments);
const id = `offer_${request.address}_${amountInWei}_${tier}_${idx}`;
const offer = {
id,
principal: amountInWei,
installments,
installmentAmount,
totalCost,
apr,
dueDates: [now + biweekly, now + biweekly * 2, now + biweekly * 3, now + biweekly * 4],
merchant: request.address,
status: "available",
tier,
features: { earlyPayoff: true, autoRepay: true }
};
this.offersCache.set(id, offer);
return offer;
});
if (this.options.enableLogging) {
console.log("Generated offers:", offers);
}
this.triggerEvent("offersLoaded", offers);
return offers;
} catch (error) {
if (error instanceof MorphCreditError) {
throw error;
}
throw new MorphCreditError(
"NETWORK_ERROR" /* NETWORK_ERROR */,
`Failed to get offers: ${error instanceof Error ? error.message : "Unknown error"}`,
error
);
}
}
// Removed mock offer generation
async createAgreement(offerId, borrowerAddress) {
try {
const signer = await this.getSigner();
const offer = this.offersCache.get(offerId);
if (!offer) throw new MorphCreditError("INVALID_OFFER" /* INVALID_OFFER */, "Offer not found in cache");
const borrower = borrowerAddress || await signer.getAddress();
const merchant = await signer.getAddress();
const fac = new ethers.Contract(this.config.contracts.bnplFactory, _MorphCreditSDK.BNPL_FACTORY_ABI, signer);
if (!this.options.skipRoleCheck) {
try {
const FACTORY_ROLE = ethers.id("FACTORY_ROLE");
const caller = await signer.getAddress();
const hasRole = await fac.hasRole(FACTORY_ROLE, caller);
if (!hasRole) {
throw new MorphCreditError("AGREEMENT_FAILED" /* AGREEMENT_FAILED */, "Merchant is not authorized (FACTORY_ROLE missing)");
}
} catch (e) {
if (this.options.enableLogging) console.warn("Role check skipped due to error; set skipRoleCheck to true to silence", e);
if (!this.options.skipRoleCheck) throw e;
}
}
const aprBps = Math.floor(offer.apr * 1e4);
const tx = await fac.createAgreement(borrower, merchant, offer.principal, offer.installments, aprBps, {
gasLimit: this.config.gasLimit
});
const receipt = await tx.wait(this.config.confirmations);
let agreementAddress = tx.hash;
try {
const iface = new ethers.Interface(_MorphCreditSDK.BNPL_FACTORY_ABI);
const factoryAddr = this.config.contracts.bnplFactory.toLowerCase();
const logs = (receipt?.logs ?? []).filter((l) => l?.address?.toLowerCase() === factoryAddr);
for (const log of logs) {
try {
const parsed = iface.parseLog({ topics: log.topics, data: log.data });
if (parsed?.name === "AgreementCreated") {
const args = parsed.args;
agreementAddress = args.agreement ?? args[2];
break;
}
} catch {
}
}
} catch {
}
const txResult = {
success: receipt?.status === 1,
txHash: tx.hash,
agreementId: agreementAddress,
blockNumber: receipt?.blockNumber ?? 0,
gasUsed: Number(receipt?.gasUsed ?? 0n),
gasPrice: tx.gasPrice ?? 0n,
receipt
};
this.triggerEvent("agreementCreated", txResult);
return txResult;
} catch (error) {
if (error instanceof MorphCreditError) {
throw error;
}
throw new MorphCreditError(
"AGREEMENT_FAILED" /* AGREEMENT_FAILED */,
`Failed to create agreement: ${error instanceof Error ? error.message : "Unknown error"}`,
error
);
}
}
async getAgreementStatus(agreementAddress) {
try {
if (!this.provider) throw new MorphCreditError("WALLET_CONNECTION_FAILED" /* WALLET_CONNECTION_FAILED */, "Provider not initialized");
const ag = new ethers.Contract(agreementAddress, _MorphCreditSDK.BNPL_AGREEMENT_ABI, this.provider);
const a = await ag.getAgreement();
const installments = await ag.getAllInstallments();
const next = installments.find((i) => !i.isPaid);
const remaining = Number(a.installments) - Number(a.paidInstallments);
const statusNum = Number(a.status);
const status = statusNum === 2 ? "completed" : statusNum === 3 ? "defaulted" : statusNum === 4 ? "written_off" : "active";
return {
id: agreementAddress,
status,
paidInstallments: Number(a.paidInstallments),
totalInstallments: Number(a.installments),
nextDueDate: next ? Number(next.dueDate) : 0,
nextAmount: next ? BigInt(next.amount) : BigInt(0),
remainingBalance: BigInt(remaining) * BigInt(a.installmentAmount),
lastPaymentDate: Number(a.lastPaymentDate),
delinquencyDays: 0
};
} catch (error) {
throw new MorphCreditError(
"AGREEMENT_NOT_FOUND" /* AGREEMENT_NOT_FOUND */,
`Failed to get agreement status: ${error instanceof Error ? error.message : "Unknown error"}`,
error
);
}
}
// Event handling
onAgreementCreated(callback) {
this.addEventCallback("agreementCreated", callback);
}
onOffersLoaded(callback) {
this.addEventCallback("offersLoaded", callback);
}
onWalletConnected(callback) {
this.addEventCallback("walletConnected", callback);
}
onWalletDisconnected(callback) {
this.addEventCallback("walletDisconnected", callback);
}
addEventCallback(event, callback) {
if (!this.eventCallbacks.has(event)) {
this.eventCallbacks.set(event, []);
}
this.eventCallbacks.get(event).push(callback);
}
triggerEvent(event, data) {
const callbacks = this.eventCallbacks.get(event);
if (callbacks) {
callbacks.forEach((callback) => {
try {
callback(data);
} catch (error) {
console.error(`Error in event callback for ${event}:`, error);
}
});
}
}
// Configuration methods
updateConfig(config) {
this.config = { ...this.config, ...config };
this.initializeProviders();
}
updateOptions(options) {
this.options = { ...this.options, ...options };
}
getConfig() {
return { ...this.config };
}
setErrorHandler(handler) {
this.addEventCallback("error", handler);
}
};
// Minimal ABIs
_MorphCreditSDK.BNPL_FACTORY_ABI = [
"function createAgreement(address borrower,address merchant,uint256 principal,uint256 installments,uint256 apr) returns (address)",
"function hasRole(bytes32 role, address account) view returns (bool)",
"event AgreementCreated(address indexed borrower, address indexed merchant, address agreement, uint256 principal)"
];
_MorphCreditSDK.BNPL_AGREEMENT_ABI = [
"function getAgreement() view returns (tuple(uint256 principal,address borrower,address merchant,uint256 installments,uint256 installmentAmount,uint256 apr,uint256 penaltyRate,uint256[] dueDates,uint8 status,uint256 paidInstallments,uint256 lastPaymentDate,uint256 gracePeriod,uint256 writeOffPeriod))",
"function getAllInstallments() view returns (tuple(uint256 id,uint256 amount,uint256 dueDate,bool isPaid,uint256 paidAt,uint256 penaltyAccrued)[])"
];
var MorphCreditSDK = _MorphCreditSDK;
var index_default = MorphCreditSDK;
export { ErrorCodes, button_default as MorphCreditButton, MorphCreditError, MorphCreditSDK, index_default as default };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map