diff --git a/.github/workflows/add-ipfs-notes-to-release.yml b/.github/workflows/add-ipfs-notes-to-release.yml index 203ab399f..77c729555 100644 --- a/.github/workflows/add-ipfs-notes-to-release.yml +++ b/.github/workflows/add-ipfs-notes-to-release.yml @@ -82,3 +82,38 @@ jobs: https://${{ env.IPFS_V1 }}.ipfs.dweb.link/ https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/ ipfs://${{ env.IPFS_V0 }}/ + + - name: Ensure 'Released' label exists + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + REPO="${{ github.repository }}" + LABEL_EXIST=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/$REPO/labels/Released") + if [[ "$LABEL_EXIST" == *"Not Found"* ]]; then + curl -s -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -X POST "https://api.github.com/repos/$REPO/labels" \ + -d '{"name": "Released", "color": "FFFFFF"}' + fi + + - name: Extract issues from release notes + id: extract-issues + run: | + ISSUES=$(echo "${{ github.event.release.body }}" | grep -o -E '#[0-9]+' | tr -d '#' | jq -R . | jq -cs .) + echo "Issues to label: $ISSUES" + echo "::set-output name=issue_numbers::$ISSUES" + + - name: Add 'Released' label to issues + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ISSUE_NUMBERS="${{ steps.extract-issues.outputs.issue_numbers }}" + REPO="${{ github.repository }}" + for ISSUE in $(echo "$ISSUE_NUMBERS" | jq -r '.[]'); do + curl -s -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -X POST "https://api.github.com/repos/$REPO/issues/$ISSUE/labels" \ + -d '{"labels": ["Released"]}' + done diff --git a/.github/workflows/ci-cd-trigger.yml b/.github/workflows/ci-cd-trigger.yml index 0f8273647..cf0a0765f 100644 --- a/.github/workflows/ci-cd-trigger.yml +++ b/.github/workflows/ci-cd-trigger.yml @@ -170,15 +170,6 @@ jobs: preview_explorer: ${{ env.PREVIEW_EXPLORER }} preview_tools: ${{ env.PREVIEW_TOOLS }} - # console-e2e: - # needs: build-sources - # name: '(CI) console python' - # uses: ./.github/workflows/console-test-run.yml - # secrets: inherit - # if: ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') && github.event_name == 'pull_request' }} - # with: - # github-sha: ${{ github.event.pull_request.head.sha || github.sha }} - check-e2e-needed: runs-on: ubuntu-latest needs: build-sources @@ -212,6 +203,15 @@ jobs: projects: ${{ needs.build-sources.outputs.projects-e2e }} tags: '@smoke' + console-e2e: + needs: [build-sources, check-e2e-needed] + name: '(CI) console python' + uses: ./.github/workflows/console-test-run.yml + secrets: inherit + if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }} && ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') }} + with: + github-sha: ${{ github.event.pull_request.head.sha || github.sha }} + publish-dist: needs: build-sources name: '(CD) publish dist' diff --git a/.github/workflows/console-test-run.yml b/.github/workflows/console-test-run.yml index da43d1e1a..661ba9472 100644 --- a/.github/workflows/console-test-run.yml +++ b/.github/workflows/console-test-run.yml @@ -1,8 +1,5 @@ name: (CI) Console tests -env: - VEGA_VERSION: v0.72.14 - on: workflow_call: inputs: @@ -19,9 +16,9 @@ on: - develop jobs: - run-tests: - name: run-tests - runs-on: 8-cores + create-docker-image: + name: Create docker image for console-test + runs-on: ubuntu-22.04 timeout-minutes: 20 steps: #---------------------------------------------- @@ -58,23 +55,105 @@ jobs: #---------------------------------------------- # build trading #---------------------------------------------- - - name: Build affected spec + - name: Build trading app run: | yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading + DIST_LOCATION=dist/apps/trading/exported + mv $DIST_LOCATION dist-result + tree dist-result + #---------------------------------------------- - # run trading server + # export trading app docker image #---------------------------------------------- - - name: Run trading server + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and export to local Docker + id: docker_build + uses: docker/build-push-action@v5 + with: + context: . + file: docker/node-outside-docker.Dockerfile + load: true + build-args: | + APP=trading + ENV_NAME=stagnet1 + tags: ci/trading:local + outputs: type=docker,dest=/tmp/console-image.tar + + - name: Verify docker image created run: | - docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629 - sleep 5 - docker ps - #---------------------------------------------- - # check if container persists between runs - #---------------------------------------------- - - name: Check server + echo ${{ steps.docker_build.outputs.digest }} + echo ${{ steps.docker_build.outputs.imageid }} + + - name: Upload docker image for console-test usage + uses: actions/upload-artifact@v3 + with: + name: console-image + path: /tmp/console-image.tar + + console-test-branch: + name: Choose console-test branch to run on + runs-on: ubuntu-22.04 + timeout-minutes: 5 + outputs: + console-branch: ${{ steps.output-step.outputs.branch }} + steps: + - name: Workflow dispatch input + id: dispatch-step + if: github.event_name == 'workflow_dispatch' + run: echo "branch=${{ inputs.console-test-branch }}" >> $GITHUB_OUTPUT + + - name: Print Workflow dispatch input + if: github.event_name == 'workflow_dispatch' + run: echo ${{ steps.dispatch-step.outputs.branch }} + + - name: Workflow_call input + id: workflow_call-step + if: github.event_name != 'workflow_dispatch' run: | - docker ps + if [[ "${{ github.base_ref }}" == "main" ]]; then + echo "branch=main" >> $GITHUB_OUTPUT + elif [[ "${{ github.base_ref }}" == "develop" && "${{ github.ref_name }}" == "main" ]]; then + echo "branch=main" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref_name }}" == *"release/mainnet"* ]]; then + echo "branch=main" >> $GITHUB_OUTPUT + else + echo "branch=develop" >> $GITHUB_OUTPUT + fi + + - name: Print Workflow_call input + if: github.event_name != 'workflow_dispatch' + run: echo ${{ steps.workflow_call-step.outputs.branch }} + + - name: Set output + id: output-step + run: echo "branch=${{ steps.dispatch-step.outputs.branch || steps.workflow_call-step.outputs.branch }}" >> $GITHUB_OUTPUT + + - name: Print final output + run: echo ${{ steps.output-step.outputs.branch }} + + run-tests: + name: run-tests + runs-on: 8-cores + needs: [create-docker-image, console-test-branch] + timeout-minutes: 20 + steps: + #---------------------------------------------- + # load docker image + #---------------------------------------------- + - name: Download docker image from previous job + uses: actions/download-artifact@v3 + with: + name: console-image + path: /tmp + + - name: Load Docker image + run: | + docker load --input /tmp/console-image.tar + docker image ls -a + #---------------------------------------------- # check-out tests repo #---------------------------------------------- @@ -82,62 +161,55 @@ jobs: uses: actions/checkout@v3 with: repository: vegaprotocol/console-test - ref: ${{ inputs.console-test-branch }} - path: './console-test' + ref: ${{ needs.console-test-branch.outputs.console-branch }} - name: Load console test envs id: console-test-env uses: falti/dotenv-action@v1.0.4 with: - path: './console-test/.env.${{ inputs.console-test-branch }}' + path: '.env.${{ needs.console-test-branch.outputs.console-branch }}' export-variables: true keys-case: upper log-variables: true #---------------------------------------------- - # install dependencies + # ----- Setup python ----- + #---------------------------------------------- + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + #---------------------------------------------- + # ----- install & configure poetry ----- + #---------------------------------------------- + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: .venv + + #---------------------------------------------- + # install python dependencies #---------------------------------------------- - name: Install dependencies - working-directory: ./console-test run: poetry install --no-interaction --no-root #---------------------------------------------- - # find vega binaries path - #---------------------------------------------- - - name: Find vega binaries path - id: vega_bin_path - working-directory: ./console-test - run: echo path=$(poetry run python -c "import vega_sim; print(vega_sim.vega_bin_path)") >> $GITHUB_OUTPUT - #---------------------------------------------- - # vega binaries cache - #---------------------------------------------- - - name: Vega binaries cache - uses: actions/cache@v3 - id: vega_binaries_cache - with: - path: ${{ steps.vega_bin_path.outputs.path }} - key: ${{ runner.os }}-vega-binaries-${{ env.VEGA_VERSION }} - #---------------------------------------------- - # install vega binaries + # install vega binaries #---------------------------------------------- - name: Install vega binaries - working-directory: ./console-test run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }} #---------------------------------------------- # install playwright #---------------------------------------------- - name: install playwright run: poetry run playwright install --with-deps chromium - working-directory: ./console-test #---------------------------------------------- # run tests #---------------------------------------------- - name: Run tests - working-directory: ./console-test - run: poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15 - - name: Check files - run: | - ls -al . - ls -al console-test + run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15 + #---------------------------------------------- # upload traces #---------------------------------------------- diff --git a/apps/explorer-e2e/src/integration/market.cy.js b/apps/explorer-e2e/src/integration/market.cy.js index 301749e8e..e0eeab954 100644 --- a/apps/explorer-e2e/src/integration/market.cy.js +++ b/apps/explorer-e2e/src/integration/market.cy.js @@ -36,7 +36,7 @@ context('Market page', { tags: '@regression' }, function () { it('Able to go to market details page', function () { cy.navigate_to('markets'); - cy.get_element_by_col_id('actions').eq(1).click(); + cy.contains('Test market 1').click(); cy.getByTestId(marketHeaders).should('have.text', 'Test market 1'); cy.validate_element_from_table('Name', 'Test market 1'); cy.validate_element_from_table('Market ID', this.createdMarketId); @@ -90,7 +90,7 @@ context('Market page', { tags: '@regression' }, function () { // Liquidity price range cy.validate_element_from_table( 'Liquidity Price Range', - '1,000.00% of mid price' + '95.00% of mid price' ); cy.validate_element_from_table('Lowest Price', '0.00 fUSDC'); cy.validate_element_from_table('Highest Price', '0.00 fUSDC'); diff --git a/apps/governance-e2e/src/integration/flow/proposal-details.cy.ts b/apps/governance-e2e/src/integration/flow/proposal-details.cy.ts index 0bc586370..81e4938f5 100644 --- a/apps/governance-e2e/src/integration/flow/proposal-details.cy.ts +++ b/apps/governance-e2e/src/integration/flow/proposal-details.cy.ts @@ -433,9 +433,10 @@ describe( 'contain.text', '0.3' ); - getProposalDetailsValue( - 'Minimum Probability Of Trading LP Orders' - ).should('contain.text', '1e-8'); + getProposalDetailsValue('Min Probability Of Trading LP Orders').should( + 'contain.text', + '1e-8' + ); }); it('Able to see suspended market proposal', function () { diff --git a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts index 769d7e9b7..f41bab520 100644 --- a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts +++ b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts @@ -424,7 +424,7 @@ context( validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%'); }); - it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () { + it.skip('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () { // 1002-STKE-004 stakingPageAssociateTokens('3'); verifyUnstakedBalance(3.0); @@ -438,7 +438,7 @@ context( verifyStakedBalance(7.0); }); - it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () { + it.skip('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () { // 1002-STKE-004 stakingPageAssociateTokens('3', { type: 'contract' }); verifyUnstakedBalance(3.0); @@ -452,7 +452,7 @@ context( verifyStakedBalance(7.0); }); - it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () { + it.skip('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () { // 1002-STKE-004 stakingPageAssociateTokens('3', { type: 'wallet' }); verifyUnstakedBalance(3.0); @@ -466,7 +466,7 @@ context( verifyStakedBalance(7.0); }); - it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () { + it.skip('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () { // 1002-STKE-004 stakingPageAssociateTokens('6'); verifyUnstakedBalance(6.0); diff --git a/apps/governance-e2e/src/integration/view/proposal.cy.ts b/apps/governance-e2e/src/integration/view/proposal.cy.ts index d81460944..efac2b02e 100644 --- a/apps/governance-e2e/src/integration/view/proposal.cy.ts +++ b/apps/governance-e2e/src/integration/view/proposal.cy.ts @@ -74,25 +74,12 @@ context( }); it('should be able to see a working link for - find out more about Vega governance', function () { - // 3001-VOTE-001 + // 3001-VOTE-001 // 3002-PROP-001 cy.getByTestId(proposalDocumentationLink) .should('be.visible') .and('have.text', 'Find out more about Vega governance') .and('have.attr', 'href') .and('equal', governanceDocsUrl); - - // 3002-PROP-001 - cy.request(governanceDocsUrl) - .its('body') - .then((body) => { - if (!body.includes('Govern the network')) { - assert.include( - body, - 'Govern the network', - `Checking that governance link destination includes 'Govern the network' text` - ); - } - }); }); // 3007-PNE-021 diff --git a/apps/trading-e2e/src/integration/trading-accounts.cy.ts b/apps/trading-e2e/src/integration/trading-accounts.cy.ts deleted file mode 100644 index dd9412ce6..000000000 --- a/apps/trading-e2e/src/integration/trading-accounts.cy.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { checkSorting } from '@vegaprotocol/cypress'; - -const dialogClose = 'dialog-close'; - -describe('accounts', { tags: '@smoke' }, () => { - beforeEach(() => { - cy.mockTradingPage(); - cy.mockWeb3Provider(); - cy.mockSubscription(); - cy.setVegaWallet(); - cy.visit('/#/markets/market-0'); - }); - - it('should open usage breakdown dialog when clicked on used', () => { - cy.getByTestId('Collateral').click(); - // 7001-COLL-009 - cy.get('[col-id="used"]').contains('1.01').click(); - const headers = ['Market', 'Account type', 'Balance', 'Margin health']; - cy.getByTestId('usage-breakdown').within(($headers) => { - cy.wrap($headers) - .get('.ag-header-cell-text') - .each(($header, i) => { - cy.wrap($header).should('have.text', headers[i]); - }); - }); - cy.getByTestId(dialogClose).click(); - }); - - describe('sorting by ag-grid columns should work well', () => { - before(() => { - const dialogs = Cypress.$('[data-testid="dialog-close"]:visible'); - if (dialogs.length > 0) { - dialogs.each((btn) => { - cy.wrap(btn).click(); - }); - } - cy.contains('Loading...').should('not.exist'); - }); - // 7001-COLL-010 - it('sorting by asset', () => { - cy.getByTestId('Collateral').click(); - const marketsSortedDefault = ['tBTC', 'tEURO', 'tDAI', 'tBTC']; - const marketsSortedAsc = ['tBTC', 'tBTC', 'tDAI', 'tEURO']; - const marketsSortedDesc = Array.from(marketsSortedAsc).reverse(); - checkSorting( - 'asset.symbol', - marketsSortedDefault, - marketsSortedAsc, - marketsSortedDesc - ); - }); - - it('sorting by total', () => { - cy.getByTestId('Collateral').click(); - const marketsSortedDefault = [ - '1,000.00002', - '1,000.01', - '1,000.00', - '1,000.00001', - ]; - const marketsSortedAsc = [ - '1,000.00', - '1,000.00001', - '1,000.00002', - '1,000.01', - ]; - const marketsSortedDesc = Array.from(marketsSortedAsc).reverse(); - - checkSorting( - 'total', - marketsSortedDefault, - marketsSortedAsc, - marketsSortedDesc - ); - }); - - it('sorting by used', () => { - cy.getByTestId('Collateral').click(); - // concat actual value with percentage value - // as cypress will pick up the entire cell contes - // textContent - const marketsSortedDefault = [ - '0.00' + '0.00%', - '0.01' + '0.00%', - '0.00' + '0.00%', - '0.00' + '0.00%', - ]; - const marketsSortedAsc = [ - '0.00' + '0.00%', - '0.00' + '0.00%', - '0.00' + '0.00%', - '0.01' + '0.00%', - ]; - const marketsSortedDesc = Array.from(marketsSortedAsc).reverse(); - checkSorting( - 'used', - marketsSortedDefault, - marketsSortedAsc, - marketsSortedDesc - ); - }); - - it('sorting by available', () => { - cy.getByTestId('Collateral').click(); - const marketsSortedDefault = [ - '1,000.00002', - '1,000.00', - '1,000.00', - '1,000.00001', - ]; - const marketsSortedAsc = [ - '1,000.00', - '1,000.00', - '1,000.00001', - '1,000.00002', - ]; - const marketsSortedDesc = Array.from(marketsSortedAsc).reverse(); - - checkSorting( - 'available', - marketsSortedDefault, - marketsSortedAsc, - marketsSortedDesc - ); - }); - }); -}); diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index a3b38f56f..07ea23adf 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -139,6 +139,9 @@ const MainGrid = memo( + + + { - + } + > @@ -77,6 +83,9 @@ export const Portfolio = () => { + + + diff --git a/apps/trading/components/funding-payments-container/funding-payments-container.tsx b/apps/trading/components/funding-payments-container/funding-payments-container.tsx new file mode 100644 index 000000000..aca80c192 --- /dev/null +++ b/apps/trading/components/funding-payments-container/funding-payments-container.tsx @@ -0,0 +1,46 @@ +import { useVegaWallet } from '@vegaprotocol/wallet'; +import { FundingPaymentsManager } from '@vegaprotocol/funding-payments'; +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; +import { useDataGridEvents } from '@vegaprotocol/datagrid'; +import { t } from '@vegaprotocol/i18n'; +import { Splash } from '@vegaprotocol/ui-toolkit'; +import type { DataGridSlice } from '../../stores/datagrid-store-slice'; +import { createDataGridSlice } from '../../stores/datagrid-store-slice'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; + +export const FundingPaymentsContainer = () => { + const onMarketClick = useMarketClickHandler(true); + const { pubKey } = useVegaWallet(); + + const gridStore = useFundingPaymentsStore((store) => store.gridStore); + const updateGridStore = useFundingPaymentsStore( + (store) => store.updateGridStore + ); + + const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => { + updateGridStore(colState); + }); + + if (!pubKey) { + return ( + + {t('Please connect Vega wallet')} + + ); + } + + return ( + + ); +}; + +const useFundingPaymentsStore = create()( + persist(createDataGridSlice, { + name: 'vega_funding_payments_store', + }) +); diff --git a/apps/trading/components/funding-payments-container/index.ts b/apps/trading/components/funding-payments-container/index.ts new file mode 100644 index 000000000..cf75ae8e1 --- /dev/null +++ b/apps/trading/components/funding-payments-container/index.ts @@ -0,0 +1 @@ +export * from './funding-payments-container'; diff --git a/libs/accounts/src/lib/accounts-table.spec.tsx b/libs/accounts/src/lib/accounts-table.spec.tsx index 3f5051d55..737a66864 100644 --- a/libs/accounts/src/lib/accounts-table.spec.tsx +++ b/libs/accounts/src/lib/accounts-table.spec.tsx @@ -3,6 +3,7 @@ import * as Types from '@vegaprotocol/types'; import type { AccountFields } from './accounts-data-provider'; import { getAccountData } from './accounts-data-provider'; import { AccountTable } from './accounts-table'; +import userEvent from '@testing-library/user-event'; const singleRow = { __typename: 'AccountBalance', @@ -24,6 +25,26 @@ const singleRow = { } as AccountFields; const singleRowData = [singleRow]; +const secondRow = { + __typename: 'AccountBalance', + type: Types.AccountType.ACCOUNT_TYPE_MARGIN, + balance: '125600002', + market: { + __typename: 'Market', + id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35', + }, + asset: { + __typename: 'Asset', + id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c', + symbol: 'aBTC', + decimals: 5, + }, + available: '125600001', + used: '125600001', + total: '251200002', +} as AccountFields; +const multiRowData = [singleRow, secondRow]; + describe('AccountsTable', () => { it('should render correct columns', async () => { // 7001-COLL-001 @@ -68,6 +89,25 @@ describe('AccountsTable', () => { expect(rows?.childElementCount).toBe(1); }); + it('should sort assets', async () => { + // 7001-COLL-010 + const { container } = render( + null} + isReadOnly={false} + /> + ); + + const headerCell = screen.getByText('Asset'); + await userEvent.click(headerCell); + const rows = container.querySelectorAll( + '.ag-center-cols-container .ag-row' + ); + expect(rows[0].textContent).toContain('aBTC'); + expect(rows[1].textContent).toContain('tBTC'); + }); + it('should apply correct formatting in view as user mode', async () => { const { container } = render( {children}>; +} + +export default ReactMarkdown; diff --git a/libs/funding-payments/jest.config.ts b/libs/funding-payments/jest.config.ts new file mode 100644 index 000000000..f93cbd35a --- /dev/null +++ b/libs/funding-payments/jest.config.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +export default { + displayName: 'funding-payments', + preset: '../../jest.preset.js', + globals: {}, + transform: { + '^.+\\.[tj]sx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + }, + ], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/funding-payments', + setupFilesAfterEnv: ['./src/setup-tests.ts'], +}; diff --git a/libs/funding-payments/postcss.config.js b/libs/funding-payments/postcss.config.js new file mode 100644 index 000000000..cbdd9c22c --- /dev/null +++ b/libs/funding-payments/postcss.config.js @@ -0,0 +1,10 @@ +const { join } = require('path'); + +module.exports = { + plugins: { + tailwindcss: { + config: join(__dirname, 'tailwind.config.js'), + }, + autoprefixer: {}, + }, +}; diff --git a/libs/funding-payments/project.json b/libs/funding-payments/project.json new file mode 100644 index 000000000..aed85bd67 --- /dev/null +++ b/libs/funding-payments/project.json @@ -0,0 +1,37 @@ +{ + "name": "funding-payments", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/funding-payments/src", + "projectType": "library", + "tags": [], + "targets": { + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/funding-payments/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/libs/funding-payments"], + "options": { + "jestConfig": "libs/funding-payments/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + }, + "build-spec": { + "executor": "nx:run-commands", + "outputs": [], + "options": { + "command": "yarn tsc --project ./libs/funding-payments/tsconfig.spec.json" + } + } + } +} diff --git a/libs/funding-payments/src/index.ts b/libs/funding-payments/src/index.ts new file mode 100644 index 000000000..be290080b --- /dev/null +++ b/libs/funding-payments/src/index.ts @@ -0,0 +1,3 @@ +export * from './lib/funding-payments-manager'; +export * from './lib/funding-payments-data-provider'; +export * from './lib/__generated__/FundingPayments'; diff --git a/libs/funding-payments/src/lib/FundingPayments.graphql b/libs/funding-payments/src/lib/FundingPayments.graphql new file mode 100644 index 000000000..a2e91bbaf --- /dev/null +++ b/libs/funding-payments/src/lib/FundingPayments.graphql @@ -0,0 +1,24 @@ +fragment FundingPaymentFields on FundingPayment { + marketId + partyId + fundingPeriodSeq + amount + timestamp +} + +query FundingPayments($partyId: ID!, $pagination: Pagination) { + fundingPayments(partyId: $partyId, pagination: $pagination) { + edges { + node { + ...FundingPaymentFields + } + cursor + } + pageInfo { + startCursor + endCursor + hasNextPage + hasPreviousPage + } + } +} diff --git a/libs/funding-payments/src/lib/__generated__/FundingPayments.ts b/libs/funding-payments/src/lib/__generated__/FundingPayments.ts new file mode 100644 index 000000000..3c31acf32 --- /dev/null +++ b/libs/funding-payments/src/lib/__generated__/FundingPayments.ts @@ -0,0 +1,71 @@ +import * as Types from '@vegaprotocol/types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type FundingPaymentFieldsFragment = { __typename?: 'FundingPayment', marketId: string, partyId: string, fundingPeriodSeq: number, amount?: string | null, timestamp: any }; + +export type FundingPaymentsQueryVariables = Types.Exact<{ + partyId: Types.Scalars['ID']; + pagination?: Types.InputMaybe; +}>; + + +export type FundingPaymentsQuery = { __typename?: 'Query', fundingPayments: { __typename?: 'FundingPaymentConnection', edges: Array<{ __typename?: 'FundingPaymentEdge', cursor: string, node: { __typename?: 'FundingPayment', marketId: string, partyId: string, fundingPeriodSeq: number, amount?: string | null, timestamp: any } }>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } }; + +export const FundingPaymentFieldsFragmentDoc = gql` + fragment FundingPaymentFields on FundingPayment { + marketId + partyId + fundingPeriodSeq + amount + timestamp +} + `; +export const FundingPaymentsDocument = gql` + query FundingPayments($partyId: ID!, $pagination: Pagination) { + fundingPayments(partyId: $partyId, pagination: $pagination) { + edges { + node { + ...FundingPaymentFields + } + cursor + } + pageInfo { + startCursor + endCursor + hasNextPage + hasPreviousPage + } + } +} + ${FundingPaymentFieldsFragmentDoc}`; + +/** + * __useFundingPaymentsQuery__ + * + * To run a query within a React component, call `useFundingPaymentsQuery` and pass it any options that fit your needs. + * When your component renders, `useFundingPaymentsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFundingPaymentsQuery({ + * variables: { + * partyId: // value for 'partyId' + * pagination: // value for 'pagination' + * }, + * }); + */ +export function useFundingPaymentsQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FundingPaymentsDocument, options); + } +export function useFundingPaymentsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FundingPaymentsDocument, options); + } +export type FundingPaymentsQueryHookResult = ReturnType; +export type FundingPaymentsLazyQueryHookResult = ReturnType; +export type FundingPaymentsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/funding-payments/src/lib/funding-payments-data-provider.ts b/libs/funding-payments/src/lib/funding-payments-data-provider.ts new file mode 100644 index 000000000..88a9c9f6e --- /dev/null +++ b/libs/funding-payments/src/lib/funding-payments-data-provider.ts @@ -0,0 +1,69 @@ +import type { PageInfo, Cursor } from '@vegaprotocol/data-provider'; +import { + makeDataProvider, + makeDerivedDataProvider, + defaultAppend as append, +} from '@vegaprotocol/data-provider'; +import type { Market } from '@vegaprotocol/markets'; +import { marketsMapProvider } from '@vegaprotocol/markets'; +import { FundingPaymentsDocument } from './__generated__/FundingPayments'; +import type { + FundingPaymentsQuery, + FundingPaymentsQueryVariables, + FundingPaymentFieldsFragment, +} from './__generated__/FundingPayments'; + +export type FundingPayment = Omit & { + market?: Market; +}; + +const getData = ( + responseData: FundingPaymentsQuery | null +): (FundingPaymentFieldsFragment & Cursor)[] => + responseData?.fundingPayments?.edges.map< + FundingPaymentFieldsFragment & Cursor + >((edge) => ({ + ...edge.node, + cursor: edge.cursor, + })) || []; + +const getPageInfo = ( + responseData: FundingPaymentsQuery | null +): PageInfo | null => responseData?.fundingPayments?.pageInfo || null; + +export const fundingPaymentsProvider = makeDataProvider< + Parameters['0'], + ReturnType, + never, + never, + FundingPaymentsQueryVariables +>({ + query: FundingPaymentsDocument, + getData, + pagination: { + getPageInfo, + append, + first: 100, + }, +}); + +export const fundingPaymentsWithMarketProvider = makeDerivedDataProvider< + FundingPayment[], + never, + FundingPaymentsQueryVariables +>( + [ + fundingPaymentsProvider, + (callback, client) => marketsMapProvider(callback, client, undefined), + ], + (partsData): FundingPayment[] | null => { + return ((partsData[0] as ReturnType) || []).map( + (fundingPayment) => ({ + ...fundingPayment, + market: (partsData[1] as Record)[ + fundingPayment.marketId + ], + }) + ); + } +); diff --git a/libs/funding-payments/src/lib/funding-payments-manager.tsx b/libs/funding-payments/src/lib/funding-payments-manager.tsx new file mode 100644 index 000000000..a5f20e986 --- /dev/null +++ b/libs/funding-payments/src/lib/funding-payments-manager.tsx @@ -0,0 +1,42 @@ +import type { AgGridReact } from 'ag-grid-react'; +import { useRef } from 'react'; +import { t } from '@vegaprotocol/i18n'; +import { FundingPaymentsTable } from './funding-payments-table'; +import type { useDataGridEvents } from '@vegaprotocol/datagrid'; +import { useDataProvider } from '@vegaprotocol/data-provider'; +import { fundingPaymentsWithMarketProvider } from './funding-payments-data-provider'; + +interface FundingPaymentsManagerProps { + partyId: string; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; + gridProps: ReturnType; +} + +export const FundingPaymentsManager = ({ + partyId, + onMarketClick, + gridProps, +}: FundingPaymentsManagerProps) => { + const gridRef = useRef(null); + const { data, error } = useDataProvider({ + dataProvider: fundingPaymentsWithMarketProvider, + update: ({ data }) => { + if (data?.length && gridRef.current?.api) { + gridRef.current?.api.setRowData(data); + return true; + } + return false; + }, + variables: { partyId }, + }); + + return ( + + ); +}; diff --git a/libs/funding-payments/src/lib/funding-payments-table.spec.tsx b/libs/funding-payments/src/lib/funding-payments-table.spec.tsx new file mode 100644 index 000000000..86f913e14 --- /dev/null +++ b/libs/funding-payments/src/lib/funding-payments-table.spec.tsx @@ -0,0 +1,92 @@ +import { act, render, screen } from '@testing-library/react'; +import { getDateTimeFormat } from '@vegaprotocol/utils'; +import type { PartialDeep } from 'type-fest'; +import type { FundingPayment } from './funding-payments-data-provider'; +import { FundingPaymentsTable } from './funding-payments-table'; +import { generateFundingPayment } from './test-helpers'; + +describe('FundingPaymentsTable', () => { + let defaultFundingPayment: PartialDeep; + + beforeEach(() => { + defaultFundingPayment = { + marketId: + '69abf5c456c20f4d189cea79a11dfd6b0958ead58ab34bd66f73eea48aee600c', + partyId: + '02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65', + fundingPeriodSeq: 84, + amount: '100', + timestamp: '2023-10-06T07:06:43.020994Z', + market: { + decimalPlaces: 2, + positionDecimalPlaces: 5, + tradableInstrument: { + instrument: { + code: 'test market', + product: { + __typename: 'Future', + settlementAsset: { + decimals: 2, + symbol: 'BTC', + }, + }, + }, + }, + }, + }; + }); + + it('correct columns are rendered', async () => { + await act(async () => { + render(); + }); + + const headers = screen.getAllByRole('columnheader'); + const expectedHeaders = ['Market', 'Amount', 'Date']; + expect(headers).toHaveLength(expectedHeaders.length); + expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders); + }); + + it('formats positive cells', async () => { + const fundingPayment = generateFundingPayment({ + ...defaultFundingPayment, + }); + render(); + const cells = screen.getAllByRole('gridcell'); + const expectedValues = [ + fundingPayment.market?.tradableInstrument.instrument.code || '', + '1.00 BTC', + getDateTimeFormat().format(new Date(fundingPayment.timestamp)), + ]; + cells.forEach((cell, i) => { + expect(cell).toHaveTextContent(expectedValues[i]); + }); + + const amountCell = cells.find((c) => c.getAttribute('col-id') === 'amount'); + expect( + amountCell?.querySelector('.ag-cell-value')?.firstElementChild + ).toHaveClass('text-market-green-600'); + }); + + it('formats negative cells', async () => { + const fundingPayment = generateFundingPayment({ + ...defaultFundingPayment, + amount: `-${defaultFundingPayment.amount}`, + }); + render(); + const cells = screen.getAllByRole('gridcell'); + const expectedValues = [ + fundingPayment.market?.tradableInstrument.instrument.code || '', + '-1.00 BTC', + getDateTimeFormat().format(new Date(fundingPayment.timestamp)), + ]; + cells.forEach((cell, i) => { + expect(cell).toHaveTextContent(expectedValues[i]); + }); + + const amountCell = cells.find((c) => c.getAttribute('col-id') === 'amount'); + expect( + amountCell?.querySelector('.ag-cell-value')?.firstElementChild + ).toHaveClass('text-market-red dark:text-market-red'); + }); +}); diff --git a/libs/funding-payments/src/lib/funding-payments-table.tsx b/libs/funding-payments/src/lib/funding-payments-table.tsx new file mode 100644 index 000000000..7811fe42d --- /dev/null +++ b/libs/funding-payments/src/lib/funding-payments-table.tsx @@ -0,0 +1,132 @@ +import { useMemo } from 'react'; +import type { + AgGridReact, + AgGridReactProps, + AgReactUiProps, +} from 'ag-grid-react'; +import type { ColDef } from 'ag-grid-community'; +import { + addDecimalsFormatNumber, + getDateTimeFormat, + isNumeric, + toBigNum, +} from '@vegaprotocol/utils'; +import { t } from '@vegaprotocol/i18n'; + +import { + AgGrid, + DateRangeFilter, + MarketNameCell, + negativeClassNames, + positiveClassNames, +} from '@vegaprotocol/datagrid'; +import type { + VegaValueFormatterParams, + VegaValueGetterParams, +} from '@vegaprotocol/datagrid'; +import { forwardRef } from 'react'; + +import type { FundingPayment } from './funding-payments-data-provider'; + +import { getAsset } from '@vegaprotocol/markets'; +import classNames from 'classnames'; + +const defaultColDef = { + resizable: true, + sortable: true, +}; + +export type Props = (AgGridReactProps | AgReactUiProps) & { + onMarketClick?: (marketId: string, metaKey?: boolean) => void; +}; + +const formatAmount = ({ + value, + data, +}: VegaValueFormatterParams) => { + if (!data?.market || !isNumeric(value)) { + return '-'; + } + const { symbol: assetSymbol, decimals: assetDecimals } = getAsset( + data.market + ); + const valueFormatted = addDecimalsFormatNumber(value, assetDecimals); + return `${valueFormatted} ${assetSymbol}`; +}; + +export const FundingPaymentsTable = forwardRef( + ({ onMarketClick, ...props }, ref) => { + const columnDefs = useMemo( + () => [ + { + headerName: t('Market'), + field: 'market.tradableInstrument.instrument.code', + cellRenderer: 'MarketNameCell', + filter: true, + cellRendererParams: { idPath: 'market.id', onMarketClick }, + }, + { + headerName: t('Amount'), + field: 'amount', + valueFormatter: formatAmount, + type: 'rightAligned', + filter: 'agNumberColumnFilter', + valueGetter: ({ data }: VegaValueGetterParams) => + data?.amount && data?.market + ? toBigNum(data.amount, getAsset(data.market).decimals).toNumber() + : 0, + cellRenderer: ({ data }: { data: FundingPayment }) => { + if (!data?.market || !isNumeric(data.amount)) { + return '-'; + } + const { symbol: assetSymbol, decimals: assetDecimals } = getAsset( + data.market + ); + const valueFormatted = addDecimalsFormatNumber( + data.amount, + assetDecimals + ); + return ( + <> + + {valueFormatted} + + {` ${assetSymbol}`} + > + ); + }, + }, + { + headerName: t('Date'), + field: 'timestamp', + type: 'rightAligned', + filter: DateRangeFilter, + valueFormatter: ({ + value, + }: VegaValueFormatterParams) => { + return value ? getDateTimeFormat().format(new Date(value)) : ''; + }, + }, + ], + [onMarketClick] + ); + return ( + + `${data?.marketId}-${data?.fundingPeriodSeq}` + } + components={{ MarketNameCell }} + {...props} + /> + ); + } +); diff --git a/libs/funding-payments/src/lib/funding-payments.mock.ts b/libs/funding-payments/src/lib/funding-payments.mock.ts new file mode 100644 index 000000000..91c65eeec --- /dev/null +++ b/libs/funding-payments/src/lib/funding-payments.mock.ts @@ -0,0 +1,74 @@ +import type { + FundingPaymentsQuery, + FundingPaymentFieldsFragment, +} from './__generated__/FundingPayments'; +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; + +export const fundingPaymentsQuery = ( + override?: PartialDeep, + vegaPublicKey?: string +): FundingPaymentsQuery => { + const defaultResult: FundingPaymentsQuery = { + fundingPayments: { + __typename: 'FundingPaymentConnection', + edges: fundingPayments(vegaPublicKey).map((node) => ({ + __typename: 'FundingPaymentEdge', + cursor: '3', + node, + })), + pageInfo: { + __typename: 'PageInfo', + startCursor: '1', + endCursor: '2', + hasNextPage: false, + hasPreviousPage: false, + }, + }, + }; + + return merge(defaultResult, override); +}; + +export const generateFundingPayment = ( + override?: PartialDeep +) => { + const defaultFundingPayment: FundingPaymentFieldsFragment = { + marketId: 'market-0', + partyId: 'partyId', + fundingPeriodSeq: 84, + amount: '126973', + timestamp: '2023-10-06T07:06:43.020994Z', + }; + + return merge(defaultFundingPayment, override); +}; + +const fundingPayments = ( + partyId = 'partyId' +): FundingPaymentFieldsFragment[] => [ + generateFundingPayment({ + partyId, + fundingPeriodSeq: 78, + amount: '92503', + timestamp: '2023-10-06T04:06:43.652759Z', + }), + generateFundingPayment({ + partyId, + fundingPeriodSeq: 77, + amount: '-37841', + timestamp: '2023-10-06T03:36:43.437139Z', + }), + generateFundingPayment({ + partyId, + fundingPeriodSeq: 76, + amount: '32838', + timestamp: '2023-10-06T03:06:43.430384Z', + }), + generateFundingPayment({ + partyId, + fundingPeriodSeq: 75, + amount: '-298259', + timestamp: '2023-10-06T02:36:43.51153Z', + }), +]; diff --git a/libs/funding-payments/src/lib/test-helpers.ts b/libs/funding-payments/src/lib/test-helpers.ts new file mode 100644 index 000000000..5be8e36a9 --- /dev/null +++ b/libs/funding-payments/src/lib/test-helpers.ts @@ -0,0 +1,102 @@ +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import * as Schema from '@vegaprotocol/types'; +import type { FundingPayment } from './funding-payments-data-provider'; + +const { MarketState, MarketTradingMode } = Schema; + +export const generateFundingPayment = ( + override?: PartialDeep +) => { + const defaultFundingPayment: FundingPayment = { + marketId: + '69abf5c456c20f4d189cea79a11dfd6b0958ead58ab34bd66f73eea48aee600c', + partyId: '02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65', + fundingPeriodSeq: 84, + amount: '126973', + timestamp: '2023-10-06T07:06:43.020994Z', + market: { + __typename: 'Market', + id: 'market-id', + positionDecimalPlaces: 0, + decimalPlaces: 5, + state: MarketState.STATE_ACTIVE, + tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS, + liquidityMonitoringParameters: { + triggeringRatio: '1', + }, + fees: { + __typename: 'Fees', + factors: { + __typename: 'FeeFactors', + infrastructureFee: '0.1', + liquidityFee: '0.1', + makerFee: '0.1', + }, + }, + marketTimestamps: { + __typename: 'MarketTimestamps', + open: '2005-04-02T19:37:00.000Z', + close: '2005-04-02T19:37:00.000Z', + }, + tradableInstrument: { + __typename: 'TradableInstrument', + instrument: { + __typename: 'Instrument', + id: 'instrument-id', + code: 'instrument-code', + name: 'UNIDAI Monthly (30 Jun 2022)', + metadata: { + __typename: 'InstrumentMetadata', + tags: ['tag-a'], + }, + product: { + __typename: 'Future', + settlementAsset: { + __typename: 'Asset', + id: 'asset-id', + name: 'asset-id', + symbol: 'SYM', + decimals: 18, + quantum: '1', + }, + quoteName: '', + dataSourceSpecForTradingTermination: { + __typename: 'DataSourceSpec', + id: 'oracleId', + data: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionExternal', + sourceType: { + __typename: 'DataSourceSpecConfiguration', + }, + }, + }, + }, + dataSourceSpecForSettlementData: { + __typename: 'DataSourceSpec', + id: 'oracleId', + data: { + __typename: 'DataSourceDefinition', + sourceType: { + __typename: 'DataSourceDefinitionExternal', + sourceType: { + __typename: 'DataSourceSpecConfiguration', + }, + }, + }, + }, + dataSourceSpecBinding: { + __typename: 'DataSourceSpecToFutureBinding', + tradingTerminationProperty: 'trading-termination-property', + settlementDataProperty: 'settlement-data-property', + }, + }, + }, + }, + }, + }; + + return merge(defaultFundingPayment, override); +}; diff --git a/libs/funding-payments/src/setup-tests.ts b/libs/funding-payments/src/setup-tests.ts new file mode 100644 index 000000000..68773380a --- /dev/null +++ b/libs/funding-payments/src/setup-tests.ts @@ -0,0 +1,4 @@ +import '@testing-library/jest-dom'; +import ResizeObserver from 'resize-observer-polyfill'; + +global.ResizeObserver = ResizeObserver; diff --git a/libs/funding-payments/tailwind.config.js b/libs/funding-payments/tailwind.config.js new file mode 100644 index 000000000..897f6c3cc --- /dev/null +++ b/libs/funding-payments/tailwind.config.js @@ -0,0 +1,17 @@ +const { join } = require('path'); +const { createGlobPatternsForDependencies } = require('@nx/react/tailwind'); +const theme = require('../tailwindcss-config/src/theme'); +const vegaCustomClasses = require('../tailwindcss-config/src/vega-custom-classes'); + +module.exports = { + content: [ + join(__dirname, 'src/**/*.{ts,tsx,html,mdx}'), + join(__dirname, '.storybook/preview.js'), + ...createGlobPatternsForDependencies(__dirname), + ], + darkMode: 'class', + theme: { + extend: theme, + }, + plugins: [vegaCustomClasses], +}; diff --git a/libs/funding-payments/tsconfig.json b/libs/funding-payments/tsconfig.json new file mode 100644 index 000000000..60c68a3c2 --- /dev/null +++ b/libs/funding-payments/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/libs/funding-payments/tsconfig.lib.json b/libs/funding-payments/tsconfig.lib.json new file mode 100644 index 000000000..733dfff52 --- /dev/null +++ b/libs/funding-payments/tsconfig.lib.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nx/react/typings/cssmodule.d.ts", + "../../node_modules/@nx/react/typings/image.d.ts" + ], + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.stories.ts", + "**/*.stories.js", + "**/*.stories.jsx", + "**/*.stories.tsx", + "jest.config.ts" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/funding-payments/tsconfig.spec.json b/libs/funding-payments/tsconfig.spec.json new file mode 100644 index 000000000..3da863401 --- /dev/null +++ b/libs/funding-payments/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node", "@testing-library/jest-dom"] + }, + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/libs/types/src/__generated__/types.ts b/libs/types/src/__generated__/types.ts index ad3606ce2..fc05118b9 100644 --- a/libs/types/src/__generated__/types.ts +++ b/libs/types/src/__generated__/types.ts @@ -1287,6 +1287,39 @@ export type Filter = { key: PropertyKey; }; +/** The funding payment from a perpetual market. */ +export type FundingPayment = { + __typename?: 'FundingPayment'; + /** Amount transferred */ + amount?: Maybe; + /** Sequence number of the funding period the funding payment belongs to. */ + fundingPeriodSeq: Scalars['Int']; + /** Market the funding payment applies to. */ + marketId: Scalars['ID']; + /** Party the funding payment applies to. */ + partyId: Scalars['ID']; + /** RFC3339Nano timestamp when the data point was received. */ + timestamp: Scalars['Timestamp']; +}; + +/** Connection type for funding payment */ +export type FundingPaymentConnection = { + __typename?: 'FundingPaymentConnection'; + /** List of funding payments */ + edges: Array; + /** Pagination information */ + pageInfo: PageInfo; +}; + +/** Edge type for funding payment */ +export type FundingPaymentEdge = { + __typename?: 'FundingPaymentEdge'; + /** Cursor identifying the funding payment */ + cursor: Scalars['String']; + /** The funding payment */ + node: FundingPayment; +}; + /** Details of a funding interval for a perpetual market. */ export type FundingPeriod = { __typename?: 'FundingPeriod'; @@ -4097,6 +4130,8 @@ export type Query = { coreSnapshots?: Maybe; /** Get the current referral program */ currentReferralProgram?: Maybe; + /** Get the current volume discount program */ + currentVolumeDiscountProgram?: Maybe; /** Find a deposit using its ID */ deposit?: Maybe; /** Fetch all deposits */ @@ -4128,6 +4163,8 @@ export type Query = { estimatePosition?: Maybe; /** Query for historic ethereum key rotations */ ethereumKeyRotations: EthereumKeyRotationsConnection; + /** Funding payment for perpetual markets. */ + fundingPayments: FundingPaymentConnection; /** * Funding period data points for a perpetual market. The data points within a funding period are used to calculate the * time-weighted average price (TWAP), funding rate and funding payments for each funding period. @@ -4196,6 +4233,8 @@ export type Query = { /** Get referrer fee and discount stats */ referralFeeStats?: Maybe; referralSetReferees: ReferralSetRefereeConnection; + /** Get referral set statistics */ + referralSetStats: ReferralSetStatsConnection; /** List referral sets */ referralSets: ReferralSetConnection; /** Get statistics about the Vega node */ @@ -4224,6 +4263,8 @@ export type Query = { transfer?: Maybe; /** Get a list of all transfers for a public key */ transfersConnection?: Maybe; + /** Get volume discount statistics */ + volumeDiscountStats: VolumeDiscountStatsConnection; /** Find a withdrawal using its ID */ withdrawal?: Maybe; /** Fetch all withdrawals */ @@ -4369,6 +4410,14 @@ export type QueryethereumKeyRotationsArgs = { }; +/** Queries allow a caller to read data and filter data via GraphQL. */ +export type QueryfundingPaymentsArgs = { + marketId?: InputMaybe; + pagination?: InputMaybe; + partyId: Scalars['ID']; +}; + + /** Queries allow a caller to read data and filter data via GraphQL. */ export type QueryfundingPeriodDataPointsArgs = { dateRange?: InputMaybe; @@ -4568,6 +4617,15 @@ export type QueryreferralSetRefereesArgs = { }; +/** Queries allow a caller to read data and filter data via GraphQL. */ +export type QueryreferralSetStatsArgs = { + epoch?: InputMaybe; + id: Scalars['ID']; + pagination?: InputMaybe; + partyId?: InputMaybe; +}; + + /** Queries allow a caller to read data and filter data via GraphQL. */ export type QueryreferralSetsArgs = { id?: InputMaybe; @@ -4642,6 +4700,14 @@ export type QuerytransfersConnectionArgs = { }; +/** Queries allow a caller to read data and filter data via GraphQL. */ +export type QueryvolumeDiscountStatsArgs = { + epoch?: InputMaybe; + pagination?: InputMaybe; + partyId?: InputMaybe; +}; + + /** Queries allow a caller to read data and filter data via GraphQL. */ export type QuerywithdrawalArgs = { id: Scalars['ID']; @@ -4700,18 +4766,6 @@ export type RecurringTransfer = { startEpoch: Scalars['Int']; }; -export type RefereeStats = { - __typename?: 'RefereeStats'; - /** Discount factor applied to the party. */ - discountFactor: Scalars['String']; - /** Current referee notional taker volume */ - epochNotionalTakerVolume: Scalars['String']; - /** Unique ID of the party. */ - partyId: Scalars['ID']; - /** Reward factor applied to the party. */ - rewardFactor: Scalars['String']; -}; - /** Referral program information */ export type ReferralProgram = { __typename?: 'ReferralProgram'; @@ -4742,22 +4796,10 @@ export type ReferralSet = { id: Scalars['ID']; /** Party that created the set. */ referrer: Scalars['ID']; - /** - * Referral set statistics for the latest or specific epoch. - * If provided the results can be filtered for a specific referee - */ - stats?: Maybe; /** Timestamp as RFC3339Nano when the referral set was updated. */ updatedAt: Scalars['Timestamp']; }; - -/** Data relating to a referral set. */ -export type ReferralSetstatsArgs = { - epoch?: InputMaybe; - referee?: InputMaybe; -}; - /** Connection type for retrieving cursor-based paginated referral set information */ export type ReferralSetConnection = { __typename?: 'ReferralSetConnection'; @@ -4828,14 +4870,36 @@ export type ReferralSetRefereeEdge = { export type ReferralSetStats = { __typename?: 'ReferralSetStats'; - /** Epoch at which the set's statistics are updated. */ - atEpoch?: Maybe; - /** Referees' statistics for that epoch. */ - referees_stats: Array; + /** Epoch at which the statistics are updated. */ + atEpoch: Scalars['Int']; + /** Discount factor applied to the party. */ + discountFactor: Scalars['String']; + /** Current referee notional taker volume */ + epochNotionalTakerVolume: Scalars['String']; + /** Unique ID of the party. */ + partyId: Scalars['ID']; /** Running volume for the set based on the window length of the current referral program. */ referralSetRunningNotionalTakerVolume: Scalars['String']; - /** Unique ID of the set */ - setId: Scalars['ID']; + /** Reward factor applied to the party. */ + rewardFactor: Scalars['String']; +}; + +/** Connection type for retrieving cursor-based paginated referral set statistics information */ +export type ReferralSetStatsConnection = { + __typename?: 'ReferralSetStatsConnection'; + /** The referral set statistics in this connection */ + edges: Array>; + /** The pagination information */ + pageInfo: PageInfo; +}; + +/** Edge type containing the referral set statistics and cursor information returned by a ReferralSetStatsConnection */ +export type ReferralSetStatsEdge = { + __typename?: 'ReferralSetStatsEdge'; + /** The cursor for this referral set statistics */ + cursor: Scalars['String']; + /** The referral set statistics */ + node: ReferralSetStats; }; /** Rewards generated for referrers by each of their referees */ @@ -6137,6 +6201,53 @@ export type VolumeBenefitTier = { volumeDiscountFactor: Scalars['String']; }; +/** Volume discount program information */ +export type VolumeDiscountProgram = { + __typename?: 'VolumeDiscountProgram'; + /** Defined tiers in increasing order. First element will give Tier 1, second element will give Tier 2, etc. */ + benefitTiers: Array; + /** Timestamp as Unix time in nanoseconds, after which when the current epoch ends, the programs will end and benefits will be disabled. */ + endOfProgramTimestamp: Scalars['Timestamp']; + /** Timestamp as RFC3339Nano when the program ended. If present, the current program has ended and no program is currently running. */ + endedAt?: Maybe; + /** Unique ID generated from the proposal that created this program. */ + id: Scalars['ID']; + /** Incremental version of the program. It is incremented each time the volume discount program is edited. */ + version: Scalars['Int']; + /** Number of epochs over which to evaluate parties' running volume. */ + windowLength: Scalars['Int']; +}; + +export type VolumeDiscountStats = { + __typename?: 'VolumeDiscountStats'; + /** Epoch at which the statistics are updated. */ + atEpoch: Scalars['Int']; + /** Discount factor applied to the party. */ + discountFactor: Scalars['String']; + /** Unique ID of the party. */ + partyId: Scalars['ID']; + /** Party's running volume. */ + runningVolume: Scalars['String']; +}; + +/** Connection type for retrieving cursor-based paginated volume discount statistics information */ +export type VolumeDiscountStatsConnection = { + __typename?: 'VolumeDiscountStatsConnection'; + /** The volume discount statistics in this connection */ + edges: Array>; + /** The pagination information */ + pageInfo: PageInfo; +}; + +/** Edge type containing the volume discount statistics and cursor information returned by a VolumeDiscountStatsConnection */ +export type VolumeDiscountStatsEdge = { + __typename?: 'VolumeDiscountStatsEdge'; + /** The cursor for this volume discount statistics */ + cursor: Scalars['String']; + /** The volume discount statistics */ + node: VolumeDiscountStats; +}; + export type Vote = { __typename?: 'Vote'; /** RFC3339Nano time and date when the vote reached Vega network */ diff --git a/libs/wallet/src/connect-dialog/connect-dialog.tsx b/libs/wallet/src/connect-dialog/connect-dialog.tsx index 4073006f4..d24f16612 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.tsx @@ -15,6 +15,7 @@ import { useCallback, useState } from 'react'; import type { WalletClientError } from '@vegaprotocol/wallet-client'; import { t } from '@vegaprotocol/i18n'; import type { Connectors, VegaConnector } from '../connectors'; +import { DEFAULT_SNAP_VERSION } from '../connectors'; import { DEFAULT_SNAP_ID, InjectedConnector, @@ -352,7 +353,9 @@ const ConnectorList = ({ > } onClick={() => { - requestSnap(DEFAULT_SNAP_ID); + requestSnap(DEFAULT_SNAP_ID, { + version: DEFAULT_SNAP_VERSION, + }); }} /> {snapStatus === SnapStatus.NOT_SUPPORTED ? ( diff --git a/libs/wallet/src/connectors/snap-connector.ts b/libs/wallet/src/connectors/snap-connector.ts index 861967a09..2a77da97a 100644 --- a/libs/wallet/src/connectors/snap-connector.ts +++ b/libs/wallet/src/connectors/snap-connector.ts @@ -41,6 +41,7 @@ const ethereumRequest = (args: RequestArguments): Promise => { export const LOCAL_SNAP_ID = 'local:http://localhost:8080'; export const DEFAULT_SNAP_ID = 'npm:@vegaprotocol/snap'; +export const DEFAULT_SNAP_VERSION = '0.2.0'; type GetSnapsResponse = Record; diff --git a/tsconfig.base.json b/tsconfig.base.json index ca3289ef3..32183dce5 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -28,6 +28,7 @@ "@vegaprotocol/deposits": ["libs/deposits/src/index.ts"], "@vegaprotocol/environment": ["libs/environment/src/index.ts"], "@vegaprotocol/fills": ["libs/fills/src/index.ts"], + "@vegaprotocol/funding-payments": ["libs/funding-payments/src/index.ts"], "@vegaprotocol/i18n": ["libs/i18n/src/index.ts"], "@vegaprotocol/ledger": ["libs/ledger/src/index.ts"], "@vegaprotocol/liquidity": ["libs/liquidity/src/index.ts"],
{t('Please connect Vega wallet')}