From d3713a31538a9ab6654a3a0017bfd5842e5b0a08 Mon Sep 17 00:00:00 2001 From: Lazy Nina <> Date: Tue, 9 Jan 2024 09:32:56 -0500 Subject: [PATCH] Add support for coin lockup txn types --- src/app/approve/approve.component.ts | 86 +++++++++++++++++++++++++++- src/app/identity.service.ts | 8 +++ src/lib/bindata/transcoders.ts | 7 ++- src/lib/bindata/util.ts | 18 ++++++ src/lib/deso/transaction.ts | 60 +++++++++++++++++++ 5 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/app/approve/approve.component.ts b/src/app/approve/approve.component.ts index a3167508..356434ec 100644 --- a/src/app/approve/approve.component.ts +++ b/src/app/approve/approve.component.ts @@ -43,6 +43,10 @@ import { TransactionMetadataUnstake, TransactionMetadataUnlockStake, TransactionMetadataUnjailValidator, + TransactionMetadataCoinLockup, + TransactionMetadataUpdateCoinLockupParams, + TransactionMetadataCoinLockupTransfer, + TransactionMetadataCoinUnlock, } from '../../lib/deso/transaction'; import { ExtraData } from '../../types/identity'; import { AccountService } from '../account.service'; @@ -639,6 +643,76 @@ export class ApproveComponent implements OnInit { case TransactionMetadataUnjailValidator: description = 'unjail your validator'; break; + case TransactionMetadataCoinLockup: + // TODO: enhanced description for coin lockup. + // Do we need to special case lock ups of DESO? + const coinLockupMetadata = this.transaction + .metadata as TransactionMetadataCoinLockup; + const lockupAmount = this.hexBaseUnitsToUnitString( + coinLockupMetadata.lockupAmountBaseUnits + ); + const lockupProfilePubKey = this.base58KeyCheck( + coinLockupMetadata.profilePublicKey + ); + const lockUpRecipientPubKey = this.base58KeyCheck( + coinLockupMetadata.recipientPublicKey + ); + publicKeys = [lockupProfilePubKey, lockUpRecipientPubKey]; + description = `lockup ${lockupAmount} of your ${lockupProfilePubKey} coins`; + break; + case TransactionMetadataUpdateCoinLockupParams: + // TODO: enhanced description for coin lockup params + const updateCoinLockupParamsMetadata = this.transaction + .metadata as TransactionMetadataUpdateCoinLockupParams; + description = + `update your coin lockup params\n` + + `${ + updateCoinLockupParamsMetadata.removeYieldCurvePoint + ? 'Remove' + : 'Add' + } yield curve point with + ${ + updateCoinLockupParamsMetadata.lockupYieldAPYBasisPoints / 100 + }% APY for a lockup of + ${ + updateCoinLockupParamsMetadata.lockupYieldDurationNanoSecs + } nanoseconds + ${ + !updateCoinLockupParamsMetadata.newLockupTransferRestrictions + ? '' + : 'and update your lockup transfer restrictions' + }`; + + break; + case TransactionMetadataCoinLockupTransfer: + // TODO: enhanced description for coin lockup transfer + // Do we need a special case for DESO? + const coinLockupTransferMetadata = this.transaction + .metadata as TransactionMetadataCoinLockupTransfer; + const lockupTransferAmount = this.hexBaseUnitsToUnitString( + coinLockupTransferMetadata.lockedCoinsToTransferBaseUnits + ); + const lockupTransferProfilePubKey = this.base58KeyCheck( + coinLockupTransferMetadata.profilePublicKey + ); + const lockupTransferRecipientPubKey = this.base58KeyCheck( + coinLockupTransferMetadata.recipientPublicKey + ); + publicKeys = [ + lockupTransferProfilePubKey, + lockupTransferRecipientPubKey, + ]; + description = `transfer ${lockupTransferAmount} of your locked ${lockupTransferProfilePubKey} coins to ${lockupTransferRecipientPubKey}`; + break; + case TransactionMetadataCoinUnlock: + const coinUnlockMetadata = this.transaction + .metadata as TransactionMetadataCoinUnlock; + const unlockProfilePubKey = this.base58KeyCheck( + coinUnlockMetadata.profilePublicKey + ); + publicKeys = [unlockProfilePubKey]; + description = `unlock your locked ${unlockProfilePubKey} coins`; + break; } // Set the transaction description based on the description populated with public keys. @@ -659,16 +733,24 @@ export class ApproveComponent implements OnInit { return bs58check.encode(Buffer.from([...prefix, ...keyBytes])); } - // TODO: create hexBaseUnitsToUnitString function to support proper - // DAO Coin base unit conversions. + // uint256 to DESO nano conversions. hexNanosToUnitString(nanos: Buffer): string { return this.nanosToUnitString(parseInt(nanos.toString('hex'), 16)); } + // unit256 to base unit conversions. + hexBaseUnitsToUnitString(baseUnits: Buffer): string { + return this.baseUnitsToUnitString(parseInt(baseUnits.toString('hex'), 16)); + } + nanosToUnitString(nanos: number): string { return this.toFixedLengthDecimalString(nanos / 1e9); } + baseUnitsToUnitString(baseUnits: number): string { + return this.toFixedLengthDecimalString(baseUnits / 1e18); + } + hexScaledExchangeRateToFloat(hex: Buffer): number { return parseInt(hex.toString('hex'), 16) / 1e38; } diff --git a/src/app/identity.service.ts b/src/app/identity.service.ts index 7601a45b..673956f2 100644 --- a/src/app/identity.service.ts +++ b/src/app/identity.service.ts @@ -41,6 +41,10 @@ import { TransactionMetadataUnstake, TransactionMetadataUnlockStake, TransactionMetadataUnjailValidator, + TransactionMetadataCoinLockup, + TransactionMetadataUpdateCoinLockupParams, + TransactionMetadataCoinLockupTransfer, + TransactionMetadataCoinUnlock, } from '../lib/deso/transaction'; import { SwalHelper } from '../lib/helpers/swal-helper'; import { AccessLevel, PublicUserInfo } from '../types/identity'; @@ -529,6 +533,10 @@ export class IdentityService { case TransactionMetadataStake: case TransactionMetadataUnstake: case TransactionMetadataUnlockStake: + case TransactionMetadataCoinLockup: + case TransactionMetadataUpdateCoinLockupParams: + case TransactionMetadataCoinLockupTransfer: + case TransactionMetadataCoinUnlock: return AccessLevel.Full; case TransactionMetadataFollow: diff --git a/src/lib/bindata/transcoders.ts b/src/lib/bindata/transcoders.ts index e5fbf15d..15b82694 100644 --- a/src/lib/bindata/transcoders.ts +++ b/src/lib/bindata/transcoders.ts @@ -1,4 +1,4 @@ -import { bufToUvarint64, uvarint64ToBuf } from './util'; +import { bufToUvarint64, bufToVarint64, uvarint64ToBuf } from './util'; import { TransactionNonce } from '../deso/transaction'; export interface Transcoder { @@ -19,6 +19,11 @@ export const Uvarint64: Transcoder = { write: (uint) => uvarint64ToBuf(uint), }; +export const Varint64: Transcoder = { + read: (bytes) => bufToVarint64(bytes), + write: (int) => uvarint64ToBuf(int), +}; + export const Boolean: Transcoder = { read: (bytes) => [bytes.readUInt8(0) != 0, bytes.slice(1)], write: (bool) => { diff --git a/src/lib/bindata/util.ts b/src/lib/bindata/util.ts index 9a11ea93..33669541 100644 --- a/src/lib/bindata/util.ts +++ b/src/lib/bindata/util.ts @@ -50,3 +50,21 @@ export const uint64ToBufBigEndian = (uint: number): Buffer => { return new Buffer(result.reverse()); }; + +// TODO: Verify these are correct.... +export const varint64ToBuf = (int: number): Buffer => { + let ux = BigInt(int) << BigInt(1); + if (int < 0) { + ux = ~ux; + } + return uvarint64ToBuf(Number(ux)); +}; + +export const bufToVarint64 = (buffer: Buffer): [number, Buffer] => { + const [ux, n] = bufToUvarint64(buffer); + let x = BigInt(ux) >> BigInt(1); + if (ux & 1) { + x = ~x; + } + return [Number(x), n]; +}; diff --git a/src/lib/deso/transaction.ts b/src/lib/deso/transaction.ts index 699ef4b1..ce599240 100644 --- a/src/lib/deso/transaction.ts +++ b/src/lib/deso/transaction.ts @@ -12,6 +12,7 @@ import { Uvarint64, VarBuffer, VarBufferArray, + Varint64, } from '../bindata/transcoders'; export class TransactionInput extends BinaryRecord { @@ -643,6 +644,61 @@ export class TransactionMetadataUnlockStake extends TransactionMetadata { export class TransactionMetadataUnjailValidator extends TransactionMetadata {} +export class TransactionMetadataCoinLockup extends TransactionMetadata { + @Transcode(VarBuffer) + profilePublicKey: Buffer = Buffer.alloc(0); + + @Transcode(VarBuffer) + recipientPublicKey: Buffer = Buffer.alloc(0); + + @Transcode(Varint64) + unlockTimestampNanoSecs: number = 0; + + @Transcode(Varint64) + vestingEndTimestampNanoSecs: number = 0; + + // TODO: We may want a better way to handle uint256s. + @Transcode(Optional(VarBuffer)) + lockupAmountBaseUnits: Buffer = Buffer.alloc(0); +} + +export class TransactionMetadataUpdateCoinLockupParams extends TransactionMetadata { + @Transcode(Varint64) + lockupYieldDurationNanoSecs: number = 0; + + @Transcode(Uvarint64) + lockupYieldAPYBasisPoints: number = 0; + + @Transcode(Boolean) + removeYieldCurvePoint: boolean = false; + + @Transcode(Boolean) + newLockupTransferRestrictions: boolean = false; + + @Transcode(Uint8) + lockupTransferRestrictionStatus: number = 0; +} + +export class TransactionMetadataCoinLockupTransfer extends TransactionMetadata { + @Transcode(VarBuffer) + recipientPublicKey: Buffer = Buffer.alloc(0); + + @Transcode(VarBuffer) + profilePublicKey: Buffer = Buffer.alloc(0); + + @Transcode(Varint64) + unlockTimestampNanoSecs: number = 0; + + // TODO: We may want a better way to handle uint256s. + @Transcode(Optional(VarBuffer)) + lockedCoinsToTransferBaseUnits: Buffer = Buffer.alloc(0); +} + +export class TransactionMetadataCoinUnlock extends TransactionMetadata { + @Transcode(VarBuffer) + profilePublicKey: Buffer = Buffer.alloc(0); +} + export const TransactionTypeMetadataMap = { 1: TransactionMetadataBlockReward, 2: TransactionMetadataBasicTransfer, @@ -682,6 +738,10 @@ export const TransactionTypeMetadataMap = { 37: TransactionMetadataUnstake, 38: TransactionMetadataUnlockStake, 39: TransactionMetadataUnjailValidator, + 40: TransactionMetadataCoinLockup, + 41: TransactionMetadataUpdateCoinLockupParams, + 42: TransactionMetadataCoinLockupTransfer, + 43: TransactionMetadataCoinUnlock, }; export class Transaction extends BinaryRecord {