Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/schemas/delegatedStaking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ components:
ordinal: { type: integer }
source: { type: string }
nodeId: { type: string }
amount: { type: string, description: "BigInt represented as a string" }
amount: { type: string, description: "Effective stake amount (current amount if increased, otherwise original). BigInt represented as a string" }
fee: { type: string, description: "BigInt represented as a string" }
tokenLockHash: { type: string }
tokenLockHash: { type: string, description: "Effective token lock hash (current if stake increased, otherwise original)" }
parentHash: { type: string, nullable: true }
type: { type: string, enum: [create, transfer] }
status: { type: string, enum: [active, transferred, pendingWithdrawal, withdrawalComplete] }
Expand All @@ -33,7 +33,7 @@ components:
nodeId: { type: string }
status: { type: string, enum: [active, transferred, pendingWithdrawal, withdrawalComplete] }
stakeHash: { type: string }
lockAmount: { type: string, description: "BigInt represented as a string" }
lockAmount: { type: string, description: "Effective locked stake amount (current amount if increased, otherwise original). BigInt represented as a string" }
rewardsAccrued: { type: string, description: "BigInt represented as a string" }
withdrawnAmount: { type: string, description: "BigInt represented as a string" }
transferredFromHash: { type: string, nullable: true }
Expand Down
7 changes: 5 additions & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -601,8 +601,10 @@ model delegate_stake_create_events {
parent_hash String @db.VarChar
global_snapshot_hash String @db.VarChar
transfer_from_hash String? @unique @db.VarChar
created_at DateTime @default(now()) @db.Timestamp(6)
updated_at DateTime @default(now()) @db.Timestamp(6)
created_at DateTime @default(now()) @db.Timestamp(6)
updated_at DateTime @default(now()) @db.Timestamp(6)
current_token_lock_hash String? @db.VarChar
current_amount BigInt?

global_snapshot global_snapshots @relation(fields: [global_snapshot_hash], references: [hash], onDelete: Cascade, onUpdate: Restrict)
addresses addresses @relation(fields: [source_addr], references: [address], onDelete: Cascade, onUpdate: Restrict)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: the column name -- snapshot-streaming PR #114 creates this column as current_lock_reference_hash in the SQL schema (sql/delegated_staking.sql), not current_token_lock_hash. These need to be aligned or the Prisma client won't find the data that snapshot-streaming writes. Same issue for the delegate_stake_withdraw_events table below and the replacement_hash on dag_token_locks (which SS calls replace_token_lock_ref).

I'd suggest coordinating with the SS PR author on a single naming convention. The tessellation Scala types use currentTokenLockRef on DelegatedStakeRecord and replaceTokenLockRef on TokenLock, so current_token_lock_hash and replace_token_lock_ref might be reasonable compromises -- but the important thing is they match across repos.

Expand All @@ -617,6 +619,7 @@ model delegate_stake_create_events {
@@index([global_snapshot_hash])
@@index([source_addr])
@@index([lock_reference_hash])
@@index([current_token_lock_hash])
}

model delegate_stake_withdraw_events {
Expand Down
12 changes: 12 additions & 0 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ export const data_dag_token_locks = [
round_id: randomUUID(),
snapshot_hash: data_global_snapshots[1].hash,
},
{
hash: `token-lock-hash-004`,
currency_id: "currency-2",
source_addr: data_addresses[1].address,
amount: 5000000000000n,
ordinal: 4n,
unlock_epoch: 10n,
round_id: randomUUID(),
snapshot_hash: data_global_snapshots[2].hash,
},
];

// Test data for DAG token unlocks
Expand Down Expand Up @@ -555,6 +565,8 @@ export const data_delegate_stake_create_events = [
lock_reference_hash: data_dag_token_locks[1].hash,
parent_hash: "stake-event-hash-001",
global_snapshot_hash: data_global_snapshots[1].hash,
current_token_lock_hash: data_dag_token_locks[3].hash,
current_amount: 5000000000000n,
},
{
hash: "stake-event-hash-003",
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/delegatedStakingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ const delegateStakeCreateResponse = (event) => ({
ordinal: event.ordinal,
source: event.source_addr,
nodeId: event.node_id,
amount: event.amount,
amount: event.current_amount ?? event.amount,
fee: event.fee,
tokenLockHash: event.lock_reference_hash,
tokenLockHash: event.current_token_lock_hash ?? event.lock_reference_hash,
parentHash: event.parent_hash,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies the effective tokenLockHash for creates, but delegateStakeWithdrawResponse (line ~64-73) isn't updated to do the same. The snapshot-streaming side (PR #114, SnapshotDAO.scala) inserts current_lock_reference_hash and current_amount into delegate_stake_withdraw_events too (via PendingDelegatedStakeWithdrawal.currentTokenLockRef from tessellation delegatedStake.scala:147-148). So withdrawal responses should also show effective amounts if a withdrawal happens on an increased stake.

I think you need the same null-coalescing pattern in delegateStakeWithdrawResponse:

currentTokenLockHash: event.current_token_lock_hash ?? event.lock_reference_hash, // or however the field maps
currentAmount: event.current_amount ?? event.amount,

(Though note the column naming issue -- see my comment on snapshot-streaming PR #114 re: current_lock_reference_hash vs current_token_lock_hash.)

type: event.transfer_from_hash ? "transfer" : "create",
status: stakeStatus(event),
Expand Down Expand Up @@ -92,7 +92,7 @@ const delegateStakePositionResponse = (change) => {
nodeId: change.node_id,
status: stakeStatus(change),
stakeHash: change.hash,
lockAmount: change.amount,
lockAmount: change.current_amount ?? change.amount,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lockAmount now shows the effective amount, but withdrawnAmount on line 98 is computed by completedAmount (line 80) which still uses change.amount — the original. For an increased stake that gets withdrawn, these will be inconsistent within the same response. completedAmount should use (change.current_amount ?? change.amount) to match.

rewardsAccrued:
change.delegate_stake_total_rewards?.delegate_stake_total_rewards ?? 0,
withdrawnAmount: completedAmount(change),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lockAmount gets the effective amount here, but the position response also has an implicit tokenLockHash that still uses the original lock_reference_hash (not shown in the diff since it's unchanged). For consistency with the create response above, I think delegateStakePositionResponse should also resolve the effective token lock hash -- otherwise you'd get a position showing the increased amount but referencing the original (now-replaced) token lock, which is confusing for API consumers.

Expand Down
2 changes: 1 addition & 1 deletion tests/handlers/actionsHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe("actionsHandler", () => {

expect(result.statusCode).toBe(200);
const body = validatePaginatedResponse(result);
expect(body.data.length).toBe(19);
expect(body.data.length).toBe(20);
body.data.forEach(validateAction);
});

Expand Down
8 changes: 6 additions & 2 deletions tests/handlers/delegatedStakingHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ const validateCreateStake = (tx) => {
);
expect(tx.source).toBe(match.source_addr);
expect(tx.nodeId).toBe(match.node_id);
expect(tx.amount).toBeBigInt(match.amount);
// Use effective amount (current_amount if set, otherwise original amount)
const effectiveAmount = match.current_amount ?? match.amount;
expect(tx.amount).toBeBigInt(effectiveAmount);
expect(tx.fee).toBeBigInt(match.fee);
expect(tx.timestamp).toBeDefined();
expect(tx.type).toBe(match.transfer_from_hash ? "transfer" : "create");
Expand Down Expand Up @@ -70,7 +72,9 @@ const validateStakingPosition = (tx) => {
);
expect(tx.address).toBe(match.source_addr);
expect(tx.nodeId).toBe(match.node_id);
expect(tx.lockAmount).toBeBigInt(match.amount);
// Use effective amount (current_amount if set, otherwise original amount)
const effectiveLockAmount = match.current_amount ?? match.amount;
expect(tx.lockAmount).toBeBigInt(effectiveLockAmount);
//rewards for this staking event
expect(tx.rewardsAccrued).toBeBigInt(
data_delegate_stake_rewards
Expand Down
12 changes: 7 additions & 5 deletions tests/handlers/tokenLocksHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,11 @@ describe("Token Locks Handler Integration Tests", () => {
expect(response.statusCode).toBe(200);
const body = validatePaginatedResponse(response);

// Only the second and third token lock should be active (not unlocked)
expect(body.data.length).toBe(2);
expect(body.data[1].hash).toBe(data_dag_token_locks[1].hash);
expect(body.data[0].hash).toBe(data_dag_token_locks[2].hash);
// Token locks 2, 3, and 4 should be active (not unlocked)
expect(body.data.length).toBe(3);
expect(body.data[2].hash).toBe(data_dag_token_locks[1].hash);
expect(body.data[1].hash).toBe(data_dag_token_locks[2].hash);
expect(body.data[0].hash).toBe(data_dag_token_locks[3].hash);
});
});

Expand Down Expand Up @@ -212,7 +213,8 @@ describe("Token Locks Handler Integration Tests", () => {
expect(response.statusCode).toBe(200);
const body = validatePaginatedResponse(response);

expect(body.data.length).toBe(data_dag_token_locks.length);
// Token locks 1, 2, and 3 belong to address[0]; token lock 4 belongs to address[1]
expect(body.data.length).toBe(3);
body.data.forEach(validateDagTokenLock);
});

Expand Down