Skip to content

Commit 9886528

Browse files
committed
feat(udt): add udt info querying methods
1 parent 71659a2 commit 9886528

File tree

4 files changed

+505
-91
lines changed

4 files changed

+505
-91
lines changed

.changeset/tired-ghosts-greet.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@ckb-ccc/ssri": minor
3+
"@ckb-ccc/udt": minor
4+
"@ckb-ccc/core": patch
5+
---
6+
7+
feat(udt): udt info querying methods
8+

packages/core/src/ckb/transaction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,7 @@ export class WitnessArgs extends mol.Entity.Base<
10861086
/**
10871087
* Convert a bytes to a num.
10881088
*
1089+
* @deprecated Use `Udt.balanceFrom` from `@ckb-ccc/udt` instead
10891090
* @public
10901091
*/
10911092
export function udtBalanceFrom(dataLike: BytesLike): Num {

packages/udt/src/udt/index.test.ts

Lines changed: 179 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ describe("Udt", () => {
102102
expect(tx.inputs.length).toBe(2);
103103

104104
// Verify the inputs are UDT cells
105-
const inputBalance = await udt.getInputsBalance(tx, client);
105+
const inputBalance = await udt.getInputsBalance(client, tx);
106106
expect(inputBalance).toBe(ccc.numFrom(200));
107107
});
108108

@@ -123,7 +123,7 @@ describe("Udt", () => {
123123
expect(addedCount).toBe(1);
124124
expect(tx.inputs.length).toBe(1);
125125

126-
const inputBalance = await udt.getInputsBalance(tx, client);
126+
const inputBalance = await udt.getInputsBalance(client, tx);
127127
expect(inputBalance).toBe(ccc.numFrom(100));
128128
});
129129

@@ -145,7 +145,7 @@ describe("Udt", () => {
145145
expect(addedCount).toBe(2);
146146
expect(tx.inputs.length).toBe(2);
147147

148-
const inputBalance = await udt.getInputsBalance(tx, client);
148+
const inputBalance = await udt.getInputsBalance(client, tx);
149149
expect(inputBalance).toBe(ccc.numFrom(200));
150150
});
151151

@@ -215,10 +215,10 @@ describe("Udt", () => {
215215
expect(addedCount).toBe(3);
216216
expect(tx.inputs.length).toBe(3);
217217

218-
const inputBalance = await udt.getInputsBalance(tx, client);
218+
const inputBalance = await udt.getInputsBalance(client, tx);
219219
expect(inputBalance).toBe(ccc.numFrom(300));
220220

221-
const outputBalance = await udt.getOutputsBalance(tx, client);
221+
const outputBalance = await udt.getOutputsBalance(client, tx);
222222
expect(outputBalance).toBe(ccc.numFrom(250));
223223
});
224224

@@ -245,7 +245,7 @@ describe("Udt", () => {
245245
expect(addedCount).toBe(1);
246246
expect(tx.inputs.length).toBe(2);
247247

248-
const inputBalance = await udt.getInputsBalance(tx, client);
248+
const inputBalance = await udt.getInputsBalance(client, tx);
249249
expect(inputBalance).toBe(ccc.numFrom(200));
250250
});
251251

@@ -266,7 +266,7 @@ describe("Udt", () => {
266266
expect(addedCount).toBe(1);
267267
expect(tx.inputs.length).toBe(1);
268268

269-
const inputBalance = await udt.getInputsBalance(tx, client);
269+
const inputBalance = await udt.getInputsBalance(client, tx);
270270
expect(inputBalance).toBe(ccc.numFrom(100));
271271
});
272272
});
@@ -327,7 +327,7 @@ describe("Udt", () => {
327327
expect(completedTx.inputs.length).toBe(5);
328328

329329
// Verify total UDT balance is 500 (5 cells × 100 UDT each)
330-
const inputBalance = await udt.getInputsBalance(completedTx, client);
330+
const inputBalance = await udt.getInputsBalance(client, completedTx);
331331
expect(inputBalance).toBe(ccc.numFrom(500));
332332

333333
// Verify all cells were added by checking outpoints
@@ -361,15 +361,15 @@ describe("Udt", () => {
361361
expect(completedTx.inputs.length).toBe(5);
362362

363363
// Verify total UDT balance is 500 (all available)
364-
const inputBalance = await udt.getInputsBalance(completedTx, client);
364+
const inputBalance = await udt.getInputsBalance(client, completedTx);
365365
expect(inputBalance).toBe(ccc.numFrom(500));
366366

367367
// Verify output balance is still 350
368-
const outputBalance = await udt.getOutputsBalance(completedTx, client);
368+
const outputBalance = await udt.getOutputsBalance(client, completedTx);
369369
expect(outputBalance).toBe(ccc.numFrom(350));
370370

371371
// Should have 150 UDT excess balance (500 - 350)
372-
const balanceBurned = await udt.getBalanceBurned(completedTx, client);
372+
const balanceBurned = await udt.getBalanceBurned(client, completedTx);
373373
expect(balanceBurned).toBe(ccc.numFrom(150));
374374
});
375375

@@ -394,7 +394,7 @@ describe("Udt", () => {
394394
expect(completedTx.inputs.length).toBe(5); // 2 existing + 3 added
395395

396396
// Verify total UDT balance is still 500 (all 5 cells)
397-
const inputBalance = await udt.getInputsBalance(completedTx, client);
397+
const inputBalance = await udt.getInputsBalance(client, completedTx);
398398
expect(inputBalance).toBe(ccc.numFrom(500));
399399
});
400400

@@ -416,7 +416,7 @@ describe("Udt", () => {
416416
expect(completedTx.inputs.length).toBe(5); // Same as before
417417

418418
// Verify total UDT balance is still 500
419-
const inputBalance = await udt.getInputsBalance(completedTx, client);
419+
const inputBalance = await udt.getInputsBalance(client, completedTx);
420420
expect(inputBalance).toBe(ccc.numFrom(500));
421421
});
422422

@@ -438,7 +438,7 @@ describe("Udt", () => {
438438
expect(completedTx.inputs.length).toBe(5);
439439

440440
// All 500 UDT will be "burned" since no UDT outputs
441-
const balanceBurned = await udt.getBalanceBurned(completedTx, client);
441+
const balanceBurned = await udt.getBalanceBurned(client, completedTx);
442442
expect(balanceBurned).toBe(ccc.numFrom(500));
443443
});
444444

@@ -480,7 +480,7 @@ describe("Udt", () => {
480480
expect(completedTx.inputs.length).toBe(6); // 1 non-UDT + 5 UDT
481481

482482
// Verify only UDT balance is counted
483-
const inputBalance = await udt.getInputsBalance(completedTx, client);
483+
const inputBalance = await udt.getInputsBalance(client, completedTx);
484484
expect(inputBalance).toBe(ccc.numFrom(500));
485485
});
486486

@@ -505,7 +505,7 @@ describe("Udt", () => {
505505
expect(completedTx.inputs.length).toBe(0);
506506

507507
// UDT balance should be 0
508-
const inputBalance = await udt.getInputsBalance(completedTx, client);
508+
const inputBalance = await udt.getInputsBalance(client, completedTx);
509509
expect(inputBalance).toBe(ccc.numFrom(0));
510510
});
511511
});
@@ -536,7 +536,7 @@ describe("Udt", () => {
536536
],
537537
});
538538

