Skip to content

Commit be5ae4f

Browse files
committed
feat(lockup): add linearTimelock shape inference
1 parent 9f7cbda commit be5ae4f

File tree

4 files changed

+44
-10
lines changed

4 files changed

+44
-10
lines changed

envio/lockup/store/entity-stream.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function createLinear(
4141

4242
const cliff = addCliff(baseStream, params);
4343
const initial = addInitial(params);
44-
const shape = addLinearShape(baseStream, cliff.cliff ?? false);
44+
const shape = addLinearShape(baseStream, cliff.cliff ?? false, cliff.cliffTime, params.endTime);
4545

4646
const stream: Entity.Stream = {
4747
...baseStream,
@@ -217,11 +217,16 @@ function addInitial(
217217
* Older versions of Lockup did not have a shape field, but it can be inferred.
218218
* @see https://github.com/sablier-labs/interfaces/blob/30fffc0/packages/constants/src/stream/shape.ts#L12
219219
*/
220-
function addLinearShape(stream: Entity.Stream, cliff: boolean): ShapeResult {
220+
function addLinearShape(
221+
stream: Entity.Stream,
222+
cliff: boolean,
223+
cliffTime: bigint | undefined,
224+
endTime: bigint
225+
): ShapeResult {
221226
if (stream.shape) {
222227
return { shape: stream.shape, shapeSource: "Event" };
223228
}
224-
return { shape: inferLinearShape(cliff), shapeSource: "Inferred" };
229+
return { shape: inferLinearShape(cliff, cliffTime, endTime), shapeSource: "Inferred" };
225230
}
226231

227232
function addDynamicShape(stream: Entity.Stream, segments: Segment[]): ShapeResult {

envio/lockup/store/shape-inference.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,21 @@ export function normalizeEventShape(shape: string): LockupShape | undefined {
6363

6464
/**
6565
* Infer the shape of a linear stream based on whether it has a cliff.
66+
* Detects linearTimelock when cliff is followed by immediate unlock (≤ 1 second).
6667
*/
67-
export function inferLinearShape(cliff: boolean): LockupShape {
68-
return cliff ? Shape.Lockup.Cliff : Shape.Lockup.Linear;
68+
export function inferLinearShape(
69+
cliff: boolean,
70+
cliffTime: bigint | undefined,
71+
endTime: bigint
72+
): LockupShape {
73+
if (!cliff) {
74+
return Shape.Lockup.Linear;
75+
}
76+
// Detect linearTimelock: cliff followed by immediate unlock (≤ 1 second)
77+
if (cliffTime !== undefined && endTime - cliffTime <= TIMELOCK_MAX_DURATION) {
78+
return Shape.Lockup.LinearTimelock;
79+
}
80+
return Shape.Lockup.Cliff;
6981
}
7082

7183
/**

graph/lockup/store/entity-stream.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function createStreamLinear(
6666
}
6767

6868
stream = addCliff(stream, commonParams, linearParams);
69-
stream = addLinearShape(stream, stream.cliff);
69+
stream = addLinearShape(stream, stream.cliff, stream.cliffTime, stream.endTime);
7070
stream.save();
7171
}
7272

@@ -235,12 +235,17 @@ function addCliff(
235235
* Older versions of Lockup did not have a shape field, but it can be inferred.
236236
* @see https://github.com/sablier-labs/interfaces/blob/30fffc0/packages/constants/src/stream/shape.ts#L12
237237
*/
238-
function addLinearShape(stream: Entity.Stream, cliff: boolean): Entity.Stream {
238+
function addLinearShape(
239+
stream: Entity.Stream,
240+
cliff: boolean,
241+
cliffTime: BigInt | null,
242+
endTime: BigInt
243+
): Entity.Stream {
239244
if (stream.shape !== null) {
240245
stream.shapeSource = "Event";
241246
return stream;
242247
}
243-
stream.shape = inferLinearShape(cliff);
248+
stream.shape = inferLinearShape(cliff, cliffTime, endTime);
244249
stream.shapeSource = "Inferred";
245250
return stream;
246251
}

graph/lockup/store/shape-inference.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,21 @@ const TIMELOCK_MAX_DURATION = ONE;
88

99
/**
1010
* Infer the shape of a linear stream based on whether it has a cliff.
11+
* Detects linearTimelock when cliff is followed by immediate unlock (≤ 1 second).
1112
*/
12-
export function inferLinearShape(cliff: boolean): string {
13-
return cliff ? LockupShape.Cliff : LockupShape.Linear;
13+
export function inferLinearShape(
14+
cliff: boolean,
15+
cliffTime: BigInt | null,
16+
endTime: BigInt
17+
): string {
18+
if (!cliff) {
19+
return LockupShape.Linear;
20+
}
21+
// Detect linearTimelock: cliff followed by immediate unlock (≤ 1 second)
22+
if (cliffTime !== null && endTime.minus(cliffTime).le(TIMELOCK_MAX_DURATION)) {
23+
return LockupShape.LinearTimelock;
24+
}
25+
return LockupShape.Cliff;
1426
}
1527

1628
/**

0 commit comments

Comments
 (0)