Skip to content

Commit 6f6e1a8

Browse files
committed
rosen-version: Merge branch '310-feat-add-collateral-box-package' into 'dev'
add collateral box package Closes #310 See merge request ergo/rosen-bridge/utils!352
2 parents 4998b45 + f492cdb commit 6f6e1a8

14 files changed

Lines changed: 607 additions & 0 deletions

.changeset/shaggy-spies-beam.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# @rosen-bridge/collateral
2+
3+
## 0.1.0
4+
5+
### Minor Changes
6+
7+
- Initial new package for collateral box
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# @rosen-bridge/collateral
2+
3+
## Table of contents
4+
5+
- [Introduction](#introduction)
6+
- [Installation](#installation)
7+
8+
## Introduction
9+
10+
provides a structured and reusable way to work with collateral boxes.
11+
The CollateralBox class is responsible for reading and exposing all collateral
12+
box properties in a clear and consistent manner. The CollateralBoxBuilder class is a builder
13+
that constructs the final collateral box based on those properties.
14+
15+
## Installation
16+
17+
npm:
18+
19+
```sh
20+
npm i @rosen-bridge/collateral
21+
```
22+
23+
yarn:
24+
25+
```sh
26+
yarn add @rosen-bridge/collateral
27+
```
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { AbstractLogger, DummyLogger } from '@rosen-bridge/abstract-logger';
2+
import * as ergoLib from 'ergo-lib-wasm-nodejs';
3+
import { CollateralBoxBuilder } from './collateralBuilder';
4+
5+
export class CollateralBox {
6+
private readonly wid: Uint8Array;
7+
private readonly rsnAmount: bigint;
8+
9+
constructor(
10+
protected box: ergoLib.ErgoBox,
11+
private logger: AbstractLogger = new DummyLogger(),
12+
) {
13+
try {
14+
const widReg = this.box.register_value(4);
15+
if (!widReg) {
16+
throw new Error('missing R4 register');
17+
}
18+
this.wid = widReg.to_byte_array();
19+
20+
const rsnAmount = this.box.register_value(5);
21+
if (!rsnAmount) {
22+
throw new Error('missing R5 register');
23+
}
24+
this.rsnAmount = BigInt(rsnAmount.to_i64().to_str());
25+
26+
if (this.box.tokens().len() < 1) {
27+
throw new Error('expected at least 1 token (X-AWC NFT)');
28+
}
29+
30+
this.logger.debug(
31+
`CollateralBox wid=[${Buffer.from(this.wid).toString(
32+
'hex',
33+
)}] Rsn=[${this.rsnAmount}]`,
34+
);
35+
} catch (e) {
36+
throw Error(`Failed to create CollateralBox: ${e}`);
37+
}
38+
}
39+
/**
40+
* Creates an instance of CollateralBoxBuilder using current instance's
41+
* properties
42+
*
43+
* @return {CollateralBoxBuilder}
44+
*/
45+
toBuilder = (): CollateralBoxBuilder => {
46+
return new CollateralBoxBuilder(
47+
this.getErgoTree(),
48+
this.getAwcNftId(),
49+
this.getRsnAmount(),
50+
this.getRsnId(),
51+
this.getCollateralRsnAmount(),
52+
this.wid,
53+
this.getCollateralValue(),
54+
this.logger,
55+
);
56+
};
57+
58+
/**
59+
* @returns ErgoTree of the box
60+
*/
61+
getErgoTree = (): string => {
62+
return this.box.ergo_tree().to_base16_bytes();
63+
};
64+
/**
65+
* @returns Owner WID extracted from R4
66+
*/
67+
getOwnerWid = (): Uint8Array => {
68+
return this.wid;
69+
};
70+
71+
/**
72+
* @returns RSN amount stored in R5
73+
*/
74+
getRsnAmount = (): bigint => {
75+
return this.rsnAmount;
76+
};
77+
78+
/**
79+
* @returns collateral box value
80+
*/
81+
getCollateralValue = (): string => {
82+
return this.box.value().as_i64().to_str();
83+
};
84+
/**
85+
* @returns X-AWC NFT token ID
86+
*/
87+
getAwcNftId = (): string => {
88+
return this.box.tokens().get(0).id().to_str();
89+
};
90+
91+
/**
92+
* getRsnId() Returns the RSN token ID if present in the box; otherwise returns `undefined`.
93+
*
94+
* @returns {string} RSN token ID | undefined
95+
*/
96+
getRsnId = (): string | undefined => {
97+
if (this.box.tokens().len() < 2) return undefined;
98+
return this.box.tokens().get(1).id().to_str();
99+
};
100+
/**
101+
* @returns Amount of RSN tokens locked in the box as collateral
102+
*/
103+
getCollateralRsnAmount = (): bigint => {
104+
if (this.box.tokens().len() < 2) return 0n;
105+
return BigInt(this.box.tokens().get(1).amount().as_i64().to_str());
106+
};
107+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { AbstractLogger, DummyLogger } from '@rosen-bridge/abstract-logger';
2+
import * as ergoLib from 'ergo-lib-wasm-nodejs';
3+
4+
export class CollateralBoxBuilder {
5+
private height?: number;
6+
7+
constructor(
8+
private collateralErgoTree: string,
9+
private awcNftId: string,
10+
private rsnAmount: bigint,
11+
private rsnTokenId: string | undefined,
12+
private collateralRsn: bigint = 0n,
13+
private wid: Uint8Array,
14+
private value: string,
15+
private logger: AbstractLogger = new DummyLogger(),
16+
) {}
17+
18+
/**
19+
* Increases the locked RSN amount allocated to this watcher.
20+
* @param amount RSN amount to lock
21+
* @returns Updated CollateralBoxBuilder instance
22+
*/
23+
lockRsn = (amount: bigint): CollateralBoxBuilder => {
24+
if (amount <= 0n) throw new Error('amount must be positive');
25+
26+
this.rsnAmount += amount;
27+
this.logger.debug(
28+
`locked RSN increased by ${amount}, total=${this.rsnAmount}`,
29+
);
30+
return this;
31+
};
32+
33+
/**
34+
* Decreases the locked RSN amount allocated to this watcher.
35+
* @param amount RSN amount to unlock
36+
* @returns Updated CollateralBoxBuilder instance
37+
*/
38+
unlockRsn = (amount: bigint): CollateralBoxBuilder => {
39+
if (amount <= 0n) throw new Error('amount must be positive');
40+
41+
if (this.rsnAmount < amount)
42+
throw new Error(
43+
`locked RSN [${this.rsnAmount}] < unlock amount [${amount}]`,
44+
);
45+
46+
this.rsnAmount -= amount;
47+
this.logger.debug(
48+
`locked RSN decreased by ${amount}, total=${this.rsnAmount}`,
49+
);
50+
return this;
51+
};
52+
53+
/**
54+
* Creates a CollateralBox from the properties of this CollateralBoxBuilder instance
55+
*
56+
* @return {ergoLib.ErgoBoxCandidate}
57+
*/
58+
build = (): ergoLib.ErgoBoxCandidate => {
59+
if (this.value === undefined || this.height === undefined) {
60+
throw new Error(`value and height must be set before build`);
61+
}
62+
63+
const boxBuilder = new ergoLib.ErgoBoxCandidateBuilder(
64+
ergoLib.BoxValue.from_i64(ergoLib.I64.from_str(this.value)),
65+
ergoLib.Contract.new(
66+
ergoLib.ErgoTree.from_base16_bytes(this.collateralErgoTree),
67+
),
68+
this.height,
69+
);
70+
71+
boxBuilder.set_register_value(
72+
4,
73+
ergoLib.Constant.from_byte_array(this.wid),
74+
);
75+
76+
boxBuilder.set_register_value(
77+
5,
78+
ergoLib.Constant.from_i64(
79+
ergoLib.I64.from_str(this.rsnAmount.toString()),
80+
),
81+
);
82+
83+
boxBuilder.add_token(
84+
ergoLib.TokenId.from_str(this.awcNftId),
85+
ergoLib.TokenAmount.from_i64(ergoLib.I64.from_str('1')),
86+
);
87+
88+
if (this.rsnTokenId) {
89+
boxBuilder.add_token(
90+
ergoLib.TokenId.from_str(this.rsnTokenId),
91+
ergoLib.TokenAmount.from_i64(
92+
ergoLib.I64.from_str(this.collateralRsn.toString()),
93+
),
94+
);
95+
}
96+
97+
return boxBuilder.build();
98+
};
99+
100+
/** Sets the height of the box
101+
* @param {number} height
102+
*/
103+
setHeight = (height: number) => {
104+
if (height < 1) throw new Error('height must be positive');
105+
this.height = height;
106+
};
107+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { CollateralBox } from './collateral';
2+
export { CollateralBoxBuilder } from './collateralBuilder';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@rosen-bridge/collateral",
3+
"version": "0.1.0",
4+
"description": "a package to manage collateral box",
5+
"repository": {
6+
"type": "git",
7+
"url": "git+https://github.com/rosen-bridge/utils.git"
8+
},
9+
"license": "MIT",
10+
"author": "Rosen Team",
11+
"type": "module",
12+
"main": "dist/index.js",
13+
"types": "dist/index.d.ts",
14+
"files": [
15+
"dist",
16+
"CHANGELOG.md"
17+
],
18+
"scripts": {
19+
"build": "tsc --build tsconfig.build.json",
20+
"coverage": "npm run test -- --run --coverage",
21+
"lint": "eslint --fix . && npm run prettify",
22+
"lint:check": "eslint . && npm run prettify:check",
23+
"prettify": "prettier --write . --ignore-path ../../../.gitignore",
24+
"prettify:check": "prettier --check . --ignore-path ../../../.gitignore",
25+
"release": "npm run clean && npm run build && npm publish --access public",
26+
"test": "NODE_OPTIONS='--import tsx' vitest",
27+
"type-check": "tsc --noEmit"
28+
},
29+
"dependencies": {
30+
"@rosen-bridge/abstract-logger": "^4.0.0",
31+
"ergo-lib-wasm-nodejs": "^0.24.1"
32+
},
33+
"devDependencies": {
34+
"@rosen-bridge/json-bigint": "^1.1.0",
35+
"@vitest/coverage-istanbul": "^3.1.4",
36+
"tsx": "^4.20.5",
37+
"vitest": "^3.1.4"
38+
},
39+
"engines": {
40+
"node": ">=22.18.0",
41+
"npm": "11.6.2"
42+
}
43+
}

0 commit comments

Comments
 (0)