Skip to content
Closed
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
26 changes: 8 additions & 18 deletions .github/workflows/all.publish.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Publishing


on:
release:
types:
- published
types: [published]


permissions:
contents: read
Expand All @@ -12,6 +13,8 @@ jobs:
publish:
name: Publish NPM Packages
runs-on: ubuntu-latest
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

steps:
- name: Harden Runner
Expand All @@ -27,32 +30,19 @@ jobs:
with:
node-version: 20.x

- name: Create file .npmrc
run: |
touch .npmrc
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> .npmrc
cp .npmrc ./packages/ats/contracts/.npmrc
cp .npmrc ./packages/ats/sdk/.npmrc

- name: Install dependencies
run: npm ci

# --- ATS publishing ---
- name: Publish ats/contracts
if: contains(github.ref_name, 'ats')
run: npm run ats:contracts:publish --access=public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run ats:contracts:publish

- name: Publish ats/sdk
if: contains(github.ref_name, 'ats')
run: npm run ats:sdk:publish --access=public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run ats:sdk:publish

# --- Mass Payout publishing ---
- name: Publish mass-payout
if: contains(github.ref_name, 'mp')
run: npm run mass-payout:publish --access=public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run mass-payout:publish
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ dist

# Stores VSCode versions used for testing VSCode extensions
.vscode-test
.github/
Copy link
Member

Choose a reason for hiding this comment

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

What is the purpose of this change?
I don't think we want to ignore changes to the .github dir as this would impact modifications to the workflow files, dependabot config, codeowners file and the PR template.


