Skip to content

Commit 5325b2f

Browse files
authored
Merge pull request #82 from blend-capital/bundled-load
feat: implement bundled load for pool v1 reserves and v2 emissions
2 parents 60e3df6 + bce787d commit 5325b2f

File tree

4 files changed

+143
-17
lines changed

4 files changed

+143
-17
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@blend-capital/blend-sdk",
3-
"version": "3.0.0-beta.5",
3+
"version": "3.0.0-beta.6",
44
"description": "Javascript SDK for the Blend Protocol",
55
"type": "module",
66
"scripts": {

src/pool/pool.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ export class PoolV1 extends Pool {
6565
): Promise<PoolV1> {
6666
const timestamp = Math.floor(Date.now() / 1000);
6767

68-
const reserveList = await Promise.all(
69-
metadata.reserveList.map((asset, index) =>
70-
ReserveV1.load(network, id, BigInt(metadata.backstopRate), asset, index, timestamp)
71-
)
68+
const reserveList = await ReserveV1.loadList(
69+
network,
70+
id,
71+
BigInt(metadata.backstopRate),
72+
metadata.reserveList,
73+
timestamp
7274
);
7375
const reserves = new Map<string, Reserve>();
7476
for (const reserve of reserveList) {

src/pool/reserve.ts

Lines changed: 118 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Network, PoolContractV2, Version } from '../index.js';
44
import { decodeEntryKey } from '../ledger_entry_helper.js';
55
import * as FixedMath from '../math.js';
66
import {
7-
getEmissionEntryTokenType,
7+
getEmissionIndex,
8+
getReserveId,
89
ReserveConfig,
910
ReserveData,
1011
ReserveEmissionConfig,
@@ -516,19 +517,19 @@ export class ReserveV1 extends Reserve {
516517
reserveData = ReserveData.fromLedgerEntryData(ledgerEntry);
517518
break;
518519
case `EmisConfig`: {
519-
const token_type = getEmissionEntryTokenType(ledgerEntry);
520-
if (token_type == 0) {
520+
const index = getEmissionIndex(ledgerEntry);
521+
if (index % 2 == 0) {
521522
emissionBorrowConfig = EmissionConfig.fromLedgerEntryData(ledgerEntry);
522-
} else if (token_type == 1) {
523+
} else {
523524
emissionSupplyConfig = EmissionConfig.fromLedgerEntryData(ledgerEntry);
524525
}
525526
break;
526527
}
527528
case `EmisData`: {
528-
const token_type = getEmissionEntryTokenType(ledgerEntry);
529-
if (token_type == 0) {
529+
const index = getEmissionIndex(ledgerEntry);
530+
if (index % 2 == 0) {
530531
emissionBorrowData = EmissionData.fromLedgerEntryData(ledgerEntry);
531-
} else if (token_type == 1) {
532+
} else {
532533
emissionSupplyData = EmissionData.fromLedgerEntryData(ledgerEntry);
533534
}
534535
break;
@@ -576,6 +577,116 @@ export class ReserveV1 extends Reserve {
576577
reserve.accrue(backstopTakeRate, timestamp);
577578
return reserve;
578579
}
580+
581+
static async loadList(
582+
network: Network,
583+
poolId: string,
584+
backstopTakeRate: bigint,
585+
reserveList: string[],
586+
timestamp?: number
587+
): Promise<Reserve[]> {
588+
const reserves = new Array<Reserve>();
589+
const stellarRpc = new rpc.Server(network.rpc, network.opts);
590+
591+
const ledgerKeys: xdr.LedgerKey[] = [];
592+
for (const [index, reserveId] of reserveList.entries()) {
593+
const dTokenIndex = index * 2;
594+
const bTokenIndex = index * 2 + 1;
595+
ledgerKeys.push(
596+
...[
597+
ReserveConfig.ledgerKey(poolId, reserveId),
598+
ReserveData.ledgerKey(poolId, reserveId),
599+
ReserveEmissionConfig.ledgerKey(poolId, bTokenIndex),
600+
ReserveEmissionData.ledgerKey(poolId, bTokenIndex),
601+
ReserveEmissionConfig.ledgerKey(poolId, dTokenIndex),
602+
ReserveEmissionData.ledgerKey(poolId, dTokenIndex),
603+
]
604+
);
605+
}
606+
607+
const reserveLedgerEntries = await stellarRpc.getLedgerEntries(...ledgerKeys);
608+
609+
const reserveConfigMap: Map<string, ReserveConfig> = new Map();
610+
const reserveDataMap: Map<string, ReserveData> = new Map();
611+
const emissionConfigMap: Map<number, EmissionConfig> = new Map();
612+
const emissionDataMap: Map<number, EmissionData> = new Map();
613+
614+
for (const entry of reserveLedgerEntries.entries) {
615+
const ledgerEntry = entry.val;
616+
const key = decodeEntryKey(ledgerEntry.contractData().key());
617+
switch (key) {
618+
case 'ResConfig': {
619+
const reserveId = getReserveId(ledgerEntry);
620+
reserveConfigMap.set(reserveId, ReserveConfig.fromLedgerEntryData(ledgerEntry));
621+
break;
622+
}
623+
case 'ResData': {
624+
const reserveId = getReserveId(ledgerEntry);
625+
reserveDataMap.set(reserveId, ReserveData.fromLedgerEntryData(ledgerEntry));
626+
break;
627+
}
628+
case `EmisConfig`: {
629+
const emissionIndex = getEmissionIndex(ledgerEntry);
630+
emissionConfigMap.set(emissionIndex, EmissionConfig.fromLedgerEntryData(ledgerEntry));
631+
break;
632+
}
633+
case `EmisData`: {
634+
const emissionIndex = getEmissionIndex(ledgerEntry);
635+
emissionDataMap.set(emissionIndex, EmissionData.fromLedgerEntryData(ledgerEntry));
636+
break;
637+
}
638+
default:
639+
throw Error(`Invalid reserve key: should not contain ${key}`);
640+
}
641+
}
642+
643+
for (const [index, reserveId] of reserveList.entries()) {
644+
const reserveConfig = reserveConfigMap.get(reserveId);
645+
const reserveData = reserveDataMap.get(reserveId);
646+
if (reserveConfig == undefined || reserveData == undefined) {
647+
throw new Error('Unable to load reserve: missing data.');
648+
}
649+
const dTokenIndex = index * 2;
650+
const bTokenIndex = index * 2 + 1;
651+
const emissionBorrowConfig = emissionConfigMap.get(dTokenIndex);
652+
const emissionBorrowData = emissionDataMap.get(dTokenIndex);
653+
const emissionSupplyConfig = emissionConfigMap.get(bTokenIndex);
654+
const emissionSupplyData = emissionDataMap.get(bTokenIndex);
655+
656+
let borrowEmissions: Emissions | undefined = undefined;
657+
if (emissionBorrowConfig && emissionBorrowData) {
658+
borrowEmissions = new EmissionsV1(
659+
emissionBorrowConfig,
660+
emissionBorrowData,
661+
reserveLedgerEntries.latestLedger
662+
);
663+
borrowEmissions.accrue(reserveData.dSupply, reserveConfig.decimals, timestamp);
664+
}
665+
let supplyEmissions: Emissions | undefined = undefined;
666+
if (emissionSupplyConfig && emissionSupplyData) {
667+
supplyEmissions = new EmissionsV1(
668+
emissionSupplyConfig,
669+
emissionSupplyData,
670+
reserveLedgerEntries.latestLedger
671+
);
672+
supplyEmissions.accrue(reserveData.bSupply, reserveConfig.decimals, timestamp);
673+
}
674+
const reserve = new ReserveV1(
675+
poolId,
676+
reserveId,
677+
reserveConfig,
678+
reserveData,
679+
borrowEmissions,
680+
supplyEmissions,
681+
0,
682+
0,
683+
reserveLedgerEntries.latestLedger
684+
);
685+
reserve.accrue(backstopTakeRate, timestamp);
686+
reserves.push(reserve);
687+
}
688+
return reserves;
689+
}
579690
}
580691

581692
export class ReserveV2 extends Reserve {

src/pool/reserve_types.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,10 @@ export class ReserveEmissions {
480480
const key = decodeEntryKey(ledgerEntry.contractData().key());
481481
switch (key) {
482482
case `EmisData`: {
483-
const token_type = getEmissionEntryTokenType(ledgerEntry);
484-
if (token_type == 0) {
483+
const token_type = getEmissionIndex(ledgerEntry);
484+
if (token_type % 2 == 0) {
485485
emissionBorrowData = EmissionDataV2.fromLedgerEntryData(ledgerEntry);
486-
} else if (token_type == 1) {
486+
} else {
487487
emissionSupplyData = EmissionDataV2.fromLedgerEntryData(ledgerEntry);
488488
}
489489
break;
@@ -569,13 +569,26 @@ export class ContractReserve {
569569
/**
570570
* Decode the reserve token type (0 = dToken, 1 = bToken) from a Reserve Emission LedgerEntryData
571571
*/
572-
export function getEmissionEntryTokenType(ledger_entry_data: xdr.LedgerEntryData | string): number {
572+
export function getEmissionIndex(ledger_entry_data: xdr.LedgerEntryData | string): number {
573573
if (typeof ledger_entry_data == 'string') {
574574
ledger_entry_data = xdr.LedgerEntryData.fromXDR(ledger_entry_data, 'base64');
575575
}
576576
const ledgerEntryKey = ledger_entry_data.contractData()?.key()?.vec()?.at(1)?.u32();
577577
if (ledgerEntryKey == undefined) {
578578
throw new Error('Unable to parse emission entry token type');
579579
}
580-
return ledgerEntryKey % 2;
580+
return ledgerEntryKey;
581+
}
582+
583+
export function getReserveId(ledger_entry_data: xdr.LedgerEntryData | string): string {
584+
if (typeof ledger_entry_data == 'string') {
585+
ledger_entry_data = xdr.LedgerEntryData.fromXDR(ledger_entry_data, 'base64');
586+
}
587+
const ledgerEntryKey = Address.fromScAddress(
588+
ledger_entry_data.contractData()?.key()?.vec()?.at(1)?.address()
589+
).toString();
590+
if (ledgerEntryKey == undefined) {
591+
throw new Error('Unable to parse asset id from ledger entry data');
592+
}
593+
return ledgerEntryKey;
581594
}

0 commit comments

Comments
 (0)