Skip to content

Commit

Permalink
feat: adapt btc-assets-api#154, add total_satoshi/available_satoshi i…
Browse files Browse the repository at this point in the history
…n BtcAssetsApi.getBtcBalance() and add only_non_rgbpp_utxos in BtcAssetsApi.getBtcUtxos() params
  • Loading branch information
ShookLyngs committed Jun 3, 2024
1 parent ebb098b commit 4f05b1b
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 88 deletions.
11 changes: 11 additions & 0 deletions .changeset/tender-hornets-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@rgbpp-sdk/service": minor
"@rgbpp-sdk/btc": minor
---

Adapt btc-assets-api#154, adding new props and return values to the /balance and /unspent APIs

- Add `available_satoshi` and `total_satoshi` to the BtcAssetsApi.getBtcBalance() API
- Add `only_non_rgbpp_utxos` to the props of the BtcAssetsApi.getBtcUtxos() API
- Remove `service.getRgbppAssetsByBtcUtxo()` lines from the DataCollector.collectSatoshi()
- Remove `hasRgbppAssets` related variables/function from the DataCache
29 changes: 0 additions & 29 deletions packages/btc/src/query/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { Utxo } from '../transaction/utxo';

export class DataCache {
private utxos: Map<string, Utxo[]>; // Map<Key, Utxo[]>
private hasRgbppAssets: Map<string, boolean>; // Map<`{txid}:{vout}`, HasAssets>

constructor() {
this.utxos = new Map();
this.hasRgbppAssets = new Map();
}

setUtxos(key: string, utxos: Utxo[]) {
Expand All @@ -32,31 +30,4 @@ export class DataCache {

return utxos;
}

setHasRgbppAssets(key: string, hasAssets: boolean) {
this.hasRgbppAssets.set(key, hasAssets);
}
getHasRgbppAssets(key: string): boolean | undefined {
return this.hasRgbppAssets.get(key);
}
cleanHasRgbppAssets(key: string) {
if (this.hasRgbppAssets.has(key)) {
this.hasRgbppAssets.delete(key);
}
}
async optionalCacheHasRgbppAssets(props: {
key?: string;
getter: () => Promise<boolean> | boolean;
}): Promise<boolean> {
if (props.key && this.hasRgbppAssets.has(props.key)) {
return this.getHasRgbppAssets(props.key) as boolean;
}

const hasRgbppAssets = await props.getter();
if (props.key) {
this.setHasRgbppAssets(props.key, hasRgbppAssets);
}

return hasRgbppAssets;
}
}
44 changes: 12 additions & 32 deletions packages/btc/src/query/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,32 +116,24 @@ export class DataSource {
satoshi: number;
exceedSatoshi: number;
}> {
const {
address,
targetAmount,
minUtxoSatoshi,
onlyConfirmedUtxos,
onlyNonRgbppUtxos,
noAssetsApiCache,
internalCacheKey,
allowInsufficient = false,
excludeUtxos = [],
} = props;
const allowInsufficient = props.allowInsufficient ?? false;
const excludeUtxos = props.excludeUtxos ?? [];

const utxos = await this.cache.optionalCacheUtxos({
key: internalCacheKey,
key: props.internalCacheKey,
getter: () =>
this.getUtxos(address, {
only_confirmed: onlyConfirmedUtxos,
min_satoshi: minUtxoSatoshi,
no_cache: noAssetsApiCache,
this.getUtxos(props.address, {
only_non_rgbpp_utxos: props.onlyNonRgbppUtxos,
only_confirmed: props.onlyConfirmedUtxos,
min_satoshi: props.minUtxoSatoshi,
no_cache: props.noAssetsApiCache,
}),
});

const collected = [];
let collectedAmount = 0;
for (const utxo of utxos) {
if (collectedAmount >= targetAmount) {
if (collectedAmount >= props.targetAmount) {
break;
}
if (excludeUtxos.length > 0) {
Expand All @@ -152,33 +144,21 @@ export class DataSource {
continue;
}
}
if (onlyNonRgbppUtxos) {
const hasRgbppAssets = await this.cache.optionalCacheHasRgbppAssets({
key: `${utxo.txid}:${utxo.vout}`,
getter: async () => {
const ckbRgbppAssets = await this.service.getRgbppAssetsByBtcUtxo(utxo.txid, utxo.vout);
return Array.isArray(ckbRgbppAssets) && ckbRgbppAssets.length > 0;
},
});
if (hasRgbppAssets) {
continue;
}
}
collected.push(utxo);
collectedAmount += utxo.value;
}

if (!allowInsufficient && collectedAmount < targetAmount) {
if (!allowInsufficient && collectedAmount < props.targetAmount) {
throw TxBuildError.withComment(
ErrorCodes.INSUFFICIENT_UTXO,
`expected: ${targetAmount}, actual: ${collectedAmount}`,
`expected: ${props.targetAmount}, actual: ${collectedAmount}`,
);
}

return {
utxos: collected,
satoshi: collectedAmount,
exceedSatoshi: collectedAmount - targetAmount,
exceedSatoshi: collectedAmount - props.targetAmount,
};
}

Expand Down
54 changes: 29 additions & 25 deletions packages/btc/tests/DataSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,39 @@ describe('DataSource', { retry: 3 }, () => {
source.getUtxo('70b250e2a3cc7a33b47f7a4e94e41e1ee2501ce73b393d824db1dd4c872c5348', 0),
).rejects.toHaveProperty('code', ErrorCodes.UNSPENDABLE_OUTPUT);
});
it('Get UTXO[] via collectSatoshi()', async () => {
const address = 'tb1qnxdtut9vpycmpnjpp77rmx33mfxsr86dl3ce6a';
const nonRgbppSatoshi = 2546;
const totalSatoshi = 3092;
const nonRgbppUtxo = 2;
describe('collectSatoshi()', () => {
const address = 'tb1qn5kgn70tpwsw4nuxrch8l7qa9nqn4fahxgzjg6';
const totalSatoshi = 546 + 2000 + 1500;
const nonRgbppSatoshi = 1500;
const nonRgbppUtxo = 1;
const totalUtxo = 3;

const c1 = await source.collectSatoshi({
address,
targetAmount: totalSatoshi,
onlyNonRgbppUtxos: false,
});
expect(c1.utxos).toHaveLength(totalUtxo);
expect(c1.satoshi).toEqual(totalSatoshi);

const c2 = await source.collectSatoshi({
address,
targetAmount: nonRgbppSatoshi,
onlyNonRgbppUtxos: true,
});
expect(c2.utxos).toHaveLength(nonRgbppUtxo);
expect(c2.satoshi).toEqual(nonRgbppSatoshi);

await expect(() =>
source.collectSatoshi({
it('onlyNonRgbppUtxos = false', async () => {
const c = await source.collectSatoshi({
address,
targetAmount: totalSatoshi,
onlyNonRgbppUtxos: false,
});
expect(c.utxos).toHaveLength(totalUtxo);
expect(c.satoshi).toEqual(totalSatoshi);
});
it('onlyNonRgbppUtxos = true', async () => {
const c = await source.collectSatoshi({
address,
targetAmount: nonRgbppSatoshi,
onlyNonRgbppUtxos: true,
}),
).rejects.toThrowError();
});
expect(c.utxos).toHaveLength(nonRgbppUtxo);
expect(c.satoshi).toEqual(nonRgbppSatoshi);
});
it('Try onlyNonRgbppUtxos = true and targetAmount = totalSatoshi', async () => {
await expect(() =>
source.collectSatoshi({
address,
targetAmount: totalSatoshi,
onlyNonRgbppUtxos: true,
}),
).rejects.toThrowError();
});
});
});
5 changes: 5 additions & 0 deletions packages/service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,18 @@ interface BtcApiBalanceParams {

interface BtcApiBalance {
address: string;
// @deprecated Use available_satoshi instead
satoshi: number;
total_satoshi: number;
available_satoshi: number;
pending_satoshi: number;
rgbpp_satoshi: number;
dust_satoshi: number;
utxo_count: number;
}

interface BtcApiUtxoParams {
only_non_rgbpp_utxos?: boolean;
only_confirmed?: boolean;
min_satoshi?: number;
no_cache?: boolean;
Expand Down
5 changes: 5 additions & 0 deletions packages/service/src/types/btc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,18 @@ export interface BtcApiBalanceParams {
}
export interface BtcApiBalance {
address: string;
// @deprecated Use available_satoshi instead
satoshi: number;
total_satoshi: number;
available_satoshi: number;
pending_satoshi: number;
rgbpp_satoshi: number;
dust_satoshi: number;
utxo_count: number;
}

export interface BtcApiUtxoParams {
only_non_rgbpp_utxos?: boolean;
only_confirmed?: boolean;
min_satoshi?: number;
no_cache?: boolean;
Expand Down
5 changes: 3 additions & 2 deletions packages/service/tests/Service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ describe(
const res = await service.getBtcBalance(btcAddress);
expect(res.address).toEqual(btcAddress);
expect(res.satoshi).toBeTypeOf('number');
expect(res.total_satoshi).toBeTypeOf('number');
expect(res.available_satoshi).toBeTypeOf('number');
expect(res.pending_satoshi).toBeTypeOf('number');
expect(res.dust_satoshi).toBeTypeOf('number');
expect(res.utxo_count).toBeTypeOf('number');
Expand All @@ -89,8 +91,7 @@ describe(
min_satoshi: originalBalance.satoshi + 1,
});

expect(filteredBalance.satoshi).toEqual(0);
// expect(filteredBalance.dust_satoshi).toEqual(originalBalance.satoshi + originalBalance.dust_satoshi);
expect(filteredBalance.available_satoshi).toEqual(0);
});
it('getBtcBalance() with no_cache', async () => {
const res = await service.getBtcBalance(btcAddress, {
Expand Down

0 comments on commit 4f05b1b

Please sign in to comment.