Skip to content

Commit 25a1301

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

File tree

4 files changed

+502
-91
lines changed

4 files changed

+502
-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: 176 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,8 @@ 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(client, tx.getOutput(1)!);
795+
const inputBalance = await udt.getInputsBalance(client, completedTx);
793796
expect(changeAmount).toBe(inputBalance - ccc.numFrom(50));
794797

795798
// Verify change output has correct type script
@@ -820,7 +823,7 @@ describe("Udt", () => {
820823
expect(addedCount).toBeGreaterThan(2);
821824

822825
// Should have added at least one cell with capacity
823-
expect(await udt.getInputsBalance(tx, client)).toBeGreaterThan(ccc.Zero);
826+
expect(await udt.getInputsBalance(client, tx)).toBeGreaterThan(ccc.Zero);
824827
});
825828

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

861864
// Should have change output
862865
expect(completedTx.outputs.length).toBe(2);
863-
const changeAmount = ccc.udtBalanceFrom(completedTx.outputsData[1]);
866+
const changeAmount = await udt.balanceFrom(
867+
client,
868+
completedTx.getOutput(1)!,
869+
);
864870
expect(changeAmount).toBe(
865-
(await udt.getInputsBalance(completedTx, client)) - ccc.numFrom(50),
871+
(await udt.getInputsBalance(client, tx)) - ccc.numFrom(50),
866872
); // 100 input - 50 output = 50 change
867873

868874
completeInputsByBalanceSpy.mockRestore();
@@ -885,8 +891,11 @@ describe("Udt", () => {
885891
expect(completedTx.inputs.length).toBeGreaterThan(0);
886892

887893
// 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);
894+
const changeAmount = await udt.balanceFrom(
895+
client,
896+
completedTx.getOutput(0)!,
897+
);
898+
const inputBalance = await udt.getInputsBalance(client, completedTx);
890899

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

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

0 commit comments

Comments
 (0)