Skip to content

Commit 7205d0b

Browse files
authored
Merge pull request #205 from ckb-cell/develop (v2.4.1)
Merge develop to main branch (v2.4.1)
2 parents 2794960 + e97816b commit 7205d0b

File tree

18 files changed

+377
-78
lines changed

18 files changed

+377
-78
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "btc-assets-api",
3-
"version": "2.4.0",
3+
"version": "2.4.1",
44
"title": "Bitcoin/RGB++ Assets API",
55
"description": "",
66
"main": "index.js",
@@ -47,6 +47,7 @@
4747
"@rgbpp-sdk/service": "^0.4.0",
4848
"@sentry/node": "^7.102.1",
4949
"@sentry/profiling-node": "^7.102.1",
50+
"@spore-sdk/core": "^0.2.0-beta.9",
5051
"async-retry": "^1.3.3",
5152
"awilix": "^10.0.1",
5253
"axios": "^1.6.7",

pnpm-lock.yaml

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/constants.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,16 @@ export const JWT_IGNORE_URLS = ['/token', '/docs', '/cron', '/internal', '/healt
2828
export const SWAGGER_PROD_IGNORE_URLS = isAdminMode ? ['/token', '/cron'] : ['/cron'];
2929

3030
export const VERCEL_MAX_DURATION = 300;
31+
32+
// estimate time: 2024-04-03 09:45:17
33+
// ref: https://mempool.space/testnet/block/000000000000000493ba5eebf0602f3e0e5381dd35f763a62ca7ea135343a0d6
34+
export const BTC_TESTNET_SPV_START_BLOCK_HEIGHT = 2584900;
35+
36+
// estimate time: 2024-06-13 18:31:56
37+
// ref: https://mempool.space/signet/block/000000b2af39a66ec81d414b102804d975c5c4527adfd9bd3cabf2b7b4634737
38+
// Signet BTC SPV deployment time: https://pudge.explorer.nervos.org/transaction/0x61efdeddbaa0bb4132c0eb174b3e8002ff5ec430f61ba46f30768d683c516eec
39+
export const BTC_SIGNET_SPV_START_BLOCK_HEIGHT = 199800;
40+
41+
// estimate time: 2024-04-02 06:20:03
42+
// ref: https://mempool.space/block/0000000000000000000077d98a103858c7d7cbc5ba67a4135f348a436bec1748
43+
export const BTC_MAINNET_SPV_START_BLOCK_HEIGHT = 837300;

src/routes/rgbpp/address.ts

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { FastifyPluginCallback, FastifyRequest } from 'fastify';
1+
import { FastifyPluginCallback } from 'fastify';
22
import { Server } from 'http';
33
import validateBitcoinAddress from '../../utils/validators';
44
import { ZodTypeProvider } from 'fastify-type-provider-zod';
55
import { CKBTransaction, Cell, IsomorphicTransaction, Script, XUDTBalance } from './types';
6-
import { blockchain } from '@ckb-lumos/base';
76
import z from 'zod';
87
import { Env } from '../../env';
98
import { buildPreLockArgs, getXudtTypeScript, isScriptEqual, isTypeAssetSupported } from '@rgbpp-sdk/ckb';
@@ -13,6 +12,7 @@ import { UTXO } from '../../services/bitcoin/schema';
1312
import { Transaction as BTCTransaction } from '../bitcoin/types';
1413
import { TransactionWithStatus } from '../../services/ckb';
1514
import { computeScriptHash } from '@ckb-lumos/lumos/utils';
15+
import { filterCellsByTypeScript, getTypeScript } from '../../utils/typescript';
1616

1717
const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodTypeProvider> = (fastify, _, done) => {
1818
const env: Env = fastify.container.resolve('env');
@@ -25,26 +25,6 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
2525
}
2626
});
2727

28-
/**
29-
* Get type script from request query
30-
*/
31-
function getTypeScript(request: FastifyRequest) {
32-
const { type_script } = request.query as { type_script: string | Script };
33-
let typeScript: Script | undefined = undefined;
34-
if (type_script) {
35-
if (typeof type_script === 'string') {
36-
if (type_script.startsWith('0x')) {
37-
typeScript = blockchain.Script.unpack(type_script);
38-
} else {
39-
typeScript = JSON.parse(decodeURIComponent(type_script));
40-
}
41-
} else {
42-
typeScript = type_script;
43-
}
44-
}
45-
return typeScript;
46-
}
47-
4828
/**
4929
* Get UTXOs by btc address
5030
*/
@@ -72,23 +52,6 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
7252
return cells;
7353
}
7454