# Mac files
.DS_Store
Expand Down
2 changes: 1 addition & 1 deletion apps/ats/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hashgraph/asset-tokenization-dapp",
"version": "1.16.0",
"version": "1.16.1",
"license": "Apache-2.0",
"scripts": {
"build": "tsc && vite build",
Expand Down
15 changes: 15 additions & 0 deletions apps/ats/web/src/i18n/en/security/coupons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export default {
recordDate: 'Record Date',
executionDate: 'Execution Date',
rate: 'Coupon Rate',
period: 'Period',
snapshotId: 'Snapshot',
},
emptyTable: 'No coupons found',
Expand All @@ -239,6 +240,19 @@ export default {
placeholder: '0,123%',
tooltip: 'Interest rate for the coupon.',
},
period: {
label: 'Coupon period',
placeholder: 'Select coupon period',
tooltip:
'The period between coupon payments. This field is required for all coupon operations.',
options: {
day: '1 Day',
week: '1 Week',
month: '1 Month',
quarter: '3 Months',
year: '1 Year',
},
},
},
},
see: {
Expand All @@ -261,6 +275,7 @@ export default {
details: {
title: 'Detail',
paymentDay: 'Payment day',
period: 'Period',
amount: 'Amount',
},
},
Expand Down
23 changes: 23 additions & 0 deletions apps/ats/web/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,5 +224,28 @@ export const NOMINAL_VALUE_FACTOR = 100;

export const DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm:ss';

// * Time periods (in seconds and milliseconds)
export const TIME_PERIODS_S = {
SECOND: 1,
MINUTE: 60,
HOUR: 60 * 60,
DAY: 24 * 60 * 60,
WEEK: 7 * 24 * 60 * 60,
MONTH: 30 * 24 * 60 * 60,
QUARTER: 90 * 24 * 60 * 60,
YEAR: 365 * 24 * 60 * 60,
};

export const TIME_PERIODS_MS = {
SECOND: TIME_PERIODS_S.SECOND * 1000,
MINUTE: TIME_PERIODS_S.MINUTE * 1000,
HOUR: TIME_PERIODS_S.HOUR * 1000,
DAY: TIME_PERIODS_S.DAY * 1000,
WEEK: TIME_PERIODS_S.WEEK * 1000,
MONTH: TIME_PERIODS_S.MONTH * 1000,
QUARTER: TIME_PERIODS_S.QUARTER * 1000,
YEAR: TIME_PERIODS_S.YEAR * 1000,
};

export const DEFAULT_PARTITION =
'0x0000000000000000000000000000000000000000000000000000000000000001';
53 changes: 53 additions & 0 deletions apps/ats/web/src/utils/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,59 @@ export const formatPeriod = ({
return `${amount} ${unit}`;
};

/**
* Formats a period in seconds to human-readable format
*/
export const formatCouponPeriod = (periodInSeconds: number): string => {
const { TIME_PERIODS_S } = require('./constants');

if (periodInSeconds >= TIME_PERIODS_S.YEAR) {
const years = Math.floor(periodInSeconds / TIME_PERIODS_S.YEAR);
return `${years} ${years === 1 ? 'Year' : 'Years'}`;
}
if (periodInSeconds >= TIME_PERIODS_S.QUARTER) {
const quarters = Math.floor(periodInSeconds / TIME_PERIODS_S.QUARTER);
return `${quarters} ${quarters === 1 ? 'Quarter' : 'Quarters'}`;
}
if (periodInSeconds >= TIME_PERIODS_S.MONTH) {
const months = Math.floor(periodInSeconds / TIME_PERIODS_S.MONTH);
return `${months} ${months === 1 ? 'Month' : 'Months'}`;
}
if (periodInSeconds >= TIME_PERIODS_S.WEEK) {
const weeks = Math.floor(periodInSeconds / TIME_PERIODS_S.WEEK);
return `${weeks} ${weeks === 1 ? 'Week' : 'Weeks'}`;
}
if (periodInSeconds >= TIME_PERIODS_S.DAY) {
const days = Math.floor(periodInSeconds / TIME_PERIODS_S.DAY);
return `${days} ${days === 1 ? 'Day' : 'Days'}`;
}
return `${periodInSeconds} Seconds`;
};

/**
* Validates if a period is within acceptable bounds
* Period is REQUIRED for all coupon operations
*/
export const validateCouponPeriod = (
periodInSeconds: number,
maturityDate?: Date,
): string | true => {
// Period is required - cannot be null or undefined
if (!periodInSeconds || periodInSeconds < 0) {
return 'Coupon period is required and must be greater or equal to 0';
}

if (maturityDate) {
const timeToMaturity = Math.floor(
(maturityDate.getTime() - Date.now()) / 1000,
);
if (periodInSeconds > timeToMaturity) {
return 'Period cannot exceed bond maturity date';
}
}
return true;
};

//TODO: remove?
export const formatNumber = (
value: number | string | null,
Expand Down
24 changes: 22 additions & 2 deletions apps/ats/web/src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ import isEqual from 'date-fns/isEqual';
import i18n from '../i18n';
import { formatDate, toDate } from './format';

const t = (key: string, options?: object) => {
return i18n.t(`rules:${key}`, options || {});
const t = (key: string, options?: Record<string, unknown>) => {
return i18n.t(`rules:${key}`, options);
};

export const maxLength = (value: number) => ({
Expand Down Expand Up @@ -321,3 +321,23 @@ export const isValidHederaId = (val: string) => {
const maskRegex = /^[0-9]\.[0-9]\.[0-9]{1,7}$/;
return maskRegex.test(val) || t('isValidHederaId');
};

export const isValidCouponPeriod = (val: string) => {
try {
// Period is required - cannot be empty or null
if (!val || val.trim() === '') {
return 'Coupon period is required';
}

const periodValue = parseInt(val);
if (isNaN(periodValue) || periodValue <= 0) {
return 'Coupon period must be a valid positive number';
}

const { validateCouponPeriod } = require('./format');
const validation = validateCouponPeriod(periodValue);
return validation === true || validation;
} catch (error) {
return 'Invalid coupon period';
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export const StepCoupon = () => {
options={CouponTypeOptions}
/>
</Stack>

{couponType === 1 && (
<>
<Stack w="full">
Expand Down
6 changes: 3 additions & 3 deletions apps/ats/web/src/views/CreateBond/Components/StepReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,15 @@ export const StepReview = () => {
configVersion: parseInt(process.env.REACT_APP_BOND_CONFIG_VERSION ?? '0'),
...(externalPausesList &&
externalPausesList.length > 0 && {
externalPauses: externalPausesList,
externalPausesIds: externalPausesList,
}),
...(externalControlList &&
externalControlList.length > 0 && {
externalControlLists: externalControlList,
externalControlListsIds: externalControlList,
}),
...(externalKYCList &&
externalKYCList.length > 0 && {
externalKycLists: externalKYCList,
externalKycListsIds: externalKYCList,
}),
internalKycActivated,
...(complianceId && {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,15 @@ export const StepReview = () => {
),
...(externalPausesList &&
externalPausesList.length > 0 && {
externalPauses: externalPausesList,
externalPausesIds: externalPausesList,
}),
...(externalControlList &&
externalControlList.length > 0 && {
externalControlLists: externalControlList,
externalControlListsIds: externalControlList,
}),
...(externalKYCList &&
externalKYCList.length > 0 && {
externalKycLists: externalKYCList,
externalKycListsIds: externalKYCList,
}),
internalKycActivated,
...(complianceId && {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export const ClearingOperationsCreate = () => {
amount: amount.toString(),
clearingExpirationDate: dateToUnixTimestamp(expirationDate),
holdExpirationDate: dateToUnixTimestamp(holdExpirationDate),
escrow: escrowAccount,
escrowId: escrowAccount,
// TODO: check with SDK: sourceId,
targetId,
partitionId: DEFAULT_PARTITION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createColumnHelper } from '@tanstack/table-core';
import { Table, Text } from 'io-bricks-ui';
import { useTranslation } from 'react-i18next';
import { COUPONS_FACTOR, DATE_TIME_FORMAT } from '../../../../utils/constants';
import { formatDate } from '../../../../utils/format';
import { formatDate, formatCouponPeriod } from '../../../../utils/format';

export const CouponsList = () => {
const { id } = useParams();
Expand Down Expand Up @@ -49,6 +49,11 @@ export const CouponsList = () => {
cell: (row) => `${parseInt(row.getValue()) / COUPONS_FACTOR}%`,
enableSorting: false,
}),
columnHelper.accessor('period', {
header: t('columns.period'),
cell: (row) => formatCouponPeriod(row.getValue()),
enableSorting: false,
}),
columnHelper.accessor('snapshotId', {
header: t('columns.snapshotId'),
cell: (row) => row.getValue() ?? '-',
Expand Down
Loading