juna
Version:
A cross platform NFT lending client for serious lenders
165 lines (141 loc) • 5.49 kB
text/typescript
import { Gondi } from "gondi";
import { gondiConfig, testChain } from "./config";
import axios, { AxiosInstance } from "axios";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { PrivateKeyAccount, createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";
import {
Loan,
Offer,
LendingClientParameters,
PromissoryNote,
LendingClientWithPromissoryNotes,
Listing,
SingleItemOfferParams,
CollectionOfferParams,
} from "../types";
import Loans from "./api/loans";
import Offers from "./api/offers";
import { gondiLoanMapper, gondiOfferMapper, mapError } from "./support/mappers";
import { CollectionNotSupported } from "../errors";
const nowPlusOffset = (offset: number) => {
const currentTime = new Date();
const currentTimePlusOffset = new Date(currentTime.getTime() + offset * 60 * 1000); // Add offset
return Math.floor(currentTimePlusOffset.getTime() / 1000); // Convert to seconds
};
export interface LendingClientParametersGondi extends LendingClientParameters {
originationFeeInPercentage?: number;
}
export class GondiClient implements LendingClientWithPromissoryNotes {
private http: AxiosInstance;
private readonly account: PrivateKeyAccount;
private loans: Loans;
private offers: Offers;
private client: Gondi;
private originationFeeInPercentage: number;
constructor(p: LendingClientParametersGondi) {
this.account = privateKeyToAccount(p.privateKey ?? generatePrivateKey());
this.http = axios.create({
baseURL: gondiConfig.baseUrl,
});
const wallet = createWalletClient({
account: privateKeyToAccount(p.privateKey ?? generatePrivateKey()),
transport: p.testnet ? http(gondiConfig.rpcTestnet) : http(),
chain: p.testnet ? testChain : mainnet,
});
this.client = new Gondi({ wallet });
this.originationFeeInPercentage = p.originationFeeInPercentage ?? 0;
this.loans = new Loans(this.http);
this.offers = new Offers(this.http, this.client);
}
public async _getCollectionId(collectionAddress: `0x${string}`, slug?: string): Promise<any> {
let collectionId: number;
if (slug) {
collectionId = await this.client.collectionId({
slug: slug,
});
} else {
collectionId = (
await this.client.collectionId({
contractAddress: collectionAddress,
})
)[0];
}
if (collectionId === undefined) {
throw new CollectionNotSupported();
}
return collectionId;
}
public async getListings(): Promise<Listing[]> {
throw Error("Not implemented");
}
public async getListingsForCollection(): Promise<Listing[]> {
throw Error("Not implemented");
}
public async getLoans(): Promise<Loan[]> {
return await this.loans.get([], false).then((res) => res.map(gondiLoanMapper));
}
public async getLoansForCollection(address: `0x${string}`): Promise<Loan[]> {
console.log(address);
throw Error("Not implemented");
}
public async getLoansForAccount(address: `0x${string}`): Promise<Loan[]> {
return await this.loans.get([address], true).then((res) => res.map(gondiLoanMapper));
}
public async getMyLoans(): Promise<Loan[]> {
return await this.getLoansForAccount(this.account.address);
}
public async createSingleItemOffer(offerParams: SingleItemOfferParams): Promise<Offer> {
console.log(offerParams);
throw Error("Not implemented");
}
public async createCollectionOffer(offerParams: CollectionOfferParams): Promise<Offer> {
const payload = {
collectionId: await this._getCollectionId(offerParams.collectionAddress, offerParams.slug),
principalAddress: offerParams.currency.address,
principalAmount: BigInt(offerParams.principal * 1e18),
capacity: BigInt(offerParams.principal * 1e18),
fee: BigInt(
Math.round(
((offerParams.principal * this.originationFeeInPercentage * offerParams.apr * offerParams.durationInDays) /
365) *
1e18,
),
), // Origination fee
aprBps: BigInt(Math.round(offerParams.apr * (1 - this.originationFeeInPercentage) * 10000)),
expirationTime: BigInt(nowPlusOffset(offerParams.expiryInMinutes)),
duration: BigInt(Math.round(offerParams.durationInDays * 24 * 3600)),
requiresLiquidation: false, // Sets the collateral to be liquidated on default.
lenderAddress: offerParams.lenderAddress ?? this.account.address,
// borrowerAddress: null, // Optional: allow only this borrower to accept the offer.
};
return await this.offers
.createCollectionOffer(payload)
.then((res) => gondiOfferMapper(res))
.catch((error) => {
throw mapError(error);
});
}
public async deleteOffer(offerId: string): Promise<void> {
console.log(offerId);
throw new Error("Not implemented!");
}
public async getOffers(): Promise<Offer[]> {
throw Error("Not implemented");
}
public async getOffersForAccount(address: `0x${string}`): Promise<Offer[]> {
console.log(address);
throw new Error("Not implemented!");
}
public async getOffersForCollection(address: `0x${string}`): Promise<Offer[]> {
console.log(address);
throw Error("Not implemented");
}
public async getMyOffers(): Promise<Offer[]> {
throw new Error("Not implemented!");
}
public async getPromissoryNote(loanId: number): Promise<PromissoryNote> {
console.log(loanId);
throw Error("Not implemented");
}
}