juna
Version:
A cross platform NFT lending client for serious lenders
151 lines (139 loc) • 5.18 kB
text/typescript
import { PrivateKeyAccount, createWalletClient, WalletClient, http } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { BlurLoan, BlurOffer } from "./support/types";
import { LendingPlatform, Offer, OfferType } from "../types";
import { BETH } from "../support/currencies";
import { config } from "../config";
import { Axios } from "axios";
export class GhostApi {
private axios: Axios;
private client: WalletClient;
private account: PrivateKeyAccount;
constructor(
privateKey: `0x${string}`,
rpcUrl: `https://${string}` = config.defaultRpc,
apiKey: string,
walletAddress?: string,
) {
this.account = privateKeyToAccount(privateKey);
this.client = createWalletClient({
account: this.account,
chain: mainnet,
transport: http(rpcUrl),
});
this.axios = new Axios({
headers: {
"x-api-key": apiKey,
"x-wallet-address": walletAddress ?? this.account.address,
},
});
}
public async getLiens(address: `0x${string}`): Promise<BlurLoan[]> {
const result = await this.axios.get(`${config.blur.baseUrlPortfolio}/${address.toLowerCase()}/liens`);
return JSON.parse(result.data).liens;
}
public async getLoanOffers(accountAddress: `0x${string}`, collectionAddress?: `0x${string}`): Promise<BlurOffer[]> {
const result = await this.axios.get(
`${config.blur.baseUrlPortfolio}/${accountAddress.toLowerCase()}/loan-offers?contractAddress=${collectionAddress}`,
);
return JSON.parse(result.data).loanOffers;
}
public async postLoanOffer(
collectionAddress: `0x${string}`,
principal: number,
limit: number,
apr: number,
expiryInMinutes: number,
): Promise<Offer> {
// Format the query
const payload = {
contractAddress: collectionAddress.toLowerCase(),
orders: [
{
contractAddress: collectionAddress.toLowerCase(),
expirationTime: new Date(new Date().getTime() + expiryInMinutes * 60 * 1000).toISOString(),
maxAmount: principal.toString(),
rate: apr * 10000,
totalAmount: limit.toString(),
},
],
userAddress: this.account.address.toLowerCase(),
};
const result = await this.axios.post(`${config.blur.baseUrlBlend}/loan-offer/format`, JSON.stringify(payload), {
headers: { "Content-Type": "application/json" },
});
const format = JSON.parse(result.data);
// Signing
const signData = format.signatures[0].signData;
signData.value.minAmount = BigInt(signData.value.minAmount.hex);
signData.value.maxAmount = BigInt(signData.value.maxAmount.hex);
signData.value.totalAmount = BigInt(signData.value.totalAmount.hex);
signData.value.salt = BigInt(signData.value.salt.hex);
signData.value.auctionDuration = BigInt(signData.value.auctionDuration.hex);
signData.value.expirationTime = BigInt(signData.value.expirationTime.hex);
const signature = await this.account.signTypedData({
domain: signData.domain,
types: signData.types,
primaryType: "LoanOffer",
message: signData.value,
});
// Submit the offer
const payload2 = {
contractAddress: collectionAddress.toLowerCase(),
orders: [
{
contractAddress: collectionAddress.toLowerCase(),
expirationTime: new Date(new Date().getTime() + expiryInMinutes * 60 * 1000).toISOString(),
maxAmount: principal.toString(),
rate: apr * 10000,
totalAmount: limit.toString(),
signature: signature,
marketplaceData: format.signatures[0].marketplaceData,
},
],
userAddress: this.account.address.toLowerCase(),
};
const result2 = await this.axios.post(`${config.blur.baseUrlBlend}/loan-offer/submit`, JSON.stringify(payload2), {
headers: { "Content-Type": "application/json" },
});
const response = JSON.parse(result2.data);
return {
id: response.hashes[0],
platform: LendingPlatform.blur,
lender: this.account.address,
offerDate: new Date(),
expiryDate: new Date(new Date().getTime() + expiryInMinutes * 60 * 1000),
type: OfferType.collectionOffer,
currency: BETH,
principal: principal,
durationInDays: 0,
apr: apr,
collateral: {
collectionAddress: collectionAddress,
collectionName: "",
nftId: "",
},
};
}
public async recallLoan(collectionAddress: `0x${string}`, loanId: string, nftId: string) {
// Fetching the tx data
const payload = {
userAddress: this.account.address.toLowerCase(),
contractAddress: collectionAddress.toLowerCase(),
lienRequests: [
{
lienId: loanId,
tokenId: nftId,
},
],
};
const result = await this.axios.post(`${config.blur.baseUrlBlend}/loan-offer/end`, JSON.stringify(payload), {
headers: { "Content-Type": "application/json" },
});
const format = JSON.parse(result.data);
// Sending the transaction
const tx = format.data.actions[0].txnData;
await this.client.sendTransaction(tx);
}
}