539-
const balance = await udt.getInputsBalance(tx, client);
539+
const balance = await udt.getInputsBalance(client, tx);
540540
expect(balance).toBe(ccc.numFrom(300)); // 100 + 200
541541
});
542542

@@ -565,7 +565,7 @@ describe("Udt", () => {
565565
],
566566
});
567567

568-
const balance = await udt.getInputsBalance(tx, client);
568+
const balance = await udt.getInputsBalance(client, tx);
569569
expect(balance).toBe(ccc.numFrom(100)); // Only the UDT cell
570570
});
571571
});
@@ -585,7 +585,7 @@ describe("Udt", () => {
585585
],
586586
});
587587

588-
const balance = await udt.getOutputsBalance(tx, client);
588+
const balance = await udt.getOutputsBalance(client, tx);
589589
expect(balance).toBe(ccc.numFrom(300)); // 100 + 200, ignoring non-UDT output
590590
});
591591

@@ -595,7 +595,7 @@ describe("Udt", () => {
595595
outputsData: ["0x"],
596596
});
597597

598-
const balance = await udt.getOutputsBalance(tx, client);
598+
const balance = await udt.getOutputsBalance(client, tx);
599599
expect(balance).toBe(ccc.numFrom(0));
600600
});
601601
});
@@ -654,7 +654,10 @@ describe("Udt", () => {
654654
expect(completedTx.outputs[1].type?.eq(type)).toBe(true);
655655

656656
// Change should be 50 UDT (200 input - 150 output)
657-
const changeAmount = ccc.udtBalanceFrom(completedTx.outputsData[1]);
657+
const changeAmount = await udt.balanceFrom(
658+
client,
659+
completedTx.getOutput(1)!,
660+
);
658661
expect(changeAmount).toBe(ccc.numFrom(50));
659662
});
660663