75-
/**
76-
* Filter cells by type script
77-
*/
78-
function filterCellsByTypeScript(cells: Cell[], typeScript: Script) {
79-
return cells.filter((cell) => {
80-
if (!cell.cellOutput.type) {
81-
return false;
82-
}
83-
// if typeScript.args is empty, only compare codeHash and hashType
84-
if (!typeScript.args || typeScript.args === '0x') {
85-
const script = { ...cell.cellOutput.type, args: '' };
86-
return isScriptEqual(script, typeScript);
87-
}
88-
return isScriptEqual(cell.cellOutput.type, typeScript);
89-
});
90-
}
91-
9255
fastify.get(
9356
'/:btc_address/assets',
9457
{
@@ -125,7 +88,7 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
12588
const { no_cache } = request.query;
12689
const utxos = await getUxtos(btc_address, no_cache);
12790
const cells = await getRgbppAssetsCells(btc_address, utxos, no_cache);
128-
const typeScript = getTypeScript(request);
91+
const typeScript = getTypeScript(request.query.type_script);
12992
const assetCells = typeScript ? filterCellsByTypeScript(cells, typeScript) : cells;
13093
return assetCells.map((cell) => {
13194
const typeHash = cell.cellOutput.type ? computeScriptHash(cell.cellOutput.type) : undefined;
@@ -175,7 +138,7 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
175138
const { btc_address } = request.params;
176139
const { no_cache } = request.query;
177140

178-
const typeScript = getTypeScript(request);
141+
const typeScript = getTypeScript(request.query.type_script);
179142
if (!typeScript || !isTypeAssetSupported(typeScript, env.NETWORK === 'mainnet')) {
180143
throw fastify.httpErrors.badRequest('Unsupported type asset');
181144
}
@@ -189,6 +152,7 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
189152

190153
let cells = await getRgbppAssetsCells(btc_address, utxos, no_cache);
191154
cells = typeScript ? filterCellsByTypeScript(cells, typeScript) : cells;
155+
192156
const availableXudtBalances = await fastify.rgbppCollector.getRgbppBalanceByCells(cells);
193157
Object.keys(availableXudtBalances).forEach((key) => {
194158
const { amount, ...xudtInfo } = availableXudtBalances[key];
@@ -340,7 +304,7 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
340304
async (request) => {
341305
const { btc_address } = request.params;
342306
const { rgbpp_only, after_btc_txid } = request.query;
343-
const typeScript = getTypeScript(request);
307+
const typeScript = getTypeScript(request.query.type_script);
344308

345309
const btcTxs = await fastify.bitcoin.getAddressTxs({
346310
address: btc_address,

src/routes/rgbpp/assets.ts

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import { FastifyPluginCallback } from 'fastify';
22
import { ZodTypeProvider } from 'fastify-type-provider-zod';
33
import { Server } from 'http';
44
import z from 'zod';
5-
import { Cell } from './types';
5+
import { Cell, Script, SporeTypeInfo, XUDTTypeInfo } from './types';
66
import { UTXO } from '../../services/bitcoin/schema';
7+
import { getTypeScript } from '../../utils/typescript';
8+
import { Env } from '../../env';
9+
import { IndexerCell, isSporeTypeSupported, isUDTTypeSupported } from '@rgbpp-sdk/ckb';
710
import { computeScriptHash } from '@ckb-lumos/lumos/utils';
11+
import { getSporeConfig, unpackToRawClusterData, unpackToRawSporeData } from '../../utils/spore';
12+
import { SearchKey } from '../../services/rgbpp';
813

914
const assetsRoute: FastifyPluginCallback<Record<never, never>, Server, ZodTypeProvider> = (fastify, _, done) => {
15+
const env: Env = fastify.container.resolve('env');
16+
1017
fastify.get(
1118
'/:btc_txid',
1219
{
@@ -97,6 +104,108 @@ const assetsRoute: FastifyPluginCallback<Record<never, never>, Server, ZodTypePr
97104
},
98105
);
99106

107+
fastify.get(
108+
'/type',
109+
{
110+
schema: {
111+
description: 'Get RGB++ assets type info by typescript',
112+
tags: ['RGB++@Unstable'],
113+
querystring: z.object({
114+
type_script: Script.or(z.string())
115+
.optional()
116+
.describe(
117+
`
118+
type script to filter cells
119+
120+
two ways to provide:
121+
- as a object: 'encodeURIComponent(JSON.stringify({"codeHash":"0x...", "args":"0x...", "hashType":"type"}))'
122+
- as a hex string: '0x...' (You can pack by @ckb-lumos/codec blockchain.Script.pack({ "codeHash": "0x...", ... }))
123+
`,
124+
),
125+
}),
126+
response: {
127+
200: z
128+
.union([
129+
z
130+
.object({
131+
type: z.literal('xudt'),
132+
})
133+
.merge(XUDTTypeInfo),
134+
z
135+
.object({
136+
type: z.literal('spore'),
137+
})
138+
.merge(SporeTypeInfo),
139+
])
140+
.nullable(),
141+
},
142+
},
143+
},
144+
async (request) => {
145+
const isMainnet = env.NETWORK === 'mainnet';
146+
const typeScript = getTypeScript(request.query.type_script);
147+
if (!typeScript) {
148+
return null;
149+
}
150+
if (isUDTTypeSupported(typeScript, isMainnet)) {
151+
const infoCell = await fastify.ckb.getInfoCellData(typeScript);
152+
const typeHash = computeScriptHash(typeScript);
153+
if (!infoCell) {
154+
return null;
155+
}
156+
return {
157+
type: 'xudt' as const,
158+
type_hash: typeHash,
159+
type_script: typeScript,
160+
...infoCell,
161+
};
162+
}
163+
if (isSporeTypeSupported(typeScript, isMainnet)) {
164+
const searchKey: SearchKey = {
165+
script: typeScript,
166+
scriptType: 'type',
167+
withData: true,
168+
};
169+
const result = await fastify.ckb.rpc.getCells(searchKey, 'desc', '0x1');
170+
const [sporeCell] = result.objects;
171+
const sporeData = unpackToRawSporeData(sporeCell.outputData!);
172+
const sporeInfo: SporeTypeInfo = {
173+
contentType: sporeData.contentType,
174+
};
175+
if (sporeData.clusterId) {
176+
const sporeConfig = getSporeConfig(isMainnet);
177+
const batchRequest = fastify.ckb.rpc.createBatchRequest(
178+
sporeConfig.scripts.Cluster.versions.map((version) => {
179+
const clusterScript = {
180+
...version.script,
181+
args: sporeData.clusterId!,
182+
};
183+
const searchKey: SearchKey = {
184+
script: clusterScript,
185+
scriptType: 'type',
186+
withData: true,
187+
};
188+
return ['getCells', searchKey, 'desc', '0x1'];
189+
}),
190+
);
191+
const cells = await batchRequest.exec();
192+
const [cell] = cells.map(({ objects }: { objects: IndexerCell[] }) => objects).flat();
193+
const clusterData = unpackToRawClusterData(cell.outputData!);
194+
sporeInfo.cluster = {
195+
id: sporeData.clusterId,
196+
name: clusterData.name,
197+
description: clusterData.description,
198+
};
199+
}
200+
return {
201+
type: 'spore' as const,
202+
...sporeInfo,
203+
};
204+
}
205+
return null;
206+
},
207+
);
208+
100209
done();
101210
};
102211

src/routes/rgbpp/types.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,34 @@ export const CKBVirtualResult = z.object({
8383
});
8484
export type CKBVirtualResult = z.infer<typeof CKBVirtualResult>;
8585

86-
export const XUDTBalance = z.object({
86+
export const XUDTTypeInfo = z.object({
87+
symbol: z.string(),
8788
name: z.string(),
8889
decimal: z.number(),
89-
symbol: z.string(),
90-
total_amount: z.string(),
91-
available_amount: z.string(),
92-
pending_amount: z.string(),
9390
type_hash: z.string(),
9491
type_script: Script,
9592
});
93+
export type XUDTTypeInfo = z.infer<typeof XUDTTypeInfo>;
94+
95+
export const SporeTypeInfo = z.object({
96+
contentType: z.string(),
97+
cluster: z
98+
.object({
99+
id: z.string(),
100+
name: z.string(),
101+
description: z.string(),
102+
})
103+
.optional(),
104+
});
105+
export type SporeTypeInfo = z.infer<typeof SporeTypeInfo>;
106+
107+
export const XUDTBalance = XUDTTypeInfo.merge(
108+
z.object({
109+
total_amount: z.string(),
110+
available_amount: z.string(),
111+
pending_amount: z.string(),
112+
}),
113+
);
96114
export type XUDTBalance = z.infer<typeof XUDTBalance>;
97115

98116
export const IsomorphicTransaction = z.object({

0 commit comments

Comments
 (0)