Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@blend-capital/blend-sdk",
"version": "3.0.0-beta.5",
"version": "3.0.0-beta.6",
"description": "Javascript SDK for the Blend Protocol",
"type": "module",
"scripts": {
Expand Down
10 changes: 6 additions & 4 deletions src/pool/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ export class PoolV1 extends Pool {
): Promise<PoolV1> {
const timestamp = Math.floor(Date.now() / 1000);

const reserveList = await Promise.all(
metadata.reserveList.map((asset, index) =>
ReserveV1.load(network, id, BigInt(metadata.backstopRate), asset, index, timestamp)
)
const reserveList = await ReserveV1.loadList(
network,
id,
BigInt(metadata.backstopRate),
metadata.reserveList,
timestamp
);
const reserves = new Map<string, Reserve>();
for (const reserve of reserveList) {
Expand Down
125 changes: 118 additions & 7 deletions src/pool/reserve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { Network, PoolContractV2, Version } from '../index.js';
import { decodeEntryKey } from '../ledger_entry_helper.js';
import * as FixedMath from '../math.js';
import {
getEmissionEntryTokenType,
getEmissionIndex,
getReserveId,
ReserveConfig,
ReserveData,
ReserveEmissionConfig,
Expand Down Expand Up @@ -516,19 +517,19 @@ export class ReserveV1 extends Reserve {
reserveData = ReserveData.fromLedgerEntryData(ledgerEntry);
break;
case `EmisConfig`: {
const token_type = getEmissionEntryTokenType(ledgerEntry);
if (token_type == 0) {
const index = getEmissionIndex(ledgerEntry);
if (index % 2 == 0) {
emissionBorrowConfig = EmissionConfig.fromLedgerEntryData(ledgerEntry);
} else if (token_type == 1) {
} else {
emissionSupplyConfig = EmissionConfig.fromLedgerEntryData(ledgerEntry);
}
break;
}
case `EmisData`: {
const token_type = getEmissionEntryTokenType(ledgerEntry);
if (token_type == 0) {
const index = getEmissionIndex(ledgerEntry);
if (index % 2 == 0) {
emissionBorrowData = EmissionData.fromLedgerEntryData(ledgerEntry);
} else if (token_type == 1) {
} else {
emissionSupplyData = EmissionData.fromLedgerEntryData(ledgerEntry);
}
break;
Expand Down Expand Up @@ -576,6 +577,116 @@ export class ReserveV1 extends Reserve {
reserve.accrue(backstopTakeRate, timestamp);
return reserve;
}

static async loadList(
network: Network,
poolId: string,
backstopTakeRate: bigint,
reserveList: string[],
timestamp?: number
): Promise<Reserve[]> {
const reserves = new Array<Reserve>();
const stellarRpc = new rpc.Server(network.rpc, network.opts);

const ledgerKeys: xdr.LedgerKey[] = [];
for (const [index, reserveId] of reserveList.entries()) {
const dTokenIndex = index * 2;
const bTokenIndex = index * 2 + 1;
ledgerKeys.push(
...[
ReserveConfig.ledgerKey(poolId, reserveId),
ReserveData.ledgerKey(poolId, reserveId),
ReserveEmissionConfig.ledgerKey(poolId, bTokenIndex),
ReserveEmissionData.ledgerKey(poolId, bTokenIndex),
ReserveEmissionConfig.ledgerKey(poolId, dTokenIndex),
ReserveEmissionData.ledgerKey(poolId, dTokenIndex),
]
);
}

const reserveLedgerEntries = await stellarRpc.getLedgerEntries(...ledgerKeys);

const reserveConfigMap: Map<string, ReserveConfig> = new Map();
const reserveDataMap: Map<string, ReserveData> = new Map();
const emissionConfigMap: Map<number, EmissionConfig> = new Map();
const emissionDataMap: Map<number, EmissionData> = new Map();

for (const entry of reserveLedgerEntries.entries) {
const ledgerEntry = entry.val;
const key = decodeEntryKey(ledgerEntry.contractData().key());
switch (key) {
case 'ResConfig': {
const reserveId = getReserveId(ledgerEntry);
reserveConfigMap.set(reserveId, ReserveConfig.fromLedgerEntryData(ledgerEntry));
break;
}
case 'ResData': {
const reserveId = getReserveId(ledgerEntry);
reserveDataMap.set(reserveId, ReserveData.fromLedgerEntryData(ledgerEntry));
break;
}
case `EmisConfig`: {
const emissionIndex = getEmissionIndex(ledgerEntry);
emissionConfigMap.set(emissionIndex, EmissionConfig.fromLedgerEntryData(ledgerEntry));
break;
}
case `EmisData`: {
const emissionIndex = getEmissionIndex(ledgerEntry);
emissionDataMap.set(emissionIndex, EmissionData.fromLedgerEntryData(ledgerEntry));
break;
}
default:
throw Error(`Invalid reserve key: should not contain ${key}`);
}
}

for (const [index, reserveId] of reserveList.entries()) {
const reserveConfig = reserveConfigMap.get(reserveId);
const reserveData = reserveDataMap.get(reserveId);
if (reserveConfig == undefined || reserveData == undefined) {
throw new Error('Unable to load reserve: missing data.');
}
const dTokenIndex = index * 2;
const bTokenIndex = index * 2 + 1;
const emissionBorrowConfig = emissionConfigMap.get(dTokenIndex);
const emissionBorrowData = emissionDataMap.get(dTokenIndex);
const emissionSupplyConfig = emissionConfigMap.get(bTokenIndex);
const emissionSupplyData = emissionDataMap.get(bTokenIndex);

let borrowEmissions: Emissions | undefined = undefined;
if (emissionBorrowConfig && emissionBorrowData) {
borrowEmissions = new EmissionsV1(
emissionBorrowConfig,
emissionBorrowData,
reserveLedgerEntries.latestLedger
);
borrowEmissions.accrue(reserveData.dSupply, reserveConfig.decimals, timestamp);
}
let supplyEmissions: Emissions | undefined = undefined;
if (emissionSupplyConfig && emissionSupplyData) {
supplyEmissions = new EmissionsV1(
emissionSupplyConfig,
emissionSupplyData,
reserveLedgerEntries.latestLedger
);
supplyEmissions.accrue(reserveData.bSupply, reserveConfig.decimals, timestamp);
}
const reserve = new ReserveV1(
poolId,
reserveId,
reserveConfig,
reserveData,
borrowEmissions,
supplyEmissions,
0,
0,
reserveLedgerEntries.latestLedger
);
reserve.accrue(backstopTakeRate, timestamp);
reserves.push(reserve);
}
return reserves;
}
}

export class ReserveV2 extends Reserve {
Expand Down
23 changes: 18 additions & 5 deletions src/pool/reserve_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,10 @@ export class ReserveEmissions {
const key = decodeEntryKey(ledgerEntry.contractData().key());
switch (key) {
case `EmisData`: {
const token_type = getEmissionEntryTokenType(ledgerEntry);
if (token_type == 0) {
const token_type = getEmissionIndex(ledgerEntry);
if (token_type % 2 == 0) {
emissionBorrowData = EmissionDataV2.fromLedgerEntryData(ledgerEntry);
} else if (token_type == 1) {
} else {
emissionSupplyData = EmissionDataV2.fromLedgerEntryData(ledgerEntry);
}
break;
Expand Down Expand Up @@ -569,13 +569,26 @@ export class ContractReserve {
/**
* Decode the reserve token type (0 = dToken, 1 = bToken) from a Reserve Emission LedgerEntryData
*/
export function getEmissionEntryTokenType(ledger_entry_data: xdr.LedgerEntryData | string): number {
export function getEmissionIndex(ledger_entry_data: xdr.LedgerEntryData | string): number {
if (typeof ledger_entry_data == 'string') {
ledger_entry_data = xdr.LedgerEntryData.fromXDR(ledger_entry_data, 'base64');
}
const ledgerEntryKey = ledger_entry_data.contractData()?.key()?.vec()?.at(1)?.u32();
if (ledgerEntryKey == undefined) {
throw new Error('Unable to parse emission entry token type');
}
return ledgerEntryKey % 2;
return ledgerEntryKey;
}

export function getReserveId(ledger_entry_data: xdr.LedgerEntryData | string): string {
if (typeof ledger_entry_data == 'string') {
ledger_entry_data = xdr.LedgerEntryData.fromXDR(ledger_entry_data, 'base64');
}
const ledgerEntryKey = Address.fromScAddress(
ledger_entry_data.contractData()?.key()?.vec()?.at(1)?.address()
).toString();
if (ledgerEntryKey == undefined) {
throw new Error('Unable to parse asset id from ledger entry data');
}
return ledgerEntryKey;
}