@@ -788,8 +791,11 @@ describe("Udt", () => {
788791
expect(completedTx.inputs.length).toBe(2);
789792

790793
// Check that change output has correct UDT balance (should be input - 50)
791-
const changeAmount = ccc.udtBalanceFrom(completedTx.outputsData[1]);
792-
const inputBalance = await udt.getInputsBalance(completedTx, client);
794+
const changeAmount = await udt.balanceFrom(
795+
client,
796+
completedTx.getOutput(1)!,
797+
);
798+
const inputBalance = await udt.getInputsBalance(client, completedTx);
793799
expect(changeAmount).toBe(inputBalance - ccc.numFrom(50));
794800

795801
// Verify change output has correct type script
@@ -820,7 +826,7 @@ describe("Udt", () => {
820826
expect(addedCount).toBeGreaterThan(2);
821827

822828
// Should have added at least one cell with capacity
823-
expect(await udt.getInputsBalance(tx, client)).toBeGreaterThan(ccc.Zero);
829+
expect(await udt.getInputsBalance(client, tx)).toBeGreaterThan(ccc.Zero);
824830
});
825831

826832
it("should handle the two-phase capacity completion in complete method", async () => {
@@ -860,9 +866,12 @@ describe("Udt", () => {
860866

861867
// Should have change output
862868
expect(completedTx.outputs.length).toBe(2);
863-
const changeAmount = ccc.udtBalanceFrom(completedTx.outputsData[1]);
869+
const changeAmount = await udt.balanceFrom(
870+
client,
871+
completedTx.getOutput(1)!,
872+
);
864873
expect(changeAmount).toBe(
865-
(await udt.getInputsBalance(completedTx, client)) - ccc.numFrom(50),
874+
(await udt.getInputsBalance(client, completedTx)) - ccc.numFrom(50),
866875
); // 100 input - 50 output = 50 change
867876

868877
completeInputsByBalanceSpy.mockRestore();
@@ -885,8 +894,11 @@ describe("Udt", () => {
885894
expect(completedTx.inputs.length).toBeGreaterThan(0);
886895

887896
// The first output should now contain the original amount plus any excess from inputs
888-
const changeAmount = ccc.udtBalanceFrom(completedTx.outputsData[0]);
889-
const inputBalance = await udt.getInputsBalance(completedTx, client);
897+
const changeAmount = await udt.balanceFrom(
898+
client,
899+
completedTx.getOutput(0)!,
900+
);
901+
const inputBalance = await udt.getInputsBalance(client, completedTx);
890902

891903
// Change output should have: original amount + excess from inputs
892904
// Since we only have one output, all input balance should go to it
@@ -994,8 +1006,146 @@ describe("Udt", () => {
9941006
expect(resultTx.inputs.length).toBe(3); // 1 non-UDT + 2 UDT
9951007

9961008
// Verify UDT balance is satisfied
997-
const inputBalance = await udt.getInputsBalance(resultTx, client);
1009+
const inputBalance = await udt.getInputsBalance(client, resultTx);
9981010
expect(inputBalance).toBe(ccc.numFrom(200));
9991011
});
10001012
});
1013+
1014+
describe("infoFrom", () => {
1015+
let validUdtCell1: ccc.CellAny;
1016+
let validUdtCell2: ccc.CellAny;
1017+
let nonUdtCell: ccc.CellAny;
1018+
let otherUdtCell: ccc.CellAny;
1019+
1020+
beforeEach(async () => {
1021+
validUdtCell1 = ccc.CellAny.from({
1022+
cellOutput: {
1023+
capacity: ccc.fixedPointFrom(142),
1024+
lock,
1025+
type,
1026+
},
1027+
outputData: ccc.numLeToBytes(100, 16), // 100 UDT
1028+
});
1029+
1030+
validUdtCell2 = ccc.CellAny.from({
1031+
cellOutput: {
1032+
capacity: ccc.fixedPointFrom(200),
1033+
lock,
1034+
type,
1035+
},
1036+
outputData: ccc.numLeToBytes(250, 16), // 250 UDT
1037+
});
1038+
1039+
nonUdtCell = ccc.CellAny.from({
1040+
cellOutput: {
1041+
capacity: ccc.fixedPointFrom(500),
1042+
lock,
1043+
},
1044+
outputData: "0x",
1045+
});
1046+
1047+
const otherUdtScript = await ccc.Script.fromKnownScript(
1048+
client,
1049+
ccc.KnownScript.XUdt,
1050+
"0x" + "1".repeat(64),
1051+
);
1052+
otherUdtCell = ccc.CellAny.from({
1053+
cellOutput: {
1054+
capacity: ccc.fixedPointFrom(142),
1055+
lock,
1056+
type: otherUdtScript,
1057+
},
1058+
outputData: ccc.numLeToBytes(1000, 16), // 1000 other UDT
1059+
});
1060+
});
1061+
1062+
it("should return zero for an empty list of cells", async () => {
1063+
const info = await udt.infoFrom(client);
1064+
expect(info.balance).toBe(ccc.Zero);
1065+
expect(info.capacity).toBe(ccc.Zero);
1066+
expect(info.count).toBe(0);
1067+
});
1068+
1069+
it("should correctly calculate info for a list of valid UDT cells", async () => {
1070+
const info = await udt.infoFrom(client, validUdtCell1, validUdtCell2);
1071+
expect(info.balance).toBe(ccc.numFrom(350)); // 100 + 250
1072+
expect(info.capacity).toBe(ccc.fixedPointFrom(342)); // 142 + 200
1073+
expect(info.count).toBe(2);
1074+
});
1075+
1076+
it("should ignore non-UDT cells and cells of other UDTs", async () => {
1077+
const info = await udt.infoFrom(
1078+
client,
1079+
validUdtCell1,
1080+
nonUdtCell,
1081+
otherUdtCell,
1082+
);
1083+
expect(info.balance).toBe(ccc.numFrom(100));
1084+
expect(info.capacity).toBe(ccc.fixedPointFrom(142));
1085+
expect(info.count).toBe(1);
1086+
});
1087+
1088+
it("should handle nested arrays of cells", async () => {
1089+
const info = await udt.infoFrom(client, validUdtCell1, [
1090+
validUdtCell2,
1091+
nonUdtCell,
1092+
]);
1093+
expect(info.balance).toBe(ccc.numFrom(350));
1094+
expect(info.capacity).toBe(ccc.fixedPointFrom(342));
1095+
expect(info.count).toBe(2);
1096+
});
1097+
});
1098+
1099+
describe("calculateInfo", () => {
1100+
let mockUdtCells: ccc.Cell[];
1101+
1102+
beforeEach(() => {
1103+
mockUdtCells = [
1104+
ccc.Cell.from({
1105+
outPoint: { txHash: "0x" + "a".repeat(64), index: 0 },
1106+
cellOutput: { capacity: ccc.fixedPointFrom(142), lock, type },
1107+
outputData: ccc.numLeToBytes(100, 16), // 100 UDT
1108+
}),
1109+
ccc.Cell.from({
1110+
outPoint: { txHash: "0x" + "b".repeat(64), index: 0 },
1111+
cellOutput: { capacity: ccc.fixedPointFrom(200), lock, type },
1112+
outputData: ccc.numLeToBytes(250, 16), // 250 UDT
1113+
}),
1114+
];
1115+
});
1116+
1117+
it("should calculate info from local source", async () => {
1118+
const findCellsSpy = vi
1119+
.spyOn(signer, "findCells")
1120+
.mockImplementation(async function* () {
1121+
for (const cell of mockUdtCells) {
1122+
yield cell;
1123+
}
1124+
});
1125+
1126+
const info = await udt.calculateInfo(signer, { source: "local" });
1127+
1128+
expect(info.balance).toBe(ccc.numFrom(350));
1129+
expect(info.capacity).toBe(ccc.fixedPointFrom(342));
1130+
expect(info.count).toBe(2);
1131+
expect(findCellsSpy).toHaveBeenCalledWith(udt.filter);
1132+
});
1133+
1134+
it("should calculate info from chain source", async () => {
1135+
const findCellsOnChainSpy = vi
1136+
.spyOn(signer, "findCellsOnChain")
1137+
.mockImplementation(async function* () {
1138+
for (const cell of mockUdtCells) {
1139+
yield cell;
1140+
}
1141+
});
1142+
1143+
const info = await udt.calculateInfo(signer, { source: "chain" });
1144+
1145+
expect(info.balance).toBe(ccc.numFrom(350));
1146+
expect(info.capacity).toBe(ccc.fixedPointFrom(342));
1147+
expect(info.count).toBe(2);
1148+
expect(findCellsOnChainSpy).toHaveBeenCalledWith(udt.filter);
1149+
});
1150+
});
10011151
});

0 commit comments

Comments
 (0)