From 9ae2d6a8fa81771c61fbd4bc137ec4a148ae60a7 Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Tue, 19 Jul 2022 17:31:46 +0100 Subject: [PATCH 01/19] chore: remove rationale field from proposals query (#817) --- .../src/lib/type-policies/proposal.spec.tsx | 7 ++--- apps/token/src/lib/type-policies/proposal.ts | 2 +- .../__generated__/ProposalFields.ts | 27 ------------------- .../routes/governance/proposal-fragment.ts | 5 ---- .../proposal/__generated__/Proposal.ts | 27 ------------------- .../proposals/__generated__/Proposals.ts | 27 ------------------- .../test-helpers/generate-proposals.ts | 6 ----- 7 files changed, 3 insertions(+), 98 deletions(-) diff --git a/apps/token/src/lib/type-policies/proposal.spec.tsx b/apps/token/src/lib/type-policies/proposal.spec.tsx index 4d391e1d2..993f129da 100644 --- a/apps/token/src/lib/type-policies/proposal.spec.tsx +++ b/apps/token/src/lib/type-policies/proposal.spec.tsx @@ -75,10 +75,7 @@ it('Update network', () => { it('Freeform network', () => { const name = getProposalName({ ...proposal, - rationale: { - ...proposal.rationale, - hash: '0x0', - }, + id: 'test-id', terms: { ...proposal.terms, change: { @@ -86,7 +83,7 @@ it('Freeform network', () => { }, }, }); - expect(name).toEqual('Freeform: 0x0'); + expect(name).toEqual('Freeform: test-id'); }); it("Renders unknown proposal if it's a different proposal type", () => { diff --git a/apps/token/src/lib/type-policies/proposal.ts b/apps/token/src/lib/type-policies/proposal.ts index ef9be3db8..9e300e437 100644 --- a/apps/token/src/lib/type-policies/proposal.ts +++ b/apps/token/src/lib/type-policies/proposal.ts @@ -12,7 +12,7 @@ export function getProposalName(proposal: Proposals_proposals) { } else if (change.__typename === 'UpdateNetworkParameter') { return `Update Network: ${change.networkParameter.key}`; } else if (change.__typename === 'NewFreeform') { - return `Freeform: ${proposal.rationale.hash}`; + return `Freeform: ${proposal.id}`; } return 'Unknown Proposal'; diff --git a/apps/token/src/routes/governance/__generated__/ProposalFields.ts b/apps/token/src/routes/governance/__generated__/ProposalFields.ts index 43b7aca14..39b7423cd 100644 --- a/apps/token/src/routes/governance/__generated__/ProposalFields.ts +++ b/apps/token/src/routes/governance/__generated__/ProposalFields.ts @@ -9,29 +9,6 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol // GraphQL fragment: ProposalFields // ==================================================== -export interface ProposalFields_rationale { - __typename: "ProposalRationale"; - /** - * Link to a text file describing the proposal in depth. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - url: string | null; - /** - * Description to show a short title / something in case the link goes offline. - * This is to be between 0 and 1024 unicode characters. - * This is mandatory for all proposal. - */ - description: string; - /** - * Cryptographically secure hash (SHA3-512) of the text pointed by the `url` property - * so that viewers can check that the text hasn't been changed over time. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - hash: string | null; -} - export interface ProposalFields_party { __typename: "Party"; /** @@ -282,10 +259,6 @@ export interface ProposalFields { * Error details of the rejectionReason */ errorDetails: string | null; - /** - * Rationale behind the proposal - */ - rationale: ProposalFields_rationale; /** * Party that prepared the proposal */ diff --git a/apps/token/src/routes/governance/proposal-fragment.ts b/apps/token/src/routes/governance/proposal-fragment.ts index 885e8f4a5..be5c3a3cb 100644 --- a/apps/token/src/routes/governance/proposal-fragment.ts +++ b/apps/token/src/routes/governance/proposal-fragment.ts @@ -8,11 +8,6 @@ export const PROPOSALS_FRAGMENT = gql` datetime rejectionReason errorDetails - rationale { - url - description - hash - } party { id } diff --git a/apps/token/src/routes/governance/proposal/__generated__/Proposal.ts b/apps/token/src/routes/governance/proposal/__generated__/Proposal.ts index 1e0b92e0b..691b98602 100644 --- a/apps/token/src/routes/governance/proposal/__generated__/Proposal.ts +++ b/apps/token/src/routes/governance/proposal/__generated__/Proposal.ts @@ -9,29 +9,6 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol // GraphQL query operation: Proposal // ==================================================== -export interface Proposal_proposal_rationale { - __typename: "ProposalRationale"; - /** - * Link to a text file describing the proposal in depth. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - url: string | null; - /** - * Description to show a short title / something in case the link goes offline. - * This is to be between 0 and 1024 unicode characters. - * This is mandatory for all proposal. - */ - description: string; - /** - * Cryptographically secure hash (SHA3-512) of the text pointed by the `url` property - * so that viewers can check that the text hasn't been changed over time. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - hash: string | null; -} - export interface Proposal_proposal_party { __typename: "Party"; /** @@ -282,10 +259,6 @@ export interface Proposal_proposal { * Error details of the rejectionReason */ errorDetails: string | null; - /** - * Rationale behind the proposal - */ - rationale: Proposal_proposal_rationale; /** * Party that prepared the proposal */ diff --git a/apps/token/src/routes/governance/proposals/__generated__/Proposals.ts b/apps/token/src/routes/governance/proposals/__generated__/Proposals.ts index 6e88f9d3e..9cd318827 100644 --- a/apps/token/src/routes/governance/proposals/__generated__/Proposals.ts +++ b/apps/token/src/routes/governance/proposals/__generated__/Proposals.ts @@ -9,29 +9,6 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol // GraphQL query operation: Proposals // ==================================================== -export interface Proposals_proposals_rationale { - __typename: "ProposalRationale"; - /** - * Link to a text file describing the proposal in depth. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - url: string | null; - /** - * Description to show a short title / something in case the link goes offline. - * This is to be between 0 and 1024 unicode characters. - * This is mandatory for all proposal. - */ - description: string; - /** - * Cryptographically secure hash (SHA3-512) of the text pointed by the `url` property - * so that viewers can check that the text hasn't been changed over time. - * Optional except for FreeFrom proposal where it's mandatory. - * If set, the `url` property must be set. - */ - hash: string | null; -} - export interface Proposals_proposals_party { __typename: "Party"; /** @@ -282,10 +259,6 @@ export interface Proposals_proposals { * Error details of the rejectionReason */ errorDetails: string | null; - /** - * Rationale behind the proposal - */ - rationale: Proposals_proposals_rationale; /** * Party that prepared the proposal */ diff --git a/apps/token/src/routes/governance/test-helpers/generate-proposals.ts b/apps/token/src/routes/governance/test-helpers/generate-proposals.ts index 1dd27d9ed..d6d4a4f11 100644 --- a/apps/token/src/routes/governance/test-helpers/generate-proposals.ts +++ b/apps/token/src/routes/governance/test-helpers/generate-proposals.ts @@ -24,12 +24,6 @@ export function generateProposal( __typename: 'Party', id: faker.datatype.uuid(), }, - rationale: { - __typename: 'ProposalRationale', - hash: faker.datatype.uuid(), - url: faker.internet.url(), - description: faker.lorem.words(), - }, terms: { __typename: 'ProposalTerms', closingDatetime: From 521e607e94451b46c25388e5706a50ca1bbb49c7 Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Tue, 19 Jul 2022 18:05:33 +0000 Subject: [PATCH 02/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 28 +++++++++---------- apps/static/src/assets/stagnet1-tranches.json | 4 +-- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 5b843a1f4..0c6c2da2a 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119441.92040672521249473", + "locked_amount": "119382.70469516993885157", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60383.599023737078168034", + "locked_amount": "60325.513926894384372942", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82533.95507197524777065747004", + "locked_amount": "82454.5627725649141142465892", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25402.337340307062040828", + "locked_amount": "25377.901943141313877516", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8268.214305823120249458", + "locked_amount": "8260.260821161152236927", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12778.014605978259", + "locked_amount": "12747.49065896739075", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1687449.432596807498595158", + "locked_amount": "1686122.744514252422177514", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11323.5904706170555036496913660485", + "locked_amount": "11284.6009837345513356134259418501", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3091.34367180365285328037393778538812784", + "locked_amount": "3074.38263784246516341037188613013698623", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2617523.5951378898883575804", + "locked_amount": "2615484.93745873401778962888", "deposits": [ { "amount": "1998.95815", @@ -16812,7 +16812,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "65011.13698690449648", - "locked_amount": "14506606.9984101204011954329757304703081914", + "locked_amount": "14499415.0589910418684778076106696029075426", "deposits": [ { "amount": "16249.93", @@ -19199,7 +19199,7 @@ "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", "total_removed": "2417337.516236494402292047", - "locked_amount": "7739879.23520260267374320498368782", + "locked_amount": "7733205.51542524693708593114121041", "deposits": [ { "amount": "129284.449", @@ -23699,7 +23699,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2741169.69268825216016768844203158", + "locked_amount": "2738532.86564189792864156451589045", "deposits": [ { "amount": "552496.6455", @@ -24932,7 +24932,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "423.0179607209", - "locked_amount": "414761.57605235507102892360121764", + "locked_amount": "414438.5390896286401653596347032", "deposits": [ { "amount": "3000", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index 14cb381de..e9237c046 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.63588280060883", + "locked_amount": "35.56748477929985", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "254.756665398274974", + "locked_amount": "254.004287163876194", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 9b6249a5b..bdfb6b9c8 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "233.91293822932520340023159696854388634", + "locked_amount": "233.22211821410450540023091298833079654", "deposits": [ { "amount": "1000", From c3438073b3be903cd64fadc3f14d7d67ca1d4c9f Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Wed, 20 Jul 2022 00:09:16 +0000 Subject: [PATCH 03/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 82 +++++++++++++------ apps/static/src/assets/stagnet1-tranches.json | 4 +- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 0c6c2da2a..504621515 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119382.70469516993885157", + "locked_amount": "119322.78609391473790485", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60325.513926894384372942", + "locked_amount": "60266.739360733223301396", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82454.5627725649141142465892", + "locked_amount": "82374.22808762814133361645052", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25377.901943141313877516", + "locked_amount": "25353.176498139779427496", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8260.260821161152236927", + "locked_amount": "8252.212928743191758896", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12747.49065896739075", + "locked_amount": "12716.604393115941", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1686122.744514252422177514", + "locked_amount": "1684780.30866199638305163", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11284.6009837345513356134259418501", + "locked_amount": "11245.148692167026707245568220196", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3074.38263784246516341037188613013698623", + "locked_amount": "3057.22027663622553375036981012176560125", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2615484.93745873401778962888", + "locked_amount": "2613422.08094033893583853369", "deposits": [ { "amount": "1998.95815", @@ -16811,8 +16811,8 @@ "tranche_start": "2022-06-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", - "total_removed": "65011.13698690449648", - "locked_amount": "14499415.0589910418684778076106696029075426", + "total_removed": "65238.81558128788608", + "locked_amount": "14492137.751347764039548326808819568640173", "deposits": [ { "amount": "16249.93", @@ -17386,6 +17386,11 @@ "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "tx": "0xa81f6d3ae2ad8a5ef7ef669952bae09fd41bff79c920749a711353a9fc36fad9" }, + { + "amount": "227.6785943833896", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tx": "0xe11650c233bf405e04b6087b25afcb96ef70e476200328345729f87af5c718eb" + }, { "amount": "579.636872035866225", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -17618,6 +17623,12 @@ "tranche_id": 2, "tx": "0xa81f6d3ae2ad8a5ef7ef669952bae09fd41bff79c920749a711353a9fc36fad9" }, + { + "amount": "227.6785943833896", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tranche_id": 2, + "tx": "0xe11650c233bf405e04b6087b25afcb96ef70e476200328345729f87af5c718eb" + }, { "amount": "579.636872035866225", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -17758,8 +17769,8 @@ } ], "total_tokens": "194999.1675", - "withdrawn_tokens": "15773.5485219938148", - "remaining_tokens": "179225.6189780061852" + "withdrawn_tokens": "16001.2271163772044", + "remaining_tokens": "178997.9403836227956" }, { "address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c", @@ -19198,8 +19209,8 @@ "tranche_start": "2021-11-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", - "total_removed": "2417337.516236494402292047", - "locked_amount": "7733205.51542524693708593114121041", + "total_removed": "2417759.129125450953301297", + "locked_amount": "7726452.57868387251620244877285233", "deposits": [ { "amount": "129284.449", @@ -19518,6 +19529,11 @@ "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "tx": "0xb6a67f32be39bfdb9ada605c4a9ad940eca7d8a1c0573733f987ad2ebad4ffd6" }, + { + "amount": "421.61288895655100925", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0xcb6935b6ef6fc175efbeda721ef7b1a443763afd5099a5a274a4bc97a27b74aa" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -21288,6 +21304,12 @@ "tranche_id": 3, "tx": "0xb6a67f32be39bfdb9ada605c4a9ad940eca7d8a1c0573733f987ad2ebad4ffd6" }, + { + "amount": "421.61288895655100925", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0xcb6935b6ef6fc175efbeda721ef7b1a443763afd5099a5a274a4bc97a27b74aa" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -22634,8 +22656,8 @@ } ], "total_tokens": "359123.469575", - "withdrawn_tokens": "168594.53928791585959575", - "remaining_tokens": "190528.93028708414040425" + "withdrawn_tokens": "169016.152176872410605", + "remaining_tokens": "190107.317398127589395" }, { "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", @@ -23699,7 +23721,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2738532.86564189792864156451589045", + "locked_amount": "2735864.739493958648587268538806755", "deposits": [ { "amount": "552496.6455", @@ -24932,7 +24954,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "423.0179607209", - "locked_amount": "414438.5390896286401653596347032", + "locked_amount": "414111.66768249837405240675088788", "deposits": [ { "amount": "3000", @@ -50742,7 +50764,7 @@ "tranche_start": "2021-12-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z", "total_added": "171288.42", - "total_removed": "39426.8566666082989", + "total_removed": "39676.8566666082989", "locked_amount": "0", "deposits": [ { @@ -55097,6 +55119,11 @@ "user": "0x075A6AF774C9Ef7315879231730916ceD13e84f8", "tx": "0xcb7f5ec8fed46ee245bd944823e3e314d6f7689d960e5971ebd5ade821313418" }, + { + "amount": "250", + "user": "0xED380970F4f0746C56C033f00507634a985ec330", + "tx": "0xe1eedaec4d14343fccc7a145fc5d26014dbd7eb6e9dd41c4c6c454b0dfff9c13" + }, { "amount": "60.4448387275", "user": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0", @@ -69726,10 +69753,17 @@ "tx": "0xb59405747c8088945a412703637a7b422f3639439ec2ee15e180c0a2a0d71ee4" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0xED380970F4f0746C56C033f00507634a985ec330", + "tranche_id": 6, + "tx": "0xe1eedaec4d14343fccc7a145fc5d26014dbd7eb6e9dd41c4c6c454b0dfff9c13" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xe8F33102aDD808E841268E4161326C76A3D31d24", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index e9237c046..056d94c14 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.56748477929985", + "locked_amount": "35.49827181633688", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "254.004287163876194", + "locked_amount": "253.242944571283623", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index bdfb6b9c8..8a98cf4aa 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "233.22211821410450540023091298833079654", + "locked_amount": "232.52306728817859930023022085870116693", "deposits": [ { "amount": "1000", From c0cdbe49d6b40e92e6678c94355b90227cb336fe Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Wed, 20 Jul 2022 06:04:29 +0000 Subject: [PATCH 04/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 28 +++++++++---------- apps/static/src/assets/stagnet1-tranches.json | 4 +-- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 504621515..edfb3c740 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119322.78609391473790485", + "locked_amount": "119264.267780733605195715", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60266.739360733223301396", + "locked_amount": "60209.33834672998167693", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82374.22808762814133361645052", + "locked_amount": "82295.77081135735338824657796", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25353.176498139779427496", + "locked_amount": "25329.028882811106818156", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8252.212928743191758896", + "locked_amount": "8244.353114276620049384", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12716.604393115941", + "locked_amount": "12686.43993432971175", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1684780.30866199638305163", + "locked_amount": "1683469.24531969103196263", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11245.148692167026707245568220196", + "locked_amount": "11206.618394307949220156794068504", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3057.22027663622553375036981012176560125", + "locked_amount": "3040.45899705098947977036778262937595131", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2613422.08094033893583853369", + "locked_amount": "2611407.43304698979013859112", "deposits": [ { "amount": "1998.95815", @@ -16812,7 +16812,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "65238.81558128788608", - "locked_amount": "14492137.751347764039548326808819568640173", + "locked_amount": "14485030.5132136325552785801455917158650987", "deposits": [ { "amount": "16249.93", @@ -19210,7 +19210,7 @@ "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", "total_removed": "2417759.129125450953301297", - "locked_amount": "7726452.57868387251620244877285233", + "locked_amount": "7719857.45698800157022459762577318", "deposits": [ { "amount": "129284.449", @@ -23721,7 +23721,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2735864.739493958648587268538806755", + "locked_amount": "2733258.967024957170888122225820462", "deposits": [ { "amount": "552496.6455", @@ -24954,7 +24954,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "423.0179607209", - "locked_amount": "414111.66768249837405240675088788", + "locked_amount": "413792.43520757895244730106950784", "deposits": [ { "amount": "3000", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index 056d94c14..1781cde27 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.49827181633688", + "locked_amount": "35.43068873668189", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "253.242944571283623", + "locked_amount": "252.499530695078645", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 8a98cf4aa..5b7ac96e8 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "232.52306728817859930023022085870116693", + "locked_amount": "231.84047818366311950022954502790461695", "deposits": [ { "amount": "1000", From 4fe3c916be673fb65cf80480f5a8c3610c92fcfe Mon Sep 17 00:00:00 2001 From: Joe Tsang <30622993+jtsang586@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:01:12 +0100 Subject: [PATCH 05/19] Chore/checkout capsule using tags (#822) * chore: update yaml files * fix: install binaries from Vega develop * fix: add to manual flow --- .../workflows/capsule-cypress-manual-trigger.yml | 14 +++++++++++++- .github/workflows/capsule-cypress-nightly.yml | 14 +++++++++++++- .github/workflows/capsule-cypress.yml | 14 +++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/workflows/capsule-cypress-manual-trigger.yml b/.github/workflows/capsule-cypress-manual-trigger.yml index 92dcd604a..feacec4c4 100644 --- a/.github/workflows/capsule-cypress-manual-trigger.yml +++ b/.github/workflows/capsule-cypress-manual-trigger.yml @@ -52,7 +52,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: main + ref: v0.2.0 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' @@ -72,6 +72,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} GOBIN: ${{ env.GOBIN }} + - name: Checkout Vega + uses: actions/checkout@v2 + with: + repository: vegaprotocol/vega + ref: develop + token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} + path: './vega' + + - name: Install binary from Vega repo + run: go install ./cmd/vega + working-directory: vega + ###### ## Start capsule ###### diff --git a/.github/workflows/capsule-cypress-nightly.yml b/.github/workflows/capsule-cypress-nightly.yml index cbac25a77..a810de612 100644 --- a/.github/workflows/capsule-cypress-nightly.yml +++ b/.github/workflows/capsule-cypress-nightly.yml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: main + ref: v0.2.0 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' @@ -56,6 +56,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} GOBIN: ${{ env.GOBIN }} + - name: Checkout Vega + uses: actions/checkout@v2 + with: + repository: vegaprotocol/vega + ref: develop + token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} + path: './vega' + + - name: Install binary from Vega repo + run: go install ./cmd/vega + working-directory: vega + ###### ## Start capsule ###### diff --git a/.github/workflows/capsule-cypress.yml b/.github/workflows/capsule-cypress.yml index 91c10796c..d2af41c37 100644 --- a/.github/workflows/capsule-cypress.yml +++ b/.github/workflows/capsule-cypress.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: main + ref: v0.2.0 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' @@ -58,6 +58,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} GOBIN: ${{ env.GOBIN }} + - name: Checkout Vega + uses: actions/checkout@v2 + with: + repository: vegaprotocol/vega + ref: develop + token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} + path: './vega' + + - name: Install binary from Vega repo + run: go install ./cmd/vega + working-directory: vega + ###### ## Start capsule ###### From 556be89bfd535455306baf9b4f78303963cfe324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Wed, 20 Jul 2022 11:37:28 +0200 Subject: [PATCH 06/19] Feature/218 trades scrolling (#740) * feat(#218): add skip pagination support to data provider * feat(#218): add new rows after user scroll top * feat(#218): add pagination/scroll scenarios to storybook * feat(#218): switch fills to infinite scroll mode * feat(#218): switch trades to infinite scroll mode * feat(#218): fix e2e tests * feat(#218): set rowModelType to infinite * feat(#218): fix markets-list * feat(#218): remove totalCount * feat(#218): remove totalCount from mocks, revert cypress config * feat(#218): allow null data in data-provider whan using pagination * feat(#218): add missing hasNextPage, handle only one page scenario * feat(#218): improve typing in fills, trades ann market-list tables --- .../src/support/mocks/generate-fills.ts | 3 +- .../src/support/mocks/generate-trades.ts | 27 +- libs/fills/src/lib/__generated__/Fills.ts | 6 +- libs/fills/src/lib/fills-data-provider.ts | 63 +-- libs/fills/src/lib/fills-manager.tsx | 78 ++- libs/fills/src/lib/fills-table.spec.tsx | 35 +- libs/fills/src/lib/fills-table.stories.tsx | 393 +++++++++++++++- libs/fills/src/lib/fills-table.tsx | 83 +++- libs/fills/src/lib/test-helpers.ts | 12 +- .../__generated__/MarketDataFields.ts | 46 -- .../components/__generated__/MarketDataSub.ts | 53 --- .../lib/components/__generated__/Markets.ts | 126 ----- libs/market-list/src/lib/components/index.ts | 1 - .../__generated__/index.ts | 1 + .../lib/components/markets-container/index.ts | 2 +- .../markets-container/market-list-table.tsx | 196 ++++---- .../markets-container/markets-container.tsx | 8 +- .../markets-data-provider.ts | 7 +- .../src/hooks/use-data-provider.ts | 6 +- .../src/lib/generic-data-provider.spec.ts | 445 ++++++++++++++++++ .../src/lib/generic-data-provider.ts | 141 ++++-- libs/trades/src/lib/__generated__/Trades.ts | 41 +- libs/trades/src/lib/trades-container.tsx | 161 +++++-- libs/trades/src/lib/trades-data-provider.ts | 85 ++-- libs/trades/src/lib/trades-table.spec.tsx | 6 +- libs/trades/src/lib/trades-table.tsx | 126 ++--- package.json | 2 +- 27 files changed, 1534 insertions(+), 619 deletions(-) delete mode 100644 libs/market-list/src/lib/components/__generated__/MarketDataFields.ts delete mode 100644 libs/market-list/src/lib/components/__generated__/MarketDataSub.ts delete mode 100644 libs/market-list/src/lib/components/__generated__/Markets.ts rename libs/market-list/src/lib/components/{ => markets-container}/__generated__/index.ts (76%) create mode 100644 libs/react-helpers/src/lib/generic-data-provider.spec.ts diff --git a/apps/trading-e2e/src/support/mocks/generate-fills.ts b/apps/trading-e2e/src/support/mocks/generate-fills.ts index 6093e032e..94b1d1abc 100644 --- a/apps/trading-e2e/src/support/mocks/generate-fills.ts +++ b/apps/trading-e2e/src/support/mocks/generate-fills.ts @@ -51,7 +51,6 @@ export const generateFills = (override?: PartialDeep): Fills => { id: 'buyer-id', tradesConnection: { __typename: 'TradeConnection', - totalCount: 1, edges: fills.map((f) => { return { __typename: 'TradeEdge', @@ -63,6 +62,8 @@ export const generateFills = (override?: PartialDeep): Fills => { __typename: 'PageInfo', startCursor: '1', endCursor: '2', + hasNextPage: false, + hasPreviousPage: false, }, }, __typename: 'Party', diff --git a/apps/trading-e2e/src/support/mocks/generate-trades.ts b/apps/trading-e2e/src/support/mocks/generate-trades.ts index 0031ec090..b8546e1f5 100644 --- a/apps/trading-e2e/src/support/mocks/generate-trades.ts +++ b/apps/trading-e2e/src/support/mocks/generate-trades.ts @@ -1,9 +1,12 @@ import merge from 'lodash/merge'; import type { PartialDeep } from 'type-fest'; -import type { Trades, Trades_market_trades } from '@vegaprotocol/trades'; +import type { + Trades, + Trades_market_tradesConnection_edges_node, +} from '@vegaprotocol/trades'; export const generateTrades = (override?: PartialDeep): Trades => { - const trades: Trades_market_trades[] = [ + const trades: Trades_market_tradesConnection_edges_node[] = [ { id: 'FFFFBC80005C517A10ACF481F7E6893769471098E696D0CC407F18134044CB16', price: '17116898', @@ -44,10 +47,26 @@ export const generateTrades = (override?: PartialDeep): Trades => { __typename: 'Trade', }, ]; - const defaultResult = { + const defaultResult: Trades = { market: { id: 'market-0', - trades, + tradesConnection: { + __typename: 'TradeConnection', + edges: trades.map((node, i) => { + return { + __typename: 'TradeEdge', + node, + cursor: (i + 1).toString(), + }; + }), + pageInfo: { + __typename: 'PageInfo', + startCursor: '0', + endCursor: trades.length.toString(), + hasNextPage: false, + hasPreviousPage: false, + }, + }, __typename: 'Market', }, }; diff --git a/libs/fills/src/lib/__generated__/Fills.ts b/libs/fills/src/lib/__generated__/Fills.ts index 2d42f6d7b..a1d40b511 100644 --- a/libs/fills/src/lib/__generated__/Fills.ts +++ b/libs/fills/src/lib/__generated__/Fills.ts @@ -206,14 +206,12 @@ export interface Fills_party_tradesConnection_pageInfo { __typename: "PageInfo"; startCursor: string; endCursor: string; + hasNextPage: boolean; + hasPreviousPage: boolean; } export interface Fills_party_tradesConnection { __typename: "TradeConnection"; - /** - * The total number of trades in this connection - */ - totalCount: number; /** * The trade in this connection */ diff --git a/libs/fills/src/lib/fills-data-provider.ts b/libs/fills/src/lib/fills-data-provider.ts index 3c580e5ee..8219e36dd 100644 --- a/libs/fills/src/lib/fills-data-provider.ts +++ b/libs/fills/src/lib/fills-data-provider.ts @@ -1,11 +1,16 @@ import produce from 'immer'; +import orderBy from 'lodash/orderBy'; import { gql } from '@apollo/client'; -import { makeDataProvider } from '@vegaprotocol/react-helpers'; -import type { PageInfo, Pagination } from '@vegaprotocol/react-helpers'; +import { + makeDataProvider, + defaultAppend as append, +} from '@vegaprotocol/react-helpers'; +import type { PageInfo } from '@vegaprotocol/react-helpers'; import type { FillFields } from './__generated__/FillFields'; import type { Fills, Fills_party_tradesConnection_edges, + Fills_party_tradesConnection_edges_node, } from './__generated__/Fills'; import type { FillsSub } from './__generated__/FillsSub'; @@ -64,7 +69,6 @@ export const FILLS_QUERY = gql` party(id: $partyId) { id tradesConnection(marketId: $marketId, pagination: $pagination) { - totalCount edges { node { ...FillFields @@ -74,6 +78,8 @@ export const FILLS_QUERY = gql` pageInfo { startCursor endCursor + hasNextPage + hasPreviousPage } } } @@ -90,16 +96,24 @@ export const FILLS_SUB = gql` `; const update = ( - data: Fills_party_tradesConnection_edges[], + data: (Fills_party_tradesConnection_edges | null)[], delta: FillFields[] ) => { return produce(data, (draft) => { - delta.forEach((node) => { - const index = draft.findIndex((edge) => edge.node.id === node.id); + orderBy(delta, 'createdAt').forEach((node) => { + const index = draft.findIndex((edge) => edge?.node.id === node.id); if (index !== -1) { - Object.assign(draft[index].node, node); + if (draft[index]?.node) { + Object.assign( + draft[index]?.node as Fills_party_tradesConnection_edges_node, + node + ); + } } else { - draft.unshift({ node, cursor: '', __typename: 'TradeEdge' }); + const firstNode = draft[0]?.node; + if (firstNode && node.createdAt >= firstNode.createdAt) { + draft.unshift({ node, cursor: '', __typename: 'TradeEdge' }); + } } }); }); @@ -113,40 +127,8 @@ const getData = ( const getPageInfo = (responseData: Fills): PageInfo | null => responseData.party?.tradesConnection.pageInfo || null; -const getTotalCount = (responseData: Fills): number | undefined => - responseData.party?.tradesConnection.totalCount; - const getDelta = (subscriptionData: FillsSub) => subscriptionData.trades || []; -const append = ( - data: Fills_party_tradesConnection_edges[] | null, - pageInfo: PageInfo, - insertionData: Fills_party_tradesConnection_edges[] | null, - insertionPageInfo: PageInfo | null, - pagination?: Pagination -) => { - if (data && insertionData && insertionPageInfo) { - if (pagination?.after) { - if (data[data.length - 1].cursor === pagination.after) { - return { - data: [...data, ...insertionData], - pageInfo: { ...pageInfo, endCursor: insertionPageInfo.endCursor }, - }; - } else { - const cursors = data.map((item) => item.cursor); - const startIndex = cursors.lastIndexOf(pagination.after); - if (startIndex !== -1) { - return { - data: [...data.slice(0, startIndex), ...insertionData], - pageInfo: { ...pageInfo, endCursor: insertionPageInfo.endCursor }, - }; - } - } - } - } - return { data, pageInfo }; -}; - export const fillsDataProvider = makeDataProvider( FILLS_QUERY, FILLS_SUB, @@ -155,7 +137,6 @@ export const fillsDataProvider = makeDataProvider( getDelta, { getPageInfo, - getTotalCount, append, first: 100, } diff --git a/libs/fills/src/lib/fills-manager.tsx b/libs/fills/src/lib/fills-manager.tsx index 380a51194..789016114 100644 --- a/libs/fills/src/lib/fills-manager.tsx +++ b/libs/fills/src/lib/fills-manager.tsx @@ -3,7 +3,11 @@ import { useCallback, useRef, useMemo } from 'react'; import { useDataProvider } from '@vegaprotocol/react-helpers'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { FillsTable } from './fills-table'; -import type { IGetRowsParams } from 'ag-grid-community'; +import type { + IGetRowsParams, + BodyScrollEvent, + BodyScrollEndEvent, +} from 'ag-grid-community'; import { fillsDataProvider as dataProvider } from './fills-data-provider'; import type { Fills_party_tradesConnection_edges } from './__generated__/Fills'; @@ -15,14 +19,46 @@ interface FillsManagerProps { export const FillsManager = ({ partyId }: FillsManagerProps) => { const gridRef = useRef(null); - const dataRef = useRef(null); + const dataRef = useRef<(Fills_party_tradesConnection_edges | null)[] | null>( + null + ); const totalCountRef = useRef(undefined); + const newRows = useRef(0); + const scrolledToTop = useRef(true); + + const addNewRows = useCallback(() => { + if (newRows.current === 0) { + return; + } + if (totalCountRef.current !== undefined) { + totalCountRef.current += newRows.current; + } + newRows.current = 0; + if (!gridRef.current?.api) { + return; + } + gridRef.current.api.refreshInfiniteCache(); + }, []); const update = useCallback( - ({ data }: { data: Fills_party_tradesConnection_edges[] }) => { + ({ + data, + delta, + }: { + data: (Fills_party_tradesConnection_edges | null)[]; + delta: FillsSub_trades[]; + }) => { if (!gridRef.current?.api) { return false; } + if (!scrolledToTop.current) { + const createdAt = dataRef.current?.[0]?.node.createdAt; + if (createdAt) { + newRows.current += delta.filter( + (trade) => trade.createdAt > createdAt + ).length; + } + } dataRef.current = data; gridRef.current.api.refreshInfiniteCache(); return true; @@ -35,7 +71,7 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => { data, totalCount, }: { - data: Fills_party_tradesConnection_edges[]; + data: (Fills_party_tradesConnection_edges | null)[]; totalCount?: number; }) => { dataRef.current = data; @@ -48,7 +84,7 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => { const variables = useMemo(() => ({ partyId }), [partyId]); const { data, error, loading, load, totalCount } = useDataProvider< - Fills_party_tradesConnection_edges[], + (Fills_party_tradesConnection_edges | null)[], FillsSub_trades[] >({ dataProvider, update, insert, variables }); totalCountRef.current = totalCount; @@ -60,15 +96,14 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => { startRow, endRow, }: IGetRowsParams) => { + startRow += newRows.current; + endRow += newRows.current; try { - if (dataRef.current && dataRef.current.length < endRow) { - await load({ - first: endRow - startRow, - after: dataRef.current[dataRef.current.length - 1].cursor, - }); + if (dataRef.current && dataRef.current.indexOf(null) < endRow) { + await load(); } const rowsThisBlock = dataRef.current - ? dataRef.current.slice(startRow, endRow).map((edge) => edge.node) + ? dataRef.current.slice(startRow, endRow).map((edge) => edge?.node) : []; let lastRow = -1; if (totalCountRef.current !== undefined) { @@ -77,6 +112,8 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => { } else if (totalCountRef.current <= endRow) { lastRow = totalCountRef.current; } + } else if (rowsThisBlock.length < endRow - startRow) { + lastRow = rowsThisBlock.length; } successCallback(rowsThisBlock, lastRow); } catch (e) { @@ -84,9 +121,26 @@ export const FillsManager = ({ partyId }: FillsManagerProps) => { } }; + const onBodyScrollEnd = (event: BodyScrollEndEvent) => { + if (event.top === 0) { + addNewRows(); + } + }; + + const onBodyScroll = (event: BodyScrollEvent) => { + scrolledToTop.current = event.top <= 0; + }; + return ( - + ); }; diff --git a/libs/fills/src/lib/fills-table.spec.tsx b/libs/fills/src/lib/fills-table.spec.tsx index 1b00c2a34..e7c66df27 100644 --- a/libs/fills/src/lib/fills-table.spec.tsx +++ b/libs/fills/src/lib/fills-table.spec.tsx @@ -4,7 +4,7 @@ import { Side } from '@vegaprotocol/types'; import type { PartialDeep } from 'type-fest'; import { FillsTable } from './fills-table'; -import { generateFill, makeGetRows } from './test-helpers'; +import { generateFill } from './test-helpers'; import type { FillFields } from './__generated__/FillFields'; const waitForGridToBeInTheDOM = () => { @@ -47,12 +47,7 @@ describe('FillsTable', () => { }); it('correct columns are rendered', async () => { - render( - - ); + render(); await waitForGridToBeInTheDOM(); await waitForDataToHaveLoaded(); @@ -84,12 +79,7 @@ describe('FillsTable', () => { }, }); - render( - - ); + render(); await waitForGridToBeInTheDOM(); await waitForDataToHaveLoaded(); @@ -126,12 +116,7 @@ describe('FillsTable', () => { }, }); - render( - - ); + render(); await waitForGridToBeInTheDOM(); await waitForDataToHaveLoaded(); @@ -163,10 +148,7 @@ describe('FillsTable', () => { }); const { rerender } = render( - + ); await waitForGridToBeInTheDOM(); await waitForDataToHaveLoaded(); @@ -184,12 +166,7 @@ describe('FillsTable', () => { aggressor: Side.Buy, }); - rerender( - - ); + rerender(); await waitForGridToBeInTheDOM(); await waitForDataToHaveLoaded(); diff --git a/libs/fills/src/lib/fills-table.stories.tsx b/libs/fills/src/lib/fills-table.stories.tsx index 49f637d26..8ae2d9ce1 100644 --- a/libs/fills/src/lib/fills-table.stories.tsx +++ b/libs/fills/src/lib/fills-table.stories.tsx @@ -1,22 +1,397 @@ import type { Story, Meta } from '@storybook/react'; -import type { FillsTableProps } from './fills-table'; +import type { Props } from './fills-table'; +import type { AgGridReact } from 'ag-grid-react'; +import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit'; +import { useCallback, useRef } from 'react'; import { FillsTable } from './fills-table'; -import { generateFills, makeGetRows } from './test-helpers'; +import { generateFills, generateFill } from './test-helpers'; +import type { Fills_party_tradesConnection_edges } from './__generated__/Fills'; +import type { FillsSub_trades } from './__generated__/FillsSub'; +import type { + IGetRowsParams, + BodyScrollEvent, + BodyScrollEndEvent, +} from 'ag-grid-community'; export default { component: FillsTable, title: 'FillsTable', } as Meta; -const Template: Story = (args) => ; - +const Template: Story = (args) => ; export const Default = Template.bind({}); + +const createdAt = new Date('2005-04-02 21:37:00').getTime(); const fills = generateFills(); Default.args = { partyId: 'party-id', - datasource: { - getRows: makeGetRows( - fills.party?.tradesConnection.edges.map((e) => e.node) || [] - ), - }, + rowData: fills.party?.tradesConnection.edges.map((e) => e.node) || [], }; + +const getData = ( + start: number, + end: number +): Fills_party_tradesConnection_edges[] => + new Array(end - start).fill(null).map((v, i) => ({ + __typename: 'TradeEdge', + node: generateFill({ + id: (start + i).toString(), + createdAt: new Date(createdAt - 1000 * (start + i)).toISOString(), + }), + cursor: (start + i).toString(), + })); + +const totalCount = 550; +const partyId = 'partyId'; + +const useDataProvider = ({ + insert, +}: { + insert: ({ + insertionData, + data, + totalCount, + }: { + insertionData: Fills_party_tradesConnection_edges[]; + data: Fills_party_tradesConnection_edges[]; + totalCount?: number; + }) => boolean; +}) => { + const data = [...getData(0, 100), ...new Array(totalCount - 100).fill(null)]; + return { + data, + error: null, + loading: false, + load: (start?: number, end?: number) => { + if (start === undefined) { + start = data.findIndex((v) => !v); + } + if (end === undefined) { + end = start + 100; + } + end = Math.min(end, totalCount); + const insertionData = getData(start, end); + data.splice(start, end - start, ...insertionData); + insert({ data, totalCount, insertionData }); + return Promise.resolve(); + }, + totalCount, + }; +}; + +interface PaginationManagerProps { + pagination: boolean; +} + +const PaginationManager = ({ pagination }: PaginationManagerProps) => { + const gridRef = useRef(null); + const dataRef = useRef(null); + const totalCountRef = useRef(undefined); + const newRows = useRef(0); + const scrolledToTop = useRef(true); + + const addNewRows = useCallback(() => { + if (newRows.current === 0) { + return; + } + if (totalCountRef.current !== undefined) { + totalCountRef.current += newRows.current; + } + newRows.current = 0; + if (!gridRef.current?.api) { + return; + } + gridRef.current.api.refreshInfiniteCache(); + }, []); + + const update = useCallback( + ({ + data, + delta, + }: { + data: Fills_party_tradesConnection_edges[]; + delta: FillsSub_trades[]; + }) => { + if (!gridRef.current?.api) { + return false; + } + if (!scrolledToTop.current) { + const createdAt = dataRef.current?.[0].node.createdAt; + if (createdAt) { + newRows.current += delta.filter( + (trade) => trade.createdAt > createdAt + ).length; + } + } + dataRef.current = data; + gridRef.current.api.refreshInfiniteCache(); + return true; + }, + [] + ); + + const insert = useCallback( + ({ + data, + totalCount, + }: { + data: Fills_party_tradesConnection_edges[]; + totalCount?: number; + }) => { + dataRef.current = data; + totalCountRef.current = totalCount; + return true; + }, + [] + ); + + const { data, error, loading, load, totalCount } = useDataProvider({ + insert, + }); + + totalCountRef.current = totalCount; + dataRef.current = data; + + const getRows = async ({ + successCallback, + failCallback, + startRow, + endRow, + }: IGetRowsParams) => { + startRow += newRows.current; + endRow += newRows.current; + try { + if ( + dataRef.current && + dataRef.current.slice(startRow, endRow).some((i) => !i) + ) { + await load(startRow, endRow); + } + const rowsThisBlock = dataRef.current + ? dataRef.current.slice(startRow, endRow).map((edge) => edge.node) + : []; + let lastRow = -1; + if (totalCountRef.current !== undefined) { + if (!totalCountRef.current) { + lastRow = 0; + } else { + lastRow = totalCountRef.current; + } + } else if (rowsThisBlock.length < endRow - startRow) { + lastRow = rowsThisBlock.length; + } + successCallback(rowsThisBlock, lastRow); + } catch (e) { + failCallback(); + } + }; + + const onBodyScrollEnd = (event: BodyScrollEndEvent) => { + if (event.top === 0) { + addNewRows(); + } + }; + + const onBodyScroll = (event: BodyScrollEvent) => { + scrolledToTop.current = event.top <= 0; + }; + + // id and onclick is needed only for mocked data + let id = 0; + const onClick = () => { + if (!dataRef.current) { + return; + } + const node = generateFill({ + id: (--id).toString(), + createdAt: new Date(createdAt - 1000 * id).toISOString(), + }); + update({ + data: [ + { cursor: '0', node, __typename: 'TradeEdge' }, + ...dataRef.current, + ], + delta: [node], + }); + }; + + return ( + <> + + + + + + ); +}; + +const PaginationTemplate: Story = (args) => ( + +); + +export const Pagination = PaginationTemplate.bind({}); +Pagination.args = { pagination: true }; + +export const PaginationScroll = PaginationTemplate.bind({}); +PaginationScroll.args = { pagination: false }; + +const InfiniteScrollManager = () => { + const gridRef = useRef(null); + const dataRef = useRef<(Fills_party_tradesConnection_edges | null)[] | null>( + null + ); + const totalCountRef = useRef(undefined); + const newRows = useRef(0); + const scrolledToTop = useRef(true); + + const addNewRows = useCallback(() => { + if (newRows.current === 0) { + return; + } + if (totalCountRef.current !== undefined) { + totalCountRef.current += newRows.current; + } + newRows.current = 0; + if (!gridRef.current?.api) { + return; + } + gridRef.current.api.refreshInfiniteCache(); + }, []); + + const update = useCallback( + ({ + data, + delta, + }: { + data: (Fills_party_tradesConnection_edges | null)[]; + delta: FillsSub_trades[]; + }) => { + if (!gridRef.current?.api) { + return false; + } + if (!scrolledToTop.current) { + const createdAt = dataRef.current?.[0]?.node.createdAt; + if (createdAt) { + newRows.current += delta.filter( + (trade) => trade.createdAt > createdAt + ).length; + } + } + dataRef.current = data; + gridRef.current.api.refreshInfiniteCache(); + return true; + }, + [] + ); + + const insert = useCallback( + ({ + data, + totalCount, + }: { + data: Fills_party_tradesConnection_edges[]; + totalCount?: number; + }) => { + dataRef.current = data; + totalCountRef.current = totalCount; + return true; + }, + [] + ); + + const { data, error, loading, load, totalCount } = useDataProvider({ + insert, + }); + totalCountRef.current = totalCount; + dataRef.current = data; + + const getRows = async ({ + successCallback, + failCallback, + startRow, + endRow, + }: IGetRowsParams) => { + startRow += newRows.current; + endRow += newRows.current; + try { + if (dataRef.current && dataRef.current.indexOf(null) < endRow) { + await load(); + } + const rowsThisBlock = dataRef.current + ? dataRef.current.slice(startRow, endRow).map((edge) => edge?.node) + : []; + let lastRow = -1; + if (totalCountRef.current !== undefined) { + if (!totalCountRef.current) { + lastRow = 0; + } else if (totalCountRef.current <= endRow) { + lastRow = totalCountRef.current; + } + } else if (rowsThisBlock.length < endRow - startRow) { + lastRow = rowsThisBlock.length; + } + successCallback(rowsThisBlock, lastRow); + } catch (e) { + failCallback(); + } + }; + + const onBodyScrollEnd = (event: BodyScrollEndEvent) => { + if (event.top === 0) { + addNewRows(); + } + }; + + const onBodyScroll = (event: BodyScrollEvent) => { + scrolledToTop.current = event.top <= 0; + }; + + // id and onclick is needed only for mocked data + let id = 0; + const onClick = () => { + if (!dataRef.current) { + return; + } + const node = generateFill({ + id: (--id).toString(), + createdAt: new Date(createdAt - 1000 * id).toISOString(), + }); + update({ + data: [ + { cursor: '0', node, __typename: 'TradeEdge' }, + ...dataRef.current, + ], + delta: [node], + }); + }; + + return ( + <> + + + + + + ); +}; + +const InfiniteScrollTemplate: Story> = () => ( + +); + +export const InfiniteScroll = InfiniteScrollTemplate.bind({}); diff --git a/libs/fills/src/lib/fills-table.tsx b/libs/fills/src/lib/fills-table.tsx index 52ad384a2..1f88da586 100644 --- a/libs/fills/src/lib/fills-table.tsx +++ b/libs/fills/src/lib/fills-table.tsx @@ -6,21 +6,32 @@ import { getDateTimeFormat, t, } from '@vegaprotocol/react-helpers'; +import { Side } from '@vegaprotocol/types'; import { AgGridColumn } from 'ag-grid-react'; import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit'; import { forwardRef } from 'react'; -import type { FillFields } from './__generated__/FillFields'; -import type { ValueFormatterParams, IDatasource } from 'ag-grid-community'; +import type { ValueFormatterParams } from 'ag-grid-community'; import BigNumber from 'bignumber.js'; -import { Side } from '@vegaprotocol/types'; +import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react'; +import type { + FillFields, + FillFields_market_tradableInstrument_instrument_product, +} from './__generated__/FillFields'; +import type { Fills_party_tradesConnection_edges_node } from './__generated__/Fills'; -export interface FillsTableProps { +export type Props = (AgGridReactProps | AgReactUiProps) & { partyId: string; - datasource: IDatasource; -} +}; -export const FillsTable = forwardRef( - ({ partyId, datasource }, ref) => { +type AccountsTableValueFormatterParams = Omit< + ValueFormatterParams, + 'data' | 'value' +> & { + data: Fills_party_tradesConnection_edges_node | null; +}; + +export const FillsTable = forwardRef( + ({ partyId, ...props }, ref) => { return ( ( defaultColDef={{ flex: 1, resizable: true }} style={{ width: '100%', height: '100%' }} getRowId={({ data }) => data?.id} - rowModelType="infinite" - datasource={datasource} + {...props} > ( { + valueFormatter={({ + value, + }: AccountsTableValueFormatterParams & { + value: Fills_party_tradesConnection_edges_node['createdAt']; + }) => { if (value === undefined) { return value; } @@ -81,9 +95,14 @@ export const FillsTable = forwardRef( } ); -const formatPrice = ({ value, data }: ValueFormatterParams) => { - if (value === undefined) { - return value; +const formatPrice = ({ + value, + data, +}: AccountsTableValueFormatterParams & { + value?: Fills_party_tradesConnection_edges_node['price']; +}) => { + if (value === undefined || !data) { + return undefined; } const asset = data?.market.tradableInstrument.instrument.product.settlementAsset.symbol; @@ -95,9 +114,14 @@ const formatPrice = ({ value, data }: ValueFormatterParams) => { }; const formatSize = (partyId: string) => { - return ({ value, data }: ValueFormatterParams) => { - if (value === undefined) { - return value; + return ({ + value, + data, + }: AccountsTableValueFormatterParams & { + value?: Fills_party_tradesConnection_edges_node['size']; + }) => { + if (value === undefined || !data) { + return undefined; } let prefix; if (data?.buyer.id === partyId) { @@ -114,9 +138,14 @@ const formatSize = (partyId: string) => { }; }; -const formatTotal = ({ value, data }: ValueFormatterParams) => { - if (value === undefined) { - return value; +const formatTotal = ({ + value, + data, +}: AccountsTableValueFormatterParams & { + value?: Fills_party_tradesConnection_edges_node['price']; +}) => { + if (value === undefined || !data) { + return undefined; } const asset = data?.market.tradableInstrument.instrument.product.settlementAsset.symbol; @@ -131,7 +160,12 @@ const formatTotal = ({ value, data }: ValueFormatterParams) => { }; const formatRole = (partyId: string) => { - return ({ value, data }: ValueFormatterParams) => { + return ({ + value, + data, + }: AccountsTableValueFormatterParams & { + value?: Fills_party_tradesConnection_edges_node['aggressor']; + }) => { if (value === undefined) { return value; } @@ -156,7 +190,12 @@ const formatRole = (partyId: string) => { }; const formatFee = (partyId: string) => { - return ({ value, data }: ValueFormatterParams) => { + return ({ + value, + data, + }: AccountsTableValueFormatterParams & { + value?: FillFields_market_tradableInstrument_instrument_product; + }) => { if (value === undefined) { return value; } diff --git a/libs/fills/src/lib/test-helpers.ts b/libs/fills/src/lib/test-helpers.ts index bafd16a9f..3862fcb9a 100644 --- a/libs/fills/src/lib/test-helpers.ts +++ b/libs/fills/src/lib/test-helpers.ts @@ -1,5 +1,4 @@ import merge from 'lodash/merge'; -import type { IGetRowsParams } from 'ag-grid-community'; import type { PartialDeep } from 'type-fest'; import type { Fills, @@ -52,7 +51,6 @@ export const generateFills = (override?: PartialDeep): Fills => { id: 'buyer-id', tradesConnection: { __typename: 'TradeConnection', - totalCount: 1, edges: fills.map((f) => { return { __typename: 'TradeEdge', @@ -64,6 +62,8 @@ export const generateFills = (override?: PartialDeep): Fills => { __typename: 'PageInfo', startCursor: '1', endCursor: '2', + hasNextPage: false, + hasPreviousPage: false, }, }, __typename: 'Party', @@ -79,7 +79,7 @@ export const generateFill = ( const defaultFill: Fills_party_tradesConnection_edges_node = { __typename: 'Trade', id: '0', - createdAt: new Date().toISOString(), + createdAt: '2005-04-02T19:37:00.000Z', price: '10000000', size: '50000', buyOrder: 'buy-order', @@ -133,9 +133,3 @@ export const generateFill = ( return merge(defaultFill, override); }; - -export const makeGetRows = - (data: Fills_party_tradesConnection_edges_node[]) => - ({ successCallback }: IGetRowsParams) => { - successCallback(data, data.length); - }; diff --git a/libs/market-list/src/lib/components/__generated__/MarketDataFields.ts b/libs/market-list/src/lib/components/__generated__/MarketDataFields.ts deleted file mode 100644 index 16906d33b..000000000 --- a/libs/market-list/src/lib/components/__generated__/MarketDataFields.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// @generated -// This file was automatically generated and should not be edited. - -import { MarketState, MarketTradingMode } from "@vegaprotocol/types"; - -// ==================================================== -// GraphQL fragment: MarketDataFields -// ==================================================== - -export interface MarketDataFields_market { - __typename: "Market"; - /** - * Market ID - */ - id: string; - /** - * Current state of the market - */ - state: MarketState; - /** - * Current mode of execution of the market - */ - tradingMode: MarketTradingMode; -} - -export interface MarketDataFields { - __typename: "MarketData"; - /** - * market id of the associated mark price - */ - market: MarketDataFields_market; - /** - * the highest price level on an order book for buy orders. - */ - bestBidPrice: string; - /** - * the lowest price level on an order book for offer orders. - */ - bestOfferPrice: string; - /** - * the mark price (actually an unsigned int) - */ - markPrice: string; -} diff --git a/libs/market-list/src/lib/components/__generated__/MarketDataSub.ts b/libs/market-list/src/lib/components/__generated__/MarketDataSub.ts deleted file mode 100644 index a74ce2159..000000000 --- a/libs/market-list/src/lib/components/__generated__/MarketDataSub.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// @generated -// This file was automatically generated and should not be edited. - -import { MarketState, MarketTradingMode } from "@vegaprotocol/types"; - -// ==================================================== -// GraphQL subscription operation: MarketDataSub -// ==================================================== - -export interface MarketDataSub_marketData_market { - __typename: "Market"; - /** - * Market ID - */ - id: string; - /** - * Current state of the market - */ - state: MarketState; - /** - * Current mode of execution of the market - */ - tradingMode: MarketTradingMode; -} - -export interface MarketDataSub_marketData { - __typename: "MarketData"; - /** - * market id of the associated mark price - */ - market: MarketDataSub_marketData_market; - /** - * the highest price level on an order book for buy orders. - */ - bestBidPrice: string; - /** - * the lowest price level on an order book for offer orders. - */ - bestOfferPrice: string; - /** - * the mark price (actually an unsigned int) - */ - markPrice: string; -} - -export interface MarketDataSub { - /** - * Subscribe to the mark price changes - */ - marketData: MarketDataSub_marketData; -} diff --git a/libs/market-list/src/lib/components/__generated__/Markets.ts b/libs/market-list/src/lib/components/__generated__/Markets.ts deleted file mode 100644 index 37cc01771..000000000 --- a/libs/market-list/src/lib/components/__generated__/Markets.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -// @generated -// This file was automatically generated and should not be edited. - -import { MarketState, MarketTradingMode } from "@vegaprotocol/types"; - -// ==================================================== -// GraphQL query operation: Markets -// ==================================================== - -export interface Markets_markets_data_market { - __typename: "Market"; - /** - * Market ID - */ - id: string; - /** - * Current state of the market - */ - state: MarketState; - /** - * Current mode of execution of the market - */ - tradingMode: MarketTradingMode; -} - -export interface Markets_markets_data { - __typename: "MarketData"; - /** - * market id of the associated mark price - */ - market: Markets_markets_data_market; - /** - * the highest price level on an order book for buy orders. - */ - bestBidPrice: string; - /** - * the lowest price level on an order book for offer orders. - */ - bestOfferPrice: string; - /** - * the mark price (actually an unsigned int) - */ - markPrice: string; -} - -export interface Markets_markets_tradableInstrument_instrument_product_settlementAsset { - __typename: "Asset"; - /** - * The symbol of the asset (e.g: GBP) - */ - symbol: string; -} - -export interface Markets_markets_tradableInstrument_instrument_product { - __typename: "Future"; - /** - * The name of the asset (string) - */ - settlementAsset: Markets_markets_tradableInstrument_instrument_product_settlementAsset; -} - -export interface Markets_markets_tradableInstrument_instrument { - __typename: "Instrument"; - /** - * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string) - */ - code: string; - /** - * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union) - */ - product: Markets_markets_tradableInstrument_instrument_product; -} - -export interface Markets_markets_tradableInstrument { - __typename: "TradableInstrument"; - /** - * An instance of or reference to a fully specified instrument. - */ - instrument: Markets_markets_tradableInstrument_instrument; -} - -export interface Markets_markets { - __typename: "Market"; - /** - * Market ID - */ - id: string; - /** - * Market full name - */ - name: string; - /** - * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct - * number denominated in the currency of the Market. (uint64) - * - * Examples: - * Currency Balance decimalPlaces Real Balance - * GBP 100 0 GBP 100 - * GBP 100 2 GBP 1.00 - * GBP 100 4 GBP 0.01 - * GBP 1 4 GBP 0.0001 ( 0.01p ) - * - * GBX (pence) 100 0 GBP 1.00 (100p ) - * GBX (pence) 100 2 GBP 0.01 ( 1p ) - * GBX (pence) 100 4 GBP 0.0001 ( 0.01p ) - * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) - */ - decimalPlaces: number; - /** - * marketData for the given market - */ - data: Markets_markets_data | null; - /** - * An instance of or reference to a tradable instrument. - */ - tradableInstrument: Markets_markets_tradableInstrument; -} - -export interface Markets { - /** - * One or more instruments that are trading on the VEGA network - */ - markets: Markets_markets[] | null; -} diff --git a/libs/market-list/src/lib/components/index.ts b/libs/market-list/src/lib/components/index.ts index 4fe60a8ed..b8ae120b2 100644 --- a/libs/market-list/src/lib/components/index.ts +++ b/libs/market-list/src/lib/components/index.ts @@ -1,3 +1,2 @@ -export * from './__generated__'; export * from './landing'; export * from './markets-container'; diff --git a/libs/market-list/src/lib/components/__generated__/index.ts b/libs/market-list/src/lib/components/markets-container/__generated__/index.ts similarity index 76% rename from libs/market-list/src/lib/components/__generated__/index.ts rename to libs/market-list/src/lib/components/markets-container/__generated__/index.ts index b169fc0c4..807f552b1 100644 --- a/libs/market-list/src/lib/components/__generated__/index.ts +++ b/libs/market-list/src/lib/components/markets-container/__generated__/index.ts @@ -1,3 +1,4 @@ export * from './MarketDataFields'; export * from './MarketDataSub'; +export * from './MarketList'; export * from './Markets'; diff --git a/libs/market-list/src/lib/components/markets-container/index.ts b/libs/market-list/src/lib/components/markets-container/index.ts index 8750972e5..a73a3fc05 100644 --- a/libs/market-list/src/lib/components/markets-container/index.ts +++ b/libs/market-list/src/lib/components/markets-container/index.ts @@ -2,4 +2,4 @@ export * from './market-list-table'; export * from './markets-container'; export * from './markets-data-provider'; export * from './summary-cell'; -export * from './__generated__/MarketList'; +export * from './__generated__'; diff --git a/libs/market-list/src/lib/components/markets-container/market-list-table.tsx b/libs/market-list/src/lib/components/markets-container/market-list-table.tsx index 45cd91a7d..7c3f7b032 100644 --- a/libs/market-list/src/lib/components/markets-container/market-list-table.tsx +++ b/libs/market-list/src/lib/components/markets-container/market-list-table.tsx @@ -1,5 +1,5 @@ import { forwardRef } from 'react'; -import type { IDatasource, ValueFormatterParams } from 'ag-grid-community'; +import type { ValueFormatterParams } from 'ag-grid-community'; import { PriceFlashCell, addDecimalsFormatNumber, @@ -8,95 +8,119 @@ import { } from '@vegaprotocol/react-helpers'; import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit'; import { AgGridColumn } from 'ag-grid-react'; -import type { AgGridReact } from 'ag-grid-react'; -import type { Markets_markets } from '../__generated__/Markets'; +import type { + AgGridReact, + AgGridReactProps, + AgReactUiProps, +} from 'ag-grid-react'; import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types'; +import type { + Markets_markets, + Markets_markets_data, +} from './__generated__/Markets'; -interface MarketListTableProps { - datasource: IDatasource; - onRowClicked: (marketId: string) => void; -} +type Props = AgGridReactProps | AgReactUiProps; -export const MarketListTable = forwardRef( - ({ datasource, onRowClicked }, ref) => { - return ( - data?.id} - ref={ref} - defaultColDef={{ - flex: 1, - resizable: true, +type MarketListTableValueFormatterParams = Omit< + ValueFormatterParams, + 'data' | 'value' +> & { + data: Markets_markets; +}; + +export const MarketListTable = forwardRef((props, ref) => { + return ( + data?.id} + ref={ref} + defaultColDef={{ + flex: 1, + resizable: true, + }} + suppressCellFocus={true} + components={{ PriceFlashCell }} + {...props} + > + + + { + if (!value) return value; + const { market, trigger } = value; + return market && + market.tradingMode === MarketTradingMode.MonitoringAuction && + trigger && + trigger !== AuctionTrigger.Unspecified + ? `${formatLabel(market.tradingMode)} - ${trigger.toLowerCase()}` + : formatLabel(market?.tradingMode); }} - suppressCellFocus={true} - onRowClicked={({ data }: { data: Markets_markets }) => - onRowClicked(data.id) + /> + + value === undefined + ? value + : addDecimalsFormatNumber(value, data.decimalPlaces) } - components={{ PriceFlashCell }} - > - - - { - if (!value) return value; - const { market, trigger } = value; - return market && - market.tradingMode === MarketTradingMode.MonitoringAuction && - trigger && - trigger !== AuctionTrigger.Unspecified - ? `${formatLabel(market.tradingMode)} - ${trigger.toLowerCase()}` - : formatLabel(market?.tradingMode); - }} - /> - - value === undefined - ? value - : addDecimalsFormatNumber(value, data.decimalPlaces) - } - /> - - value === undefined - ? value - : addDecimalsFormatNumber(value, data.decimalPlaces) - } - cellRenderer="PriceFlashCell" - /> - - value === undefined - ? value - : addDecimalsFormatNumber(value, data.decimalPlaces) - } - /> - - - ); - } -); + /> + + value === undefined + ? value + : addDecimalsFormatNumber(value, data.decimalPlaces) + } + cellRenderer="PriceFlashCell" + /> + + value === undefined + ? value + : addDecimalsFormatNumber(value, data.decimalPlaces) + } + /> + + + ); +}); export default MarketListTable; diff --git a/libs/market-list/src/lib/components/markets-container/markets-container.tsx b/libs/market-list/src/lib/components/markets-container/markets-container.tsx index d7f73d8ea..25ae64792 100644 --- a/libs/market-list/src/lib/components/markets-container/markets-container.tsx +++ b/libs/market-list/src/lib/components/markets-container/markets-container.tsx @@ -8,7 +8,7 @@ import type { IGetRowsParams } from 'ag-grid-community'; import type { Markets_markets, Markets_markets_data, -} from '../../components/__generated__/Markets'; +} from './__generated__/Markets'; import { marketsDataProvider as dataProvider } from './markets-data-provider'; import { MarketState } from '@vegaprotocol/types'; @@ -42,13 +42,15 @@ export const MarketsContainer = () => { const lastRow = dataRef.current?.length ?? -1; successCallback(rowsThisBlock, lastRow); }; - return ( push(`/markets/${id}`)} + onRowClicked={({ data }: { data: Markets_markets }) => + push(`/markets/${data.id}`) + } /> ); diff --git a/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts b/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts index 1f699308c..32121c091 100644 --- a/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts +++ b/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts @@ -3,13 +3,10 @@ import { gql } from '@apollo/client'; import type { Markets, Markets_markets, -} from '../../components/__generated__/Markets'; -import { makeDataProvider } from '@vegaprotocol/react-helpers'; - -import type { MarketDataSub, MarketDataSub_marketData, -} from '../../components/__generated__/MarketDataSub'; +} from './'; +import { makeDataProvider } from '@vegaprotocol/react-helpers'; const MARKET_DATA_FRAGMENT = gql` fragment MarketDataFields on MarketData { diff --git a/libs/react-helpers/src/hooks/use-data-provider.ts b/libs/react-helpers/src/hooks/use-data-provider.ts index 5e525ec05..236180fbc 100644 --- a/libs/react-helpers/src/hooks/use-data-provider.ts +++ b/libs/react-helpers/src/hooks/use-data-provider.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { useApolloClient } from '@apollo/client'; import type { OperationVariables } from '@apollo/client'; -import type { Subscribe, Pagination, Load } from '../lib/generic-data-provider'; +import type { Subscribe, Load } from '../lib/generic-data-provider'; /** * @@ -48,9 +48,9 @@ export function useDataProvider({ reloadRef.current(force); } }, []); - const load = useCallback((pagination: Pagination) => { + const load = useCallback>((...args) => { if (loadRef.current) { - return loadRef.current(pagination); + return loadRef.current(...args); } return Promise.reject(); }, []); diff --git a/libs/react-helpers/src/lib/generic-data-provider.spec.ts b/libs/react-helpers/src/lib/generic-data-provider.spec.ts new file mode 100644 index 000000000..0edddc797 --- /dev/null +++ b/libs/react-helpers/src/lib/generic-data-provider.spec.ts @@ -0,0 +1,445 @@ +import { makeDataProvider, defaultAppend } from './generic-data-provider'; +import type { + Query, + UpdateCallback, + Update, + PageInfo, +} from './generic-data-provider'; +import type { + ApolloClient, + FetchResult, + SubscriptionOptions, + OperationVariables, + ApolloQueryResult, + QueryOptions, +} from '@apollo/client'; +import type { Subscription, Observable } from 'zen-observable-ts'; + +type Item = { + cursor: string; + node: { + id: string; + }; +}; +type Data = Item[]; +type QueryData = { + data: Data; + pageInfo?: PageInfo; + totalCount?: number; +}; + +type SubscriptionData = QueryData; +type Delta = Data; + +describe('data provider', () => { + const update = jest.fn< + ReturnType>, + Parameters> + >(); + + const callback = jest.fn< + ReturnType>, + Parameters> + >(); + + const query: Query = { + kind: 'Document', + definitions: [], + }; + const subscriptionQuery: Query = query; + + const subscribe = makeDataProvider( + query, + subscriptionQuery, + update, + (r) => r.data, + (r) => r.data + ); + + const first = 100; + const paginatedSubscribe = makeDataProvider< + QueryData, + Data, + SubscriptionData, + Delta + >( + query, + subscriptionQuery, + update, + (r) => r.data, + (r) => r.data, + { + first, + append: defaultAppend, + getPageInfo: (r) => r?.pageInfo ?? null, + getTotalCount: (r) => r?.totalCount, + } + ); + + const generateData = (start = 0, size = first) => { + return new Array(size).fill(null).map((v, i) => ({ + cursor: (i + start + 1).toString(), + node: { + id: (i + start + 1).toString(), + }, + })); + }; + + const clientSubscribeUnsubscribe = jest.fn(); + const clientSubscribeSubscribe = jest.fn< + Subscription, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [(value: FetchResult) => void, (error: any) => void] + >(() => ({ + unsubscribe: clientSubscribeUnsubscribe, + closed: false, + })); + + const clientSubscribe = jest.fn< + Observable>, + [SubscriptionOptions] + >( + () => + ({ + subscribe: clientSubscribeSubscribe, + } as unknown as Observable>) + ); + + const clientQueryPromise: { + resolve?: ( + value: + | ApolloQueryResult + | PromiseLike> + ) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reject?: (reason?: any) => void; + } = {}; + + const clientQuery = jest.fn< + Promise>, + [QueryOptions] + >(() => { + return new Promise((resolve, reject) => { + clientQueryPromise.resolve = resolve; + clientQueryPromise.reject = reject; + }); + }); + + const client = { + query: clientQuery, + subscribe: clientSubscribe, + } as unknown as ApolloClient; + + const resolveQuery = async (data: QueryData) => { + if (clientQueryPromise.resolve) { + await clientQueryPromise.resolve({ + data, + loading: false, + networkStatus: 8, + }); + } + }; + + it('memoize instance and unsubscribe if no subscribers', () => { + const subscription1 = subscribe(jest.fn(), client); + const subscription2 = subscribe(jest.fn(), client); + expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1); + subscription1.unsubscribe(); + expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0); + subscription2.unsubscribe(); + expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(1); + }); + + it('calls callback before and after initial fetch', async () => { + callback.mockClear(); + const data: Item[] = []; + const subscription = subscribe(callback, client); + expect(callback.mock.calls.length).toBe(1); + expect(callback.mock.calls[0][0].data).toBe(null); + expect(callback.mock.calls[0][0].loading).toBe(true); + await resolveQuery({ data }); + expect(callback.mock.calls.length).toBe(2); + expect(callback.mock.calls[1][0].data).toBe(data); + expect(callback.mock.calls[1][0].loading).toBe(false); + subscription.unsubscribe(); + }); + + it('calls update and callback on each update', async () => { + const data: Item[] = []; + const subscription = subscribe(callback, client); + await resolveQuery({ data }); + const delta: Item[] = []; + update.mockImplementationOnce((data, delta) => [...data, ...delta]); + // calling onNext from client.subscribe({ query }).subscribe(onNext) + await clientSubscribeSubscribe.mock.calls[ + clientSubscribeSubscribe.mock.calls.length - 1 + ][0]({ data: { data: delta } }); + expect(update.mock.calls[update.mock.calls.length - 1][0]).toBe(data); + expect(update.mock.calls[update.mock.calls.length - 1][1]).toBe(delta); + expect(callback.mock.calls[callback.mock.calls.length - 1][0].delta).toBe( + delta + ); + subscription.unsubscribe(); + }); + + it("don't calls callback on update if data doesn't", async () => { + callback.mockClear(); + const data: Item[] = []; + const subscription = subscribe(callback, client); + await resolveQuery({ data }); + const delta: Item[] = []; + update.mockImplementationOnce((data, delta) => data); + const callbackCallsLength = callback.mock.calls.length; + // calling onNext from client.subscribe({ query }).subscribe(onNext) + await clientSubscribeSubscribe.mock.calls[ + clientSubscribeSubscribe.mock.calls.length - 1 + ][0]({ data: { data: delta } }); + expect(update.mock.calls[update.mock.calls.length - 1][0]).toBe(data); + expect(update.mock.calls[update.mock.calls.length - 1][1]).toBe(delta); + expect(callback.mock.calls.length).toBe(callbackCallsLength); + subscription.unsubscribe(); + }); + + it('refetch data on reload', async () => { + clientQuery.mockClear(); + clientSubscribeUnsubscribe.mockClear(); + clientSubscribeSubscribe.mockClear(); + const data: Item[] = []; + const subscription = subscribe(callback, client); + await resolveQuery({ data }); + subscription.reload(); + await resolveQuery({ data }); + expect(clientQuery.mock.calls.length).toBe(2); + expect(clientSubscribeSubscribe.mock.calls.length).toBe(1); + expect(clientSubscribeUnsubscribe.mock.calls.length).toBe(0); + subscription.unsubscribe(); + }); + + it('refetch data and restart subscription on reload with force', async () => { + clientQuery.mockClear(); + clientSubscribeUnsubscribe.mockClear(); + clientSubscribeSubscribe.mockClear(); + const data: Item[] = []; + const subscription = subscribe(callback, client); + await resolveQuery({ data }); + subscription.reload(true); + await resolveQuery({ data }); + expect(clientQuery.mock.calls.length).toBe(2); + expect(clientSubscribeSubscribe.mock.calls.length).toBe(2); + expect(clientSubscribeUnsubscribe.mock.calls.length).toBe(1); + subscription.unsubscribe(); + }); + + it('calls callback on flush', async () => { + callback.mockClear(); + const data: Item[] = []; + const subscription = subscribe(callback, client); + await resolveQuery({ data }); + const callbackCallsLength = callback.mock.calls.length; + subscription.flush(); + expect(callback.mock.calls.length).toBe(callbackCallsLength + 1); + subscription.unsubscribe(); + }); + + it('fills data with nulls if paginaton is enabled', async () => { + callback.mockClear(); + const totalCount = 1000; + const data: Item[] = new Array(first).fill(null).map((v, i) => ({ + cursor: i.toString(), + node: { + id: i.toString(), + }, + })); + const subscription = paginatedSubscribe(callback, client); + await resolveQuery({ + data, + totalCount, + pageInfo: { + hasNextPage: true, + }, + }); + expect(callback.mock.calls[1][0].data?.length).toBe(totalCount); + subscription.unsubscribe(); + }); + + it('loads requested data blocks and inserts data with total count', async () => { + callback.mockClear(); + const totalCount = 1000; + const subscription = paginatedSubscribe(callback, client); + await resolveQuery({ + data: generateData(), + totalCount, + pageInfo: { + hasNextPage: true, + endCursor: '100', + }, + }); + + // load next page + subscription.load(); + let lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '100', + first, + }); + await resolveQuery({ + data: generateData(100), + pageInfo: { + hasNextPage: true, + endCursor: '200', + }, + }); + + // load page with skip + subscription.load(500, 600); + lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '200', + first, + skip: 300, + }); + await resolveQuery({ + data: generateData(500), + pageInfo: { + hasNextPage: true, + endCursor: '600', + }, + }); + + // load in the gap + subscription.load(400, 500); + lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '200', + first, + skip: 200, + }); + await resolveQuery({ + data: generateData(400), + pageInfo: { + hasNextPage: true, + endCursor: '500', + }, + }); + + // load page after last block + subscription.load(700, 800); + lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '600', + first, + skip: 100, + }); + await resolveQuery({ + data: generateData(700), + pageInfo: { + hasNextPage: true, + endCursor: '800', + }, + }); + + // load last page shorter than expected + subscription.load(950, 1050); + lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '800', + first, + skip: 150, + }); + await resolveQuery({ + data: generateData(950, 20), + pageInfo: { + hasNextPage: false, + endCursor: '970', + }, + }); + let lastCallbackArgs = callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(970); + + // load next page when pageInfo.hasNextPage === false + const clientQueryCallsLength = clientQuery.mock.calls.length; + subscription.load(); + expect(clientQuery.mock.calls.length).toBe(clientQueryCallsLength); + + // load last page longer than expected + subscription.load(960, 1000); + lastQueryArgs = + clientQuery.mock.calls[clientQuery.mock.calls.length - 1][0]; + expect(lastQueryArgs?.variables?.pagination).toEqual({ + after: '960', + first, + }); + await resolveQuery({ + data: generateData(960, 40), + pageInfo: { + hasNextPage: true, + endCursor: '1000', + }, + }); + lastCallbackArgs = callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(1000); + + subscription.unsubscribe(); + }); + + it('loads requested data blocks and inserts data without totalCount', async () => { + callback.mockClear(); + const totalCount = undefined; + const subscription = paginatedSubscribe(callback, client); + await resolveQuery({ + data: generateData(), + totalCount, + pageInfo: { + hasNextPage: true, + endCursor: '100', + }, + }); + let lastCallbackArgs = callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(undefined); + + // load next page + subscription.load(); + await resolveQuery({ + data: generateData(100), + pageInfo: { + hasNextPage: true, + endCursor: '200', + }, + }); + lastCallbackArgs = callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(undefined); + + // load last page + subscription.load(); + await resolveQuery({ + data: generateData(200, 50), + pageInfo: { + hasNextPage: false, + endCursor: '250', + }, + }); + lastCallbackArgs = callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(250); + subscription.unsubscribe(); + }); + + it('sets total count when first page has no next page', async () => { + const subscription = paginatedSubscribe(callback, client); + await resolveQuery({ + data: generateData(), + pageInfo: { + hasNextPage: false, + endCursor: '100', + }, + }); + const lastCallbackArgs = + callback.mock.calls[callback.mock.calls.length - 1]; + expect(lastCallbackArgs[0].totalCount).toBe(100); + subscription.unsubscribe(); + }); +}); diff --git a/libs/react-helpers/src/lib/generic-data-provider.ts b/libs/react-helpers/src/lib/generic-data-provider.ts index d7601e257..0ea74dce1 100644 --- a/libs/react-helpers/src/lib/generic-data-provider.ts +++ b/libs/react-helpers/src/lib/generic-data-provider.ts @@ -7,6 +7,7 @@ import type { } from '@apollo/client'; import type { Subscription } from 'zen-observable-ts'; import isEqual from 'lodash/isEqual'; +import type { Pagination as PaginationWithoutSkip } from '@vegaprotocol/types'; export interface UpdateCallback { (arg: { @@ -21,15 +22,12 @@ export interface UpdateCallback { } export interface Load { - (pagination: Pagination): Promise; + (start?: number, end?: number): Promise; } -export interface Pagination { - first?: number; - after?: string; - last?: number; - before?: string; -} +type Pagination = PaginationWithoutSkip & { + skip?: number; +}; export interface PageInfo { startCursor?: string; @@ -51,7 +49,7 @@ export interface Subscribe { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -type Query = DocumentNode | TypedDocumentNode; +export type Query = DocumentNode | TypedDocumentNode; export interface Update { (data: Data, delta: Delta, reload: (forceReset?: boolean) => void): Data; @@ -60,13 +58,13 @@ export interface Update { export interface Append { ( data: Data | null, - pageInfo: PageInfo, insertionData: Data | null, insertionPageInfo: PageInfo | null, - pagination?: Pagination + pagination?: Pagination, + totalCount?: number ): { data: Data | null; - pageInfo: PageInfo; + totalCount?: number; }; } @@ -86,6 +84,46 @@ interface GetDelta { (subscriptionData: SubscriptionData): Delta; } +export function defaultAppend( + data: Data | null, + insertionData: Data | null, + insertionPageInfo: PageInfo | null, + pagination?: Pagination, + totalCount?: number +) { + if (data && insertionData && insertionPageInfo) { + if (!(data instanceof Array) || !(insertionData instanceof Array)) { + throw new Error( + 'data needs to be instance of { cursor: string }[] when using pagination' + ); + } + if (pagination?.after) { + const cursors = data.map((item) => item && item.cursor); + const startIndex = cursors.lastIndexOf(pagination.after); + if (startIndex !== -1) { + const start = startIndex + 1 + (pagination.skip ?? 0); + const end = start + insertionData.length; + let updatedData = [ + ...data.slice(0, start), + ...insertionData, + ...data.slice(end), + ]; + if (!insertionPageInfo.hasNextPage && end !== (totalCount ?? 0)) { + // adjust totalCount if last page is shorter or longer than expected + totalCount = end; + updatedData = updatedData.slice(0, end); + } + return { + data: updatedData, + // increase totalCount if last page is longer than expected + totalCount: totalCount && Math.max(updatedData.length, totalCount), + }; + } + } + } + return { data, totalCount }; +} + /** * @param subscriptionQuery query that will be used for subscription * @param update function that will be execued on each onNext, it should update data base on delta, it can reload data provider @@ -102,7 +140,7 @@ function makeDataProviderInternal( getDelta: GetDelta, pagination?: { getPageInfo: GetPageInfo; - getTotalCount: GetTotalCount; + getTotalCount?: GetTotalCount; append: Append; first: number; }, @@ -125,7 +163,7 @@ function makeDataProviderInternal( // notify single callback about current state, delta is passes optionally only if notify was invoked onNext const notify = ( callback: UpdateCallback, - dataUpdate?: { delta?: Delta; insertionData?: Data | null } + updateData?: { delta?: Delta; insertionData?: Data | null } ) => { callback({ data, @@ -133,26 +171,50 @@ function makeDataProviderInternal( loading, pageInfo, totalCount, - ...dataUpdate, + ...updateData, }); }; // notify all callbacks - const notifyAll = (dataUpdate?: { + const notifyAll = (updateData?: { delta?: Delta; insertionData?: Data | null; }) => { - callbacks.forEach((callback) => notify(callback, dataUpdate)); + callbacks.forEach((callback) => notify(callback, updateData)); }; - const load = async (params?: Pagination) => { - if (!client || !pagination || !pageInfo) { + const load = async (start?: number, end?: number) => { + if (!client || !pagination || !pageInfo || !(data instanceof Array)) { return Promise.reject(); } - const paginationVariables: Pagination = params ?? { + const paginationVariables: Pagination = { first: pagination.first, after: pageInfo.endCursor, }; + if (start !== undefined) { + if (!start) { + paginationVariables.after = undefined; + } else if (data && data[start - 1]) { + paginationVariables.after = ( + data[start - 1] as { cursor: string } + ).cursor; + } else { + let skip = 1; + while (!data[start - 1 - skip] && skip <= start) { + skip += 1; + } + paginationVariables.skip = skip; + if (skip === start) { + paginationVariables.after = undefined; + } else { + paginationVariables.after = ( + data[start - 1 - skip] as { cursor: string } + ).cursor; + } + } + } else if (!pageInfo.hasNextPage) { + return null; + } const res = await client.query({ query, variables: { @@ -162,15 +224,18 @@ function makeDataProviderInternal( fetchPolicy, }); const insertionData = getData(res.data); - const insertionDataPageInfo = pagination.getPageInfo(res.data); - ({ data, pageInfo } = pagination.append( + const insertionPageInfo = pagination.getPageInfo(res.data); + ({ data, totalCount } = pagination.append( data, - pageInfo, insertionData, - insertionDataPageInfo, - paginationVariables + insertionPageInfo, + paginationVariables, + totalCount )); - totalCount = pagination.getTotalCount(res.data); + pageInfo = insertionPageInfo; + totalCount = + (pagination.getTotalCount && pagination.getTotalCount(res.data)) ?? + totalCount; notifyAll({ insertionData }); return insertionData; }; @@ -188,9 +253,23 @@ function makeDataProviderInternal( fetchPolicy, }); data = getData(res.data); - if (pagination) { + if (data && pagination) { + if (!(data instanceof Array)) { + throw new Error( + 'data needs to be instance of { cursor: string }[] when using pagination' + ); + } pageInfo = pagination.getPageInfo(res.data); - totalCount = pagination.getTotalCount(res.data); + if (pageInfo && !pageInfo.hasNextPage) { + totalCount = data.length; + } else { + totalCount = + pagination.getTotalCount && pagination.getTotalCount(res.data); + } + + if (data && totalCount && data.length < totalCount) { + data.push(...new Array(totalCount - data.length).fill(null)); + } } // if there was some updates received from subscription during initial query loading apply them on just received data if (data && updateQueue && updateQueue.length > 0) { @@ -255,11 +334,11 @@ function makeDataProviderInternal( if (loading || !data) { updateQueue.push(delta); } else { - const newData = update(data, delta, reload); - if (newData === data) { + const updatedData = update(data, delta, reload); + if (updatedData === data) { return; } - data = newData; + data = updatedData; notifyAll({ delta }); } }, @@ -361,7 +440,7 @@ export function makeDataProvider( getDelta: GetDelta, pagination?: { getPageInfo: GetPageInfo; - getTotalCount: GetTotalCount; + getTotalCount?: GetTotalCount; append: Append; first: number; }, diff --git a/libs/trades/src/lib/__generated__/Trades.ts b/libs/trades/src/lib/__generated__/Trades.ts index e7bd4e8a8..b20eb7243 100644 --- a/libs/trades/src/lib/__generated__/Trades.ts +++ b/libs/trades/src/lib/__generated__/Trades.ts @@ -3,11 +3,13 @@ // @generated // This file was automatically generated and should not be edited. +import { Pagination } from "@vegaprotocol/types"; + // ==================================================== // GraphQL query operation: Trades // ==================================================== -export interface Trades_market_trades_market { +export interface Trades_market_tradesConnection_edges_node_market { __typename: "Market"; /** * Market ID @@ -38,7 +40,7 @@ export interface Trades_market_trades_market { positionDecimalPlaces: number; } -export interface Trades_market_trades { +export interface Trades_market_tradesConnection_edges_node { __typename: "Trade"; /** * The hash of the trade data @@ -59,7 +61,33 @@ export interface Trades_market_trades { /** * The market the trade occurred on */ - market: Trades_market_trades_market; + market: Trades_market_tradesConnection_edges_node_market; +} + +export interface Trades_market_tradesConnection_edges { + __typename: "TradeEdge"; + node: Trades_market_tradesConnection_edges_node; + cursor: string; +} + +export interface Trades_market_tradesConnection_pageInfo { + __typename: "PageInfo"; + startCursor: string; + endCursor: string; + hasNextPage: boolean; + hasPreviousPage: boolean; +} + +export interface Trades_market_tradesConnection { + __typename: "TradeConnection"; + /** + * The trade in this connection + */ + edges: Trades_market_tradesConnection_edges[]; + /** + * The pagination information + */ + pageInfo: Trades_market_tradesConnection_pageInfo; } export interface Trades_market { @@ -68,10 +96,7 @@ export interface Trades_market { * Market ID */ id: string; - /** - * Trades on a market - */ - trades: Trades_market_trades[] | null; + tradesConnection: Trades_market_tradesConnection; } export interface Trades { @@ -83,5 +108,5 @@ export interface Trades { export interface TradesVariables { marketId: string; - maxTrades: number; + pagination?: Pagination | null; } diff --git a/libs/trades/src/lib/trades-container.tsx b/libs/trades/src/lib/trades-container.tsx index 83d0a2da1..0a49b59c9 100644 --- a/libs/trades/src/lib/trades-container.tsx +++ b/libs/trades/src/lib/trades-container.tsx @@ -1,16 +1,22 @@ import { useDataProvider } from '@vegaprotocol/react-helpers'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; -import type { GridApi } from 'ag-grid-community'; import type { AgGridReact } from 'ag-grid-react'; import { useCallback, useMemo, useRef } from 'react'; +import type { + IGetRowsParams, + BodyScrollEvent, + BodyScrollEndEvent, +} from 'ag-grid-community'; import { MAX_TRADES, - sortTrades, tradesDataProvider as dataProvider, } from './trades-data-provider'; import { TradesTable } from './trades-table'; import type { TradeFields } from './__generated__/TradeFields'; -import type { TradesVariables } from './__generated__/Trades'; +import type { + TradesVariables, + Trades_market_tradesConnection_edges, +} from './__generated__/Trades'; interface TradesContainerProps { marketId: string; @@ -18,51 +24,132 @@ interface TradesContainerProps { export const TradesContainer = ({ marketId }: TradesContainerProps) => { const gridRef = useRef(null); + const dataRef = useRef< + (Trades_market_tradesConnection_edges | null)[] | null + >(null); + const totalCountRef = useRef(undefined); + const newRows = useRef(0); + const scrolledToTop = useRef(true); + const variables = useMemo( () => ({ marketId, maxTrades: MAX_TRADES }), [marketId] ); - const update = useCallback(({ delta }: { delta: TradeFields[] }) => { - if (!gridRef.current?.api) { - return false; + + const addNewRows = useCallback(() => { + if (newRows.current === 0) { + return; } - - const incoming = sortTrades(delta); - const currentRows = getAllRows(gridRef.current.api); - // Create array of trades whose index is now greater than the max so we - // can remove them from the grid - const outgoing = [...incoming, ...currentRows].filter( - (r, i) => i > MAX_TRADES - 1 - ); - - gridRef.current.api.applyTransactionAsync({ - add: incoming, - remove: outgoing, - addIndex: 0, - }); - - return true; + if (totalCountRef.current !== undefined) { + totalCountRef.current += newRows.current; + } + newRows.current = 0; + if (!gridRef.current?.api) { + return; + } + gridRef.current.api.refreshInfiniteCache(); }, []); - const { data, error, loading } = useDataProvider({ + + const update = useCallback( + ({ + data, + delta, + }: { + data: (Trades_market_tradesConnection_edges | null)[]; + delta: TradeFields[]; + }) => { + if (!gridRef.current?.api) { + return false; + } + if (!scrolledToTop.current) { + const createdAt = dataRef.current?.[0]?.node.createdAt; + if (createdAt) { + newRows.current += delta.filter( + (trade) => trade.createdAt > createdAt + ).length; + } + } + dataRef.current = data; + gridRef.current.api.refreshInfiniteCache(); + return true; + }, + [] + ); + + const insert = useCallback( + ({ + data, + totalCount, + }: { + data: (Trades_market_tradesConnection_edges | null)[]; + totalCount?: number; + }) => { + dataRef.current = data; + totalCountRef.current = totalCount; + return true; + }, + [] + ); + + const { data, error, loading, load, totalCount } = useDataProvider({ dataProvider, update, + insert, variables, }); + totalCountRef.current = totalCount; + dataRef.current = data; + + const getRows = async ({ + successCallback, + failCallback, + startRow, + endRow, + }: IGetRowsParams) => { + startRow += newRows.current; + endRow += newRows.current; + try { + if (dataRef.current && dataRef.current.indexOf(null) < endRow) { + await load(); + } + const rowsThisBlock = dataRef.current + ? dataRef.current.slice(startRow, endRow).map((edge) => edge?.node) + : []; + let lastRow = -1; + if (totalCountRef.current !== undefined) { + if (!totalCountRef.current) { + lastRow = 0; + } else if (totalCountRef.current <= endRow) { + lastRow = totalCountRef.current; + } + } else if (rowsThisBlock.length < endRow - startRow) { + lastRow = rowsThisBlock.length; + } + successCallback(rowsThisBlock, lastRow); + } catch (e) { + failCallback(); + } + }; + + const onBodyScrollEnd = (event: BodyScrollEndEvent) => { + if (event.top === 0) { + addNewRows(); + } + }; + + const onBodyScroll = (event: BodyScrollEvent) => { + scrolledToTop.current = event.top <= 0; + }; return ( - } - /> + + + ); }; - -const getAllRows = (api: GridApi) => { - const rows: TradeFields[] = []; - api.forEachNode((node) => { - rows.push(node.data); - }); - return rows; -}; diff --git a/libs/trades/src/lib/trades-data-provider.ts b/libs/trades/src/lib/trades-data-provider.ts index a2f684165..8e6e5f116 100644 --- a/libs/trades/src/lib/trades-data-provider.ts +++ b/libs/trades/src/lib/trades-data-provider.ts @@ -1,7 +1,15 @@ import { gql } from '@apollo/client'; -import { makeDataProvider } from '@vegaprotocol/react-helpers'; +import { + makeDataProvider, + defaultAppend as append, +} from '@vegaprotocol/react-helpers'; +import type { PageInfo } from '@vegaprotocol/react-helpers'; import type { TradeFields } from './__generated__/TradeFields'; -import type { Trades } from './__generated__/Trades'; +import type { + Trades, + Trades_market_tradesConnection_edges, + Trades_market_tradesConnection_edges_node, +} from './__generated__/Trades'; import type { TradesSub } from './__generated__/TradesSub'; import orderBy from 'lodash/orderBy'; import produce from 'immer'; @@ -24,11 +32,22 @@ const TRADES_FRAGMENT = gql` export const TRADES_QUERY = gql` ${TRADES_FRAGMENT} - query Trades($marketId: ID!, $maxTrades: Int!) { + query Trades($marketId: ID!, $pagination: Pagination) { market(id: $marketId) { id - trades(last: $maxTrades) { - ...TradeFields + tradesConnection(pagination: $pagination) { + edges { + node { + ...TradeFields + } + cursor + } + pageInfo { + startCursor + endCursor + hasNextPage + hasPreviousPage + } } } } @@ -43,40 +62,50 @@ export const TRADES_SUB = gql` } `; -export const sortTrades = (trades: TradeFields[]) => { - return orderBy( - trades, - (t) => { - return new Date(t.createdAt).getTime(); - }, - 'desc' - ); -}; - -const update = (data: TradeFields[], delta: TradeFields[]) => { +const update = ( + data: (Trades_market_tradesConnection_edges | null)[], + delta: TradeFields[] +) => { return produce(data, (draft) => { - const incoming = sortTrades(delta); - - // Add new trades to the top - draft.unshift(...incoming); - - // Remove old trades from the bottom - if (draft.length > MAX_TRADES) { - draft.splice(MAX_TRADES, draft.length - MAX_TRADES); - } + orderBy(delta, 'createdAt', 'desc').forEach((node) => { + const index = draft.findIndex((edge) => edge?.node.id === node.id); + if (index !== -1) { + if (draft[index]?.node) { + Object.assign( + draft[index]?.node as Trades_market_tradesConnection_edges_node, + node + ); + } + } else { + const firstNode = draft[0]?.node; + if (firstNode && node.createdAt >= firstNode.createdAt) { + draft.unshift({ node, cursor: '', __typename: 'TradeEdge' }); + } + } + }); }); }; -const getData = (responseData: Trades): TradeFields[] | null => - responseData.market ? responseData.market.trades : null; +const getData = ( + responseData: Trades +): Trades_market_tradesConnection_edges[] | null => + responseData.market ? responseData.market.tradesConnection.edges : null; const getDelta = (subscriptionData: TradesSub): TradeFields[] => subscriptionData?.trades || []; +const getPageInfo = (responseData: Trades): PageInfo | null => + responseData.market?.tradesConnection.pageInfo || null; + export const tradesDataProvider = makeDataProvider( TRADES_QUERY, TRADES_SUB, update, getData, - getDelta + getDelta, + { + getPageInfo, + append, + first: 100, + } ); diff --git a/libs/trades/src/lib/trades-table.spec.tsx b/libs/trades/src/lib/trades-table.spec.tsx index e4f566179..f27f20593 100644 --- a/libs/trades/src/lib/trades-table.spec.tsx +++ b/libs/trades/src/lib/trades-table.spec.tsx @@ -19,7 +19,7 @@ const trade: TradeFields = { it('Correct columns are rendered', async () => { await act(async () => { - render(); + render(); }); const expectedHeaders = ['Price', 'Size', 'Created at']; const headers = screen.getAllByRole('columnheader'); @@ -29,7 +29,7 @@ it('Correct columns are rendered', async () => { it('Number and data columns are formatted', async () => { await act(async () => { - render(); + render(); }); const cells = screen.getAllByRole('gridcell'); @@ -51,7 +51,7 @@ it('Price and size columns are formatted', async () => { size: (Number(trade.size) - 10).toString(), }; await act(async () => { - render(); + render(); }); const cells = screen.getAllByRole('gridcell'); diff --git a/libs/trades/src/lib/trades-table.tsx b/libs/trades/src/lib/trades-table.tsx index 24942f798..af9dbda1f 100644 --- a/libs/trades/src/lib/trades-table.tsx +++ b/libs/trades/src/lib/trades-table.tsx @@ -1,8 +1,7 @@ import type { AgGridReact } from 'ag-grid-react'; import { AgGridColumn } from 'ag-grid-react'; -import { forwardRef, useMemo } from 'react'; +import { forwardRef } from 'react'; import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit'; -import type { TradeFields } from './__generated__/TradeFields'; import { addDecimal, addDecimalsFormatNumber, @@ -10,8 +9,9 @@ import { t, } from '@vegaprotocol/react-helpers'; import type { CellClassParams, ValueFormatterParams } from 'ag-grid-community'; +import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react'; +import type { Trades_market_tradesConnection_edges_node } from './__generated__/Trades'; import BigNumber from 'bignumber.js'; -import { sortTrades } from './trades-data-provider'; export const UP_CLASS = 'text-vega-green'; export const DOWN_CLASS = 'text-vega-red'; @@ -37,58 +37,72 @@ const changeCellClass = return ['font-mono', colorClass].join(' '); }; -interface TradesTableProps { - data: TradeFields[] | null; -} +type Props = AgGridReactProps | AgReactUiProps; +type TradesTableValueFormatterParams = Omit< + ValueFormatterParams, + 'data' | 'value' +> & { + data: Trades_market_tradesConnection_edges_node | null; +}; -export const TradesTable = forwardRef( - ({ data }, ref) => { - // Sort initial trades - const trades = useMemo(() => { - if (!data) { - return null; - } - return sortTrades(data); - }, [data]); - - return ( - data.id} - ref={ref} - defaultColDef={{ - resizable: true, +export const TradesTable = forwardRef((props, ref) => { + return ( + data.id} + ref={ref} + defaultColDef={{ + resizable: true, + }} + {...props} + > + { + if (!data?.market) { + return null; + } + return addDecimalsFormatNumber(value, data.market.decimalPlaces); }} - > - { - return addDecimalsFormatNumber(value, data.market.decimalPlaces); - }} - /> - { - return addDecimal(value, data.market.positionDecimalPlaces); - }} - cellClass={changeCellClass('size')} - /> - { - return getDateTimeFormat().format(new Date(value)); - }} - /> - - ); - } -); + /> + { + if (!data?.market) { + return null; + } + return addDecimal(value, data.market.positionDecimalPlaces); + }} + cellClass={changeCellClass('size')} + /> + { + return value && getDateTimeFormat().format(new Date(value)); + }} + /> + + ); +}); diff --git a/package.json b/package.json index 3655e6b7a..adec06216 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "start": "nx serve", "build": "nx build", "test": "nx test", - "postinstall": "husky install && yarn tsc -b tools/executors/**" + "postinstall": "husky install && yarn tsc -b tools/executors/next && yarn tsc -b tools/executors/webpack" }, "engines": { "node": ">=16.14.0" From d5c8892406a5d198f62857f77202fb955d1ab24f Mon Sep 17 00:00:00 2001 From: macqbat Date: Wed, 20 Jul 2022 13:23:15 +0200 Subject: [PATCH 07/19] feat: [console-lite] - trading - direction selector (#812) * feat: [console-lite] - trading - direction selector * feat: [console-lite] - trading - direction selector - add int tests * feat: [console-lite] - trading - direction selector - fixes after review feedback * feat: [console-lite] - trading - direction selector - fix accidentaly failling int test * feat: [console-lite] - trading - direction selector - fix accidentaly failling int test Co-authored-by: maciek --- .../src/integration/market-selector.test.ts | 10 +- .../src/integration/market-trade.test.ts | 67 ++ .../src/support/mocks/generate-deal-ticket.ts | 33 + .../src/support/mocks/generate-markets.ts | 1069 +++++++++++++++++ .../deal-ticket/deal-ticket-steps.tsx | 9 +- .../components/deal-ticket/side-selector.tsx | 58 + .../simple-market-list/simple-market-list.tsx | 2 +- .../src/app/components/stepper/stepper.tsx | 1 + .../src/components/market-selector.tsx | 2 +- .../src/vega-custom-classes-lite.js | 52 + 10 files changed, 1291 insertions(+), 12 deletions(-) create mode 100644 apps/simple-trading-app-e2e/src/integration/market-trade.test.ts create mode 100644 apps/simple-trading-app-e2e/src/support/mocks/generate-deal-ticket.ts create mode 100644 apps/simple-trading-app-e2e/src/support/mocks/generate-markets.ts create mode 100644 apps/simple-trading-app/src/app/components/deal-ticket/side-selector.tsx diff --git a/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts b/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts index 333ca44b2..acbd9e8a0 100644 --- a/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts +++ b/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts @@ -9,14 +9,14 @@ describe('market selector', () => { }); cy.visit('/markets'); cy.wait('@gqlSimpleMarketsQuery').then((response) => { - if (response.response?.body?.data?.markets?.length) { - markets = response.response?.body?.data?.markets; + if (response.response.body.data?.markets?.length) { + markets = response.response.body.data.markets; } }); }); it('should be properly rendered', () => { - if (markets) { + if (markets?.length) { cy.visit(`/trading/${markets[0].id}`); cy.get('input[placeholder="Search"]').should( 'have.value', @@ -34,7 +34,7 @@ describe('market selector', () => { }); it('typing should change list', () => { - if (markets) { + if (markets?.length) { cy.visit(`/trading/${markets[0].id}`); cy.get('input[placeholder="Search"]').type('{backspace}'); cy.getByTestId('market-pane') @@ -65,7 +65,7 @@ describe('market selector', () => { }); it('mobile view', () => { - if (markets) { + if (markets?.length) { cy.viewport('iphone-xr'); cy.visit(`/trading/${markets[0].id}`); cy.get('[role="dialog"]').should('not.exist'); diff --git a/apps/simple-trading-app-e2e/src/integration/market-trade.test.ts b/apps/simple-trading-app-e2e/src/integration/market-trade.test.ts new file mode 100644 index 000000000..e88b7d90d --- /dev/null +++ b/apps/simple-trading-app-e2e/src/integration/market-trade.test.ts @@ -0,0 +1,67 @@ +import { aliasQuery } from '@vegaprotocol/cypress'; +import { generateSimpleMarkets } from '../support/mocks/generate-markets'; +import { generateDealTicket } from '../support/mocks/generate-deal-ticket'; + +describe('Market trade', () => { + let markets; + beforeEach(() => { + cy.mockGQL((req) => { + aliasQuery(req, 'SimpleMarkets', generateSimpleMarkets()); + aliasQuery(req, 'DealTicketQuery', generateDealTicket()); + }); + cy.visit('/markets'); + cy.wait('@SimpleMarkets').then((response) => { + if (response.response.body.data?.markets?.length) { + markets = response.response.body.data.markets; + } + }); + }); + it('side selector should work well', () => { + if (markets?.length) { + cy.visit(`/trading/${markets[0].id}`); + cy.get('#step-1-control [aria-label^="Selected value"]').should( + 'have.text', + 'Long' + ); + cy.get('#step-1-control [aria-label^="Selected value"]').click(); + cy.get('button[aria-label="Open short position"]').click(); + cy.get('#step-2-control').click(); + cy.get('#step-1-control [aria-label^="Selected value"]').should( + 'have.text', + 'Short' + ); + } + }); + + it('mobile view should work well', () => { + if (markets?.length) { + cy.viewport('iphone-xr'); + cy.visit(`/trading/${markets[0].id}`); + cy.getByTestId('next-button').scrollIntoView().click(); + + cy.get('button[aria-label="Open long position"]').should( + 'have.class', + 'selected' + ); + cy.get('button[aria-label="Open short position"]').should( + 'not.have.class', + 'selected' + ); + + cy.get('button[aria-label="Open short position"]').click(); + cy.get('button[aria-label="Open long position"]').should( + 'not.have.class', + 'selected' + ); + cy.get('button[aria-label="Open short position"]').should( + 'have.class', + 'selected' + ); + cy.getByTestId('next-button').scrollIntoView().click(); + cy.get('#step-1-control').should( + 'contain.html', + 'aria-label="Selected value Short"' + ); + } + }); +}); diff --git a/apps/simple-trading-app-e2e/src/support/mocks/generate-deal-ticket.ts b/apps/simple-trading-app-e2e/src/support/mocks/generate-deal-ticket.ts new file mode 100644 index 000000000..477447863 --- /dev/null +++ b/apps/simple-trading-app-e2e/src/support/mocks/generate-deal-ticket.ts @@ -0,0 +1,33 @@ +export const generateDealTicket = () => { + return { + market: { + id: 'first-btcusd-id', + name: 'AAVEDAI Monthly (30 Jun 2022)', + decimalPlaces: 5, + positionDecimalPlaces: 0, + state: 'Active', + tradingMode: 'Continuous', + tradableInstrument: { + instrument: { + product: { + quoteName: 'DAI', + settlementAsset: { + id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', + symbol: 'tDAI', + name: 'tDAI TEST', + __typename: 'Asset', + }, + __typename: 'Future', + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + depth: { + lastTrade: { price: '9893006', __typename: 'Trade' }, + __typename: 'MarketDepth', + }, + __typename: 'Market', + }, + }; +}; diff --git a/apps/simple-trading-app-e2e/src/support/mocks/generate-markets.ts b/apps/simple-trading-app-e2e/src/support/mocks/generate-markets.ts new file mode 100644 index 000000000..8e80b4afd --- /dev/null +++ b/apps/simple-trading-app-e2e/src/support/mocks/generate-markets.ts @@ -0,0 +1,1069 @@ +export const generateSimpleMarkets = () => { + return { + markets: [ + { + id: 'first-btcusd-id', + name: 'AAVEDAI Monthly (30 Jun 2022)', + data: { + market: { + id: 'first-btcusd-id', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'AAVEDAI.MF21', + metadata: { + tags: [ + 'formerly:2839D9B2329C9E70', + 'base:AAVE', + 'quote:DAI', + 'class:fx/crypto', + 'monthly', + 'sector:defi', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'DAI', + settlementAsset: { symbol: 'tDAI', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '9556163', close: '9587028', __typename: 'Candle' }, + { + open: '9587028', + close: '9769899', + __typename: 'Candle', + }, + { open: '9769899', close: '9586292', __typename: 'Candle' }, + { + open: '9586292', + close: '9261774', + __typename: 'Candle', + }, + { open: '9261773', close: '9236369', __typename: 'Candle' }, + { + open: '9236369', + close: '9226070', + __typename: 'Candle', + }, + { open: '9226077', close: '9233252', __typename: 'Candle' }, + { + open: '9249854', + close: '9333038', + __typename: 'Candle', + }, + { open: '9333038', close: '9410371', __typename: 'Candle' }, + { + open: '9410371', + close: '9626249', + __typename: 'Candle', + }, + { open: '9626247', close: '9493253', __typename: 'Candle' }, + { + open: '9493253', + close: '9309054', + __typename: 'Candle', + }, + { open: '9309054', close: '9378428', __typename: 'Candle' }, + { + open: '9378428', + close: '9352996', + __typename: 'Candle', + }, + { open: '9352996', close: '9451142', __typename: 'Candle' }, + { + open: '9451142', + close: '9691070', + __typename: 'Candle', + }, + { open: '9691071', close: '9622031', __typename: 'Candle' }, + { + open: '9622034', + close: '9519285', + __typename: 'Candle', + }, + { open: '9528904', close: '9671275', __typename: 'Candle' }, + { + open: '9671275', + close: '9988454', + __typename: 'Candle', + }, + { open: '9982457', close: '10085537', __typename: 'Candle' }, + { + open: '10085537', + close: '9967390', + __typename: 'Candle', + }, + { open: '9967390', close: '9974844', __typename: 'Candle' }, + { + open: '9974844', + close: '9940706', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a', + name: 'CELUSD (June 2022)', + data: { + market: { + id: '57fbaa322e97cfc8bb5f1de048c37e033c41b1ac1906d3aed9960912a067ef5a', + state: 'Pending', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'CELUSD', + metadata: { + tags: [ + 'base:CEL', + 'quote:USD', + 'class:fx/crypto', + 'ad-hoc', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'XYZalpha', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255', + name: 'XMRUSD market', + data: { + market: { + id: 'ccf2f04865e5951ac3405da6e16b7cbdb535a0ad32df4df2dbed4262cf473255', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'XMRUSD', + metadata: { + tags: [ + 'base:monero', + 'quote:usd', + 'class:fx/crypto', + 'ad-hoc', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'XYZbeta', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f', + name: 'UNIDAI Monthly (30 Jun 2022)', + data: { + market: { + id: '6030e5b4e0ca3297a26081e5af4d453f97f96baab2d74bf56f84efcffc4c382f', + state: 'Suspended', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'UNIDAI.MF21', + metadata: { + tags: [ + 'formerly:3C58ED2A4A6C5D7E', + 'base:UNI', + 'quote:DAI', + 'class:fx/crypto', + 'monthly', + 'sector:defi', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'DAI', + settlementAsset: { symbol: 'tDAI', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '723082', close: '726800', __typename: 'Candle' }, + { + open: '726800', + close: '733551', + __typename: 'Candle', + }, + { open: '733557', close: '719960', __typename: 'Candle' }, + { + open: '719961', + close: '707711', + __typename: 'Candle', + }, + { open: '702459', close: '705275', __typename: 'Candle' }, + { + open: '701609', + close: '698494', + __typename: 'Candle', + }, + { open: '698494', close: '702201', __typename: 'Candle' }, + { + open: '704749', + close: '704248', + __typename: 'Candle', + }, + { open: '708562', close: '714712', __typename: 'Candle' }, + { + open: '718434', + close: '732567', + __typename: 'Candle', + }, + { open: '732567', close: '750475', __typename: 'Candle' }, + { + open: '768916', + close: '746371', + __typename: 'Candle', + }, + { open: '740240', close: '744910', __typename: 'Candle' }, + { + open: '746902', + close: '731680', + __typename: 'Candle', + }, + { open: '730156', close: '729021', __typename: 'Candle' }, + { + open: '730503', + close: '738096', + __typename: 'Candle', + }, + { open: '738096', close: '743087', __typename: 'Candle' }, + { + open: '739244', + close: '732352', + __typename: 'Candle', + }, + { open: '732352', close: '751259', __typename: 'Candle' }, + { + open: '751259', + close: '751455', + __typename: 'Candle', + }, + { open: '751455', close: '753990', __typename: 'Candle' }, + { + open: '757174', + close: '761100', + __typename: 'Candle', + }, + { open: '758974', close: '759379', __typename: 'Candle' }, + { + open: '762834', + close: '761777', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4', + name: 'XMRUSD market', + data: { + market: { + id: 'de74a5572045b300e8ec50d136896912ec76e7d7ec135bc305dfd4854d9363a4', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'XMRUSD', + metadata: { + tags: [ + 'base:monero', + 'quote:usd', + 'class:fx/crypto', + 'ad-hoc', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'XYZbeta', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '14602', close: '14596', __typename: 'Candle' }, + { + open: '14596', + close: '14731', + __typename: 'Candle', + }, + { open: '14732', close: '14592', __typename: 'Candle' }, + { + open: '14592', + close: '14516', + __typename: 'Candle', + }, + { open: '14516', close: '14179', __typename: 'Candle' }, + { + open: '14179', + close: '14031', + __typename: 'Candle', + }, + { open: '14032', close: '14115', __typename: 'Candle' }, + { + open: '14116', + close: '14166', + __typename: 'Candle', + }, + { open: '14166', close: '14377', __typename: 'Candle' }, + { + open: '14377', + close: '14714', + __typename: 'Candle', + }, + { open: '14713', close: '14593', __typename: 'Candle' }, + { + open: '14594', + close: '14273', + __typename: 'Candle', + }, + { open: '14272', close: '14245', __typename: 'Candle' }, + { + open: '14244', + close: '14337', + __typename: 'Candle', + }, + { open: '14338', close: '14384', __typename: 'Candle' }, + { + open: '14385', + close: '14257', + __typename: 'Candle', + }, + { open: '14256', close: '14105', __typename: 'Candle' }, + { + open: '14106', + close: '14067', + __typename: 'Candle', + }, + { open: '14066', close: '14196', __typename: 'Candle' }, + { + open: '14197', + close: '14316', + __typename: 'Candle', + }, + { open: '14315', close: '14377', __typename: 'Candle' }, + { + open: '14378', + close: '14106', + __typename: 'Candle', + }, + { open: '14105', close: '14317', __typename: 'Candle' }, + { + open: '14318', + close: '14467', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6', + name: 'ApeCoin (18 Jul 2022)', + data: { + market: { + id: '31ea96284611771e486c820acb26a325a99664f9854b5a7e7ad99023efa8f9e6', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'APE/USD', + metadata: { + tags: [ + 'quote:USD', + 'ticker:APE', + 'class:equities/single-stock-futures', + 'sector:tech', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tUSDC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74', + name: 'BTCUSD Monthly (18 Jul 2022)', + data: { + market: { + id: '34cff959cdc2ffdb0f167820d701fe8b51cc6b8588e650d93369aaa22d6f8b74', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'BTCUSD.MF21', + metadata: { + tags: [ + 'formerly:076BB86A5AA41E3E', + 'base:BTC', + 'quote:USD', + 'class:fx/crypto', + 'monthly', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tBTC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2', + name: 'Builders Club x ETHcc', + data: { + market: { + id: '87b0bbb3c171baa5d97dfc3852332829c91e5c5dc9f7c8fb584c6d8ac75aaaf2', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'LTCUSD', + metadata: { + tags: [ + 'base:litecoin', + 'quote:usd', + 'class:fx/crypto', + 'ad-hoc', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'XYZgamma', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '5700', close: '5688', __typename: 'Candle' }, + { + open: '5688', + close: '5644', + __typename: 'Candle', + }, + { open: '5644', close: '5664', __typename: 'Candle' }, + { + open: '5664', + close: '5694', + __typename: 'Candle', + }, + { open: '5694', close: '5751', __typename: 'Candle' }, + { + open: '5751', + close: '5853', + __typename: 'Candle', + }, + { open: '5853', close: '5773', __typename: 'Candle' }, + { + open: '5773', + close: '5697', + __typename: 'Candle', + }, + { open: '5697', close: '5683', __typename: 'Candle' }, + { + open: '5683', + close: '5655', + __typename: 'Candle', + }, + { open: '5655', close: '5674', __typename: 'Candle' }, + { + open: '5674', + close: '5705', + __typename: 'Candle', + }, + { open: '5705', close: '5648', __typename: 'Candle' }, + { + open: '5648', + close: '5646', + __typename: 'Candle', + }, + { open: '5646', close: '5687', __typename: 'Candle' }, + { + open: '5687', + close: '5675', + __typename: 'Candle', + }, + { open: '5675', close: '5696', __typename: 'Candle' }, + { + open: '5696', + close: '5699', + __typename: 'Candle', + }, + { open: '5699', close: '5731', __typename: 'Candle' }, + { + open: '5731', + close: '5768', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823', + name: 'BTCUSD Monthly (18 Jul 2022)', + data: { + market: { + id: '45266143c6e9b58f4cff9b8906e971c531bb29ea7af01066973f9b77e8134823', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'BTCUSD.MF21', + metadata: { + tags: [ + 'formerly:076BB86A5AA41E3E', + 'base:BTC', + 'quote:USD', + 'class:fx/crypto', + 'monthly', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tBTC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6', + name: 'ETHBTC Quarterly (30 Jun 2022)', + data: { + market: { + id: '65663ebdc96161162769c4d5c5508137416748178d7cb28e2cb0d07a151a2bc6', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'ETHBTC.QM21', + metadata: { + tags: [ + 'formerly:1F0BB6EB5703B099', + 'base:ETH', + 'quote:BTC', + 'class:fx/crypto', + 'quarterly', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'BTC', + settlementAsset: { symbol: 'tBTC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '6680', close: '6600', __typename: 'Candle' }, + { + open: '6600', + close: '6630', + __typename: 'Candle', + }, + { open: '6630', close: '6677', __typename: 'Candle' }, + { + open: '6735', + close: '6720', + __typename: 'Candle', + }, + { open: '6720', close: '6691', __typename: 'Candle' }, + { + open: '6681', + close: '6730', + __typename: 'Candle', + }, + { open: '6756', close: '6778', __typename: 'Candle' }, + { + open: '6778', + close: '6768', + __typename: 'Candle', + }, + { open: '6768', close: '6834', __typename: 'Candle' }, + { + open: '6834', + close: '7001', + __typename: 'Candle', + }, + { open: '7001', close: '6894', __typename: 'Candle' }, + { + open: '6910', + close: '6867', + __typename: 'Candle', + }, + { open: '6867', close: '6827', __typename: 'Candle' }, + { + open: '6899', + close: '6910', + __typename: 'Candle', + }, + { open: '6868', close: '6902', __typename: 'Candle' }, + { + open: '6905', + close: '6947', + __typename: 'Candle', + }, + { open: '6932', close: '6900', __typename: 'Candle' }, + { + open: '6900', + close: '6911', + __typename: 'Candle', + }, + { open: '6936', close: '6948', __typename: 'Candle' }, + { + open: '6936', + close: '6946', + __typename: 'Candle', + }, + { open: '6946', close: '6906', __typename: 'Candle' }, + { + open: '6947', + close: '6962', + __typename: 'Candle', + }, + { open: '6962', close: '7027', __typename: 'Candle' }, + { + open: '6941', + close: '6921', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850', + name: 'Apple Monthly (30 Jun 2022)', + data: { + market: { + id: '234afabd27e3bce1a879039c041f9f00f915d98459935ddafd0169d38dd13850', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'AAPL.MF21', + metadata: { + tags: [ + 'formerly:4899E01009F1A721', + 'quote:USD', + 'ticker:AAPL', + 'class:equities/single-stock-futures', + 'sector:tech', + 'listing_venue:NASDAQ', + 'country:US', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tUSDC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '15126319', close: '15087482', __typename: 'Candle' }, + { + open: '15087482', + close: '15119759', + __typename: 'Candle', + }, + { open: '15119759', close: '15061361', __typename: 'Candle' }, + { + open: '15061361', + close: '14742622', + __typename: 'Candle', + }, + { open: '14742622', close: '14735571', __typename: 'Candle' }, + { + open: '14735571', + close: '14694437', + __typename: 'Candle', + }, + { open: '14694437', close: '14670479', __typename: 'Candle' }, + { + open: '14670479', + close: '14624796', + __typename: 'Candle', + }, + { open: '14621001', close: '14722835', __typename: 'Candle' }, + { + open: '14722835', + close: '14705183', + __typename: 'Candle', + }, + { open: '14705183', close: '14710797', __typename: 'Candle' }, + { + open: '14710796', + close: '14713874', + __typename: 'Candle', + }, + { open: '14713874', close: '14677982', __typename: 'Candle' }, + { + open: '14677982', + close: '14710031', + __typename: 'Candle', + }, + { open: '14751685', close: '14737556', __typename: 'Candle' }, + { + open: '14737556', + close: '14769139', + __typename: 'Candle', + }, + { open: '14760616', close: '14761691', __typename: 'Candle' }, + { + open: '14761691', + close: '14714380', + __typename: 'Candle', + }, + { open: '14714380', close: '14740160', __typename: 'Candle' }, + { + open: '14740160', + close: '14764832', + __typename: 'Candle', + }, + { open: '14764832', close: '14778059', __typename: 'Candle' }, + { + open: '14761936', + close: '14707831', + __typename: 'Candle', + }, + { open: '14707831', close: '14753272', __typename: 'Candle' }, + { + open: '14753272', + close: '14771411', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6', + name: 'Tesla Quarterly (30 Jun 2022)', + data: { + market: { + id: '9f2a3c1caa67bb0773ec18d908d32b55b129b9ec2d106a8e9a87f6aa5c0375a6', + state: 'Active', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'TSLA.QM21', + metadata: { + tags: [ + 'formerly:5A86B190C384997F', + 'quote:EURO', + 'ticker:TSLA', + 'class:equities/single-stock-futures', + 'sector:tech', + 'listing_venue:NASDAQ', + 'country:US', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'EURO', + settlementAsset: { symbol: 'tEURO', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '71263667', close: '71173749', __typename: 'Candle' }, + { + open: '71173749', + close: '71055959', + __typename: 'Candle', + }, + { open: '71055959', close: '71078605', __typename: 'Candle' }, + { + open: '71078605', + close: '69838205', + __typename: 'Candle', + }, + { open: '69838205', close: '69166023', __typename: 'Candle' }, + { + open: '69166023', + close: '68841759', + __typename: 'Candle', + }, + { open: '68841759', close: '68711290', __typename: 'Candle' }, + { + open: '68711290', + close: '68650154', + __typename: 'Candle', + }, + { open: '68644289', close: '68638322', __typename: 'Candle' }, + { + open: '68704232', + close: '68343581', + __typename: 'Candle', + }, + { open: '67345721', close: '67554701', __typename: 'Candle' }, + { + open: '67533143', + close: '67551571', + __typename: 'Candle', + }, + { open: '67533286', close: '67543539', __typename: 'Candle' }, + { + open: '67580208', + close: '67472337', + __typename: 'Candle', + }, + { open: '67416289', close: '67459697', __typename: 'Candle' }, + { + open: '67396085', + close: '67528265', + __typename: 'Candle', + }, + { open: '67491799', close: '67538833', __typename: 'Candle' }, + { + open: '67493210', + close: '67362908', + __typename: 'Candle', + }, + { open: '67362908', close: '67518182', __typename: 'Candle' }, + { + open: '67518182', + close: '67635775', + __typename: 'Candle', + }, + { open: '67635775', close: '67734861', __typename: 'Candle' }, + { + open: '67714943', + close: '67736742', + __typename: 'Candle', + }, + { open: '67716074', close: '67690826', __typename: 'Candle' }, + { + open: '67714352', + close: '67817444', + __typename: 'Candle', + }, + ], + __typename: 'Market', + }, + { + id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a', + name: 'Go big or Go home', + data: { + market: { + id: '8a45ee934d3ddac4b036f9884df1064a5353c620a56f775ba36597d0edef9a7a', + state: 'Settled', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'LTCUSD', + metadata: { + tags: [ + 'base:litecoin', + 'quote:usd', + 'class:fx/crypto', + 'ad-hoc', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'XYZgamma', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: [ + { open: '5809', close: '5795', __typename: 'Candle' }, + { + open: '5795', + close: '5845', + __typename: 'Candle', + }, + { open: '5845', close: '5779', __typename: 'Candle' }, + { + open: '5779', + close: '5710', + __typename: 'Candle', + }, + { open: '5710', close: '5691', __typename: 'Candle' }, + ], + __typename: 'Market', + }, + { + id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f', + name: 'BTCUSD Monthly (08 Jul 2022)', + data: { + market: { + id: 'aede7b9ac0c3b225004929c5455160a00f59864aad32ec366e8a2bff1b30fd0f', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'BTCUSD.MF21', + metadata: { + tags: [ + 'formerly:076BB86A5AA41E3E', + 'base:BTC', + 'quote:USD', + 'class:fx/crypto', + 'monthly', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tBTC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + { + id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47', + name: 'BTCUSD Monthly (18 Jul 2022)', + data: { + market: { + id: '9398707e01daa1a1f1ca6ff87cf8d6c03fe7373ce31121ce81b99a129e6bda47', + state: 'Proposed', + __typename: 'Market', + }, + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'BTCUSD.MF21', + metadata: { + tags: [ + 'formerly:076BB86A5AA41E3E', + 'base:BTC', + 'quote:USD', + 'class:fx/crypto', + 'monthly', + 'sector:crypto', + ], + __typename: 'InstrumentMetadata', + }, + product: { + __typename: 'Future', + quoteName: 'USD', + settlementAsset: { symbol: 'tBTC', __typename: 'Asset' }, + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles: null, + __typename: 'Market', + }, + ], + }; +}; diff --git a/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx b/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx index 6e59b4b9c..137bd6c11 100644 --- a/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx +++ b/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx @@ -3,11 +3,7 @@ import { useForm, Controller } from 'react-hook-form'; import { Stepper } from '../stepper'; import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket'; import { Button, InputError } from '@vegaprotocol/ui-toolkit'; -import { - SideSelector, - DealTicketAmount, - MarketSelector, -} from '@vegaprotocol/deal-ticket'; +import { DealTicketAmount, MarketSelector } from '@vegaprotocol/deal-ticket'; import type { Order } from '@vegaprotocol/orders'; import { VegaTxStatus } from '@vegaprotocol/wallet'; import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers'; @@ -19,6 +15,7 @@ import { import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import MarketNameRenderer from '../simple-market-list/simple-market-renderer'; +import SideSelector, { SIDE_NAMES } from './side-selector'; interface DealTicketMarketProps { market: DealTicketQuery_market; @@ -47,6 +44,7 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => { const step = toDecimal(market.positionDecimalPlaces); const orderType = watch('type'); const orderTimeInForce = watch('timeInForce'); + const orderSide = watch('side'); const { message: invalidText, isDisabled } = useOrderValidation({ step, @@ -96,6 +94,7 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => { )} /> ), + value: SIDE_NAMES[orderSide] || '', }, { label: t('Choose Position Size'), diff --git a/apps/simple-trading-app/src/app/components/deal-ticket/side-selector.tsx b/apps/simple-trading-app/src/app/components/deal-ticket/side-selector.tsx new file mode 100644 index 000000000..1c03d045d --- /dev/null +++ b/apps/simple-trading-app/src/app/components/deal-ticket/side-selector.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import classNames from 'classnames'; +import { FormGroup, Button } from '@vegaprotocol/ui-toolkit'; +import { VegaWalletOrderSide } from '@vegaprotocol/wallet'; +import { t } from '@vegaprotocol/react-helpers'; + +interface SideSelectorProps { + value: VegaWalletOrderSide; + onSelect: (side: VegaWalletOrderSide) => void; +} + +export const SIDE_NAMES: Record = { + [VegaWalletOrderSide.Buy]: t('Long'), + [VegaWalletOrderSide.Sell]: t('Short'), +}; + +export default ({ value, onSelect }: SideSelectorProps) => { + return ( + +
+ + +
+ {t( + 'Trading derivatives allows you to make a profit or loss regardless of whether the market you are trading goes up or down. If you open a "long" position, you will make a profit if the price of your chosen market goes up, and you will make a profit for "short" positions when the price goes down.' + )} +
+
+
+ ); +}; diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx index 0d03be2e4..2e5d395ef 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx +++ b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx @@ -66,7 +66,7 @@ const SimpleMarketList = () => { ); const handleOnGridReady = useCallback(() => { - gridRef.current?.api.sizeColumnsToFit(); + gridRef.current?.api?.sizeColumnsToFit(); }, [gridRef]); useEffect(() => { diff --git a/apps/simple-trading-app/src/app/components/stepper/stepper.tsx b/apps/simple-trading-app/src/app/components/stepper/stepper.tsx index e7080d34c..5cea2af92 100644 --- a/apps/simple-trading-app/src/app/components/stepper/stepper.tsx +++ b/apps/simple-trading-app/src/app/components/stepper/stepper.tsx @@ -146,6 +146,7 @@ export const Stepper = ({ steps }: StepperProps) => { variant="secondary" onClick={handleNext} disabled={steps[activeStep].disabled} + data-testid="next-button" > {t('Next')} diff --git a/libs/deal-ticket/src/components/market-selector.tsx b/libs/deal-ticket/src/components/market-selector.tsx index 2c3f646b5..2f426d3de 100644 --- a/libs/deal-ticket/src/components/market-selector.tsx +++ b/libs/deal-ticket/src/components/market-selector.tsx @@ -203,7 +203,7 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => { /> -
+
Date: Wed, 20 Jul 2022 12:04:52 +0000 Subject: [PATCH 08/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 45 ++++++++++++------- apps/static/src/assets/stagnet1-tranches.json | 4 +- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index edfb3c740..f9d4e452f 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119264.267780733605195715", + "locked_amount": "119204.90105771935588971", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60209.33834672998167693", + "locked_amount": "60151.105121713407225726", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82295.77081135735338824657796", + "locked_amount": "82216.17604630659251819174664", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25329.028882811106818156", + "locked_amount": "25304.53117068063800536", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8244.353114276620049384", + "locked_amount": "8236.379346698325276688", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12686.43993432971175", + "locked_amount": "12655.838145380436", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1683469.24531969103196263", + "locked_amount": "1682139.173927239343147728", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11206.618394307949220156794068504", + "locked_amount": "11167.5294767313934686410850097325", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3040.45899705098947977036778262937595131", + "locked_amount": "3023.45470918949705478036572574200913234", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2611407.43304698979013859112", + "locked_amount": "2609363.57639846592051001108", "deposits": [ { "amount": "1998.95815", @@ -16812,7 +16812,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "65238.81558128788608", - "locked_amount": "14485030.5132136325552785801455917158650987", + "locked_amount": "14477820.2329651353662326198608863168329278", "deposits": [ { "amount": "16249.93", @@ -19210,7 +19210,7 @@ "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", "total_removed": "2417759.129125450953301297", - "locked_amount": "7719857.45698800157022459762577318", + "locked_amount": "7713166.71794103455990502998443401", "deposits": [ { "amount": "129284.449", @@ -23721,7 +23721,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2733258.967024957170888122225820462", + "locked_amount": "2730615.41556224638408058729949625", "deposits": [ { "amount": "552496.6455", @@ -24954,7 +24954,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "423.0179607209", - "locked_amount": "413792.43520757895244730106950784", + "locked_amount": "413468.57443843763399729271841704", "deposits": [ { "amount": "3000", @@ -50764,7 +50764,7 @@ "tranche_start": "2021-12-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z", "total_added": "171288.42", - "total_removed": "39676.8566666082989", + "total_removed": "39680.6212136932989", "locked_amount": "0", "deposits": [ { @@ -55124,6 +55124,11 @@ "user": "0xED380970F4f0746C56C033f00507634a985ec330", "tx": "0xe1eedaec4d14343fccc7a145fc5d26014dbd7eb6e9dd41c4c6c454b0dfff9c13" }, + { + "amount": "3.764547085", + "user": "0x27049a430Df8b89Ae4f899b0383B0C876F9cAcEb", + "tx": "0x27055acf670d336b247b0abb16a5a9809e12aad5d96338ef8c7ecd4b8ea94db8" + }, { "amount": "60.4448387275", "user": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0", @@ -64499,6 +64504,12 @@ } ], "withdrawals": [ + { + "amount": "3.764547085", + "user": "0x27049a430Df8b89Ae4f899b0383B0C876F9cAcEb", + "tranche_id": 6, + "tx": "0x27055acf670d336b247b0abb16a5a9809e12aad5d96338ef8c7ecd4b8ea94db8" + }, { "amount": "38.3342872425", "user": "0x27049a430Df8b89Ae4f899b0383B0C876F9cAcEb", @@ -64537,8 +64548,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "246.235452915", - "remaining_tokens": "3.764547085" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x42bC480928828C57c39649A7D10e41227b6d5E4F", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index 1781cde27..3ac695e09 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.43068873668189", + "locked_amount": "35.362125824454593", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "252.499530695078645", + "locked_amount": "251.745338660578356", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 5b7ac96e8..4a224bf2b 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "231.84047818366311950022954502790461695", + "locked_amount": "231.14799277016739960022885939878234396", "deposits": [ { "amount": "1000", From 960ff40cc90b47d1b53cdadc034906f25e44a0a7 Mon Sep 17 00:00:00 2001 From: Joe Tsang <30622993+jtsang586@users.noreply.github.com> Date: Wed, 20 Jul 2022 15:15:13 +0100 Subject: [PATCH 09/19] fix: failing simple app test (#827) * fix: failing simple app test * fix: more fixes --- .../src/integration/market-selector.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts b/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts index acbd9e8a0..c41fd5b28 100644 --- a/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts +++ b/apps/simple-trading-app-e2e/src/integration/market-selector.test.ts @@ -27,8 +27,13 @@ describe('market selector', () => { cy.getByTestId('market-pane') .children() .find('[role="button"]') + .first() .should('contain.text', markets[0].name); - cy.getByTestId('market-pane').children().find('[role="button"]').click(); + cy.getByTestId('market-pane') + .children() + .find('[role="button"]') + .first() + .click(); cy.getByTestId('market-pane').should('not.be.visible'); } }); @@ -40,7 +45,7 @@ describe('market selector', () => { cy.getByTestId('market-pane') .children() .find('[role="button"]') - .should('have.length', 1); + .should('have.length.at.least', 1); cy.get('input[placeholder="Search"]').clear(); cy.get('input[placeholder="Search"]').type('app'); const filtered = markets.filter((market) => market.name.match(/app/i)); From 4670d5e6cf67eb4433d649b6f6414b2d924e8245 Mon Sep 17 00:00:00 2001 From: botond <105208209+notbot00@users.noreply.github.com> Date: Wed, 20 Jul 2022 15:52:53 +0100 Subject: [PATCH 10/19] Feat/647 disconnect eth (#813) * feat: add disconnect feature form ethereum wallet * fix: unit tests * fix: format * fix: format * fix: e2e withdrawal check * fix: format again * fix: eth address in e2e * fix: env var for eth address --- .../src/integration/withdraw.cy.ts | 9 +- libs/deposits/src/lib/deposit-form.spec.tsx | 358 ++++++++++-------- libs/deposits/src/lib/deposit-form.tsx | 14 +- .../src/components/select/select.tsx | 18 +- libs/web3/src/index.ts | 1 + libs/web3/src/lib/web3-wallet-input.tsx | 43 +++ libs/withdraws/src/lib/withdraw-form.spec.tsx | 161 ++++---- libs/withdraws/src/lib/withdraw-form.tsx | 27 +- libs/withdraws/src/lib/withdraw-manager.tsx | 3 - 9 files changed, 365 insertions(+), 269 deletions(-) create mode 100644 libs/web3/src/lib/web3-wallet-input.tsx diff --git a/apps/trading-e2e/src/integration/withdraw.cy.ts b/apps/trading-e2e/src/integration/withdraw.cy.ts index 1cca11113..1b0e39248 100644 --- a/apps/trading-e2e/src/integration/withdraw.cy.ts +++ b/apps/trading-e2e/src/integration/withdraw.cy.ts @@ -11,6 +11,7 @@ describe('withdraw', () => { const amountField = 'input[name="amount"]'; const useMaximumAmount = 'use-maximum'; const submitWithdrawBtn = 'submit-withdrawal'; + const ethAddressValue = Cypress.env('ETHEREUM_WALLET_ADDRESS'); beforeEach(() => { cy.mockWeb3Provider(); @@ -41,12 +42,8 @@ describe('withdraw', () => { // only 2 despite 3 fields because the ethereum address will be auto populated cy.getByTestId(formFieldError).should('have.length', 2); - // Test for invalid Ethereum address - cy.get(toAddressField) - .clear() - .type('invalid-ethereum-address') - .next('[data-testid="input-error-text"]') - .should('contain.text', 'Invalid Ethereum address'); + // Test for Ethereum address + cy.get(toAddressField).should('have.value', ethAddressValue); // Test min amount cy.get(assetSelectField).select('Asset 1'); // Select asset so we have a min viable amount calculated diff --git a/libs/deposits/src/lib/deposit-form.spec.tsx b/libs/deposits/src/lib/deposit-form.spec.tsx index 8dae0dc85..feb18597a 100644 --- a/libs/deposits/src/lib/deposit-form.spec.tsx +++ b/libs/deposits/src/lib/deposit-form.spec.tsx @@ -1,4 +1,4 @@ -import { act, fireEvent, render, screen } from '@testing-library/react'; +import { waitFor, fireEvent, render, screen } from '@testing-library/react'; import BigNumber from 'bignumber.js'; import type { DepositFormProps } from './deposit-form'; import { DepositForm } from './deposit-form'; @@ -25,6 +25,7 @@ function generateAsset(): Asset { let asset: Asset; let props: DepositFormProps; +const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; beforeEach(() => { asset = generateAsset(); @@ -43,171 +44,214 @@ beforeEach(() => { allowance: new BigNumber(30), isFaucetable: true, }; + + (useVegaWallet as jest.Mock).mockReturnValue({ keypair: null }); + (useWeb3React as jest.Mock).mockReturnValue({ account: MOCK_ETH_ADDRESS }); }); -it('Form validation', async () => { - const mockUseVegaWallet = useVegaWallet as jest.Mock; - mockUseVegaWallet.mockReturnValue({ keypair: null }); +describe('Deposit form', () => { + it('renders with default values', async () => { + render(); - const mockUseWeb3React = useWeb3React as jest.Mock; - mockUseWeb3React.mockReturnValue({ account: undefined }); + // Assert default values (including) from/to provided by useVegaWallet and useWeb3React + expect(screen.getByLabelText('From (Ethereum address)')).toHaveValue( + MOCK_ETH_ADDRESS + ); + expect(screen.getByLabelText('Asset')).toHaveValue(''); + expect(screen.getByLabelText('To (Vega key)')).toHaveValue(''); + expect(screen.getByLabelText('Amount')).toHaveValue(null); + }); - const { rerender } = render(); + describe('fields validation', () => { + it('fails when submitted with empty required fields', async () => { + render(); - // Assert default values (including) from/to provided by useVegaWallet and useWeb3React - expect(screen.getByLabelText('From (Ethereum address)')).toHaveValue(''); - expect(screen.getByLabelText('Asset')).toHaveValue(''); - expect(screen.getByLabelText('To (Vega key)')).toHaveValue(''); - expect(screen.getByLabelText('Amount')).toHaveValue(null); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect(props.submitDeposit).not.toHaveBeenCalled(); + const validationMessages = await screen.findAllByRole('alert'); + expect(validationMessages).toHaveLength(3); + validationMessages.forEach((el) => { + expect(el).toHaveTextContent('Required'); + }); + }); + + it('fails when submitted with invalid ethereum address', async () => { + (useWeb3React as jest.Mock).mockReturnValue({ account: '123' }); + render(); + + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Invalid Ethereum address') + ).toBeInTheDocument(); + }); + + it('fails when submitted with invalid vega wallet key', async () => { + render(); + + const invalidVegaKey = 'abc'; + fireEvent.change(screen.getByLabelText('To (Vega key)'), { + target: { value: invalidVegaKey }, + }); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect(await screen.findByText('Invalid Vega key')).toBeInTheDocument(); + }); + + it('fails when submitted amount is more than the amount available in the ethereum wallet', async () => { + render(); + + // Max amount validation + const amountMoreThanAvailable = '7'; + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: amountMoreThanAvailable }, + }); + + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Insufficient amount in Ethereum wallet') + ).toBeInTheDocument(); + }); + + it('fails when submitted amount is more than the maximum limit', async () => { + render(); + + const amountMoreThanLimit = '21'; + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: amountMoreThanLimit }, + }); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Amount is above permitted maximum') + ).toBeInTheDocument(); + }); + + it('fails when submitted amount is more than the approved amount', async () => { + render( + + ); + + const amountMoreThanAllowance = '31'; + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: amountMoreThanAllowance }, + }); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Amount is above approved amount') + ).toBeInTheDocument(); + }); + + it('fails when submitted amount is less than the minimum limit', async () => { + // Min amount validation + render(); // Render with selected asset so we have asset.decimals + + const amountLessThanMinViable = '0.00001'; + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: amountLessThanMinViable }, + }); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Value is below minimum') + ).toBeInTheDocument(); + }); + + it('fails when submitted amount is less than zero', async () => { + render(); + + const amountLessThanZero = '-0.00001'; + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: amountLessThanZero }, + }); + fireEvent.submit(screen.getByTestId('deposit-form')); + + expect( + await screen.findByText('Value is below minimum') + ).toBeInTheDocument(); + }); + }); + + it('handles deposit approvals', () => { + const mockUseVegaWallet = useVegaWallet as jest.Mock; + mockUseVegaWallet.mockReturnValue({ keypair: null }); + + const mockUseWeb3React = useWeb3React as jest.Mock; + mockUseWeb3React.mockReturnValue({ account: undefined }); + + render( + + ); + + fireEvent.click( + screen.getByText(`Approve ${asset.symbol}`, { + selector: '[type="button"]', + }) + ); + + expect(props.submitApprove).toHaveBeenCalled(); + }); + + it('handles submitting a deposit', async () => { + const vegaKey = + 'f8885edfa7ffdb6ed996ca912e9258998e47bf3515c885cf3c63fb56b15de36f'; + const mockUseVegaWallet = useVegaWallet as jest.Mock; + mockUseVegaWallet.mockReturnValue({ keypair: { pub: vegaKey } }); + + const account = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; + const mockUseWeb3React = useWeb3React as jest.Mock; + mockUseWeb3React.mockReturnValue({ account }); + + const limits = { + max: new BigNumber(20), + deposited: new BigNumber(10), + }; + + render( + + ); + + // Check deposit limit is displayed + expect( + screen.getByText('Max deposit total', { selector: 'th' }) + .nextElementSibling + ).toHaveTextContent(limits.max.toString()); + expect( + screen.getByText('Remaining available', { selector: 'th' }) + .nextElementSibling + ).toHaveTextContent(limits.max.minus(limits.deposited).toString()); + + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: '8' }, + }); - await act(async () => { fireEvent.click( screen.getByText('Deposit', { selector: '[type="submit"]' }) ); - }); - expect(props.submitDeposit).not.toHaveBeenCalled(); - const validationMessages = screen.getAllByRole('alert'); - expect(validationMessages).toHaveLength(4); - validationMessages.forEach((el) => { - expect(el).toHaveTextContent('Required'); - }); - - // Address validation - const invalidEthereumAddress = '123'; - fireEvent.change(screen.getByLabelText('From (Ethereum address)'), { - target: { value: invalidEthereumAddress }, - }); - expect( - await screen.findByText('Invalid Ethereum address') - ).toBeInTheDocument(); - - const invalidVegaKey = 'abc'; - fireEvent.change(screen.getByLabelText('To (Vega key)'), { - target: { value: invalidVegaKey }, - }); - expect(await screen.findByText('Invalid Vega key')).toBeInTheDocument(); - - // Max amount validation - const amountMoreThanAvailable = '7'; // but also less than lifetime limit available - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: amountMoreThanAvailable }, - }); - expect( - await screen.findByText('Insufficient amount in Ethereum wallet') - ).toBeInTheDocument(); - - const amountMoreThanLimit = '11'; - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: amountMoreThanLimit }, - }); - expect( - await screen.findByText('Amount is above permitted maximum') - ).toBeInTheDocument(); - - rerender( - - ); - - const amountMoreThanAllowance = '31'; - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: amountMoreThanAllowance }, - }); - expect( - await screen.findByText('Amount is above approved amount') - ).toBeInTheDocument(); - - // Min amount validation - rerender(); // Rerender with selected asset so we have asset.decimals - - const amountLessThanMinViable = '0.00001'; - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: amountLessThanMinViable }, - }); - - expect(await screen.findByText('Value is below minimum')).toBeInTheDocument(); - - const amountLessThanZero = '-0.00001'; - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: amountLessThanZero }, - }); - - expect(await screen.findByText('Value is below minimum')).toBeInTheDocument(); -}); - -it('Approval', () => { - const mockUseVegaWallet = useVegaWallet as jest.Mock; - mockUseVegaWallet.mockReturnValue({ keypair: null }); - - const mockUseWeb3React = useWeb3React as jest.Mock; - mockUseWeb3React.mockReturnValue({ account: undefined }); - - render( - - ); - - fireEvent.click( - screen.getByText(`Approve ${asset.symbol}`, { selector: '[type="button"]' }) - ); - - expect(props.submitApprove).toHaveBeenCalled(); -}); - -it('Deposit', async () => { - const vegaKey = - 'f8885edfa7ffdb6ed996ca912e9258998e47bf3515c885cf3c63fb56b15de36f'; - const mockUseVegaWallet = useVegaWallet as jest.Mock; - mockUseVegaWallet.mockReturnValue({ keypair: { pub: vegaKey } }); - - const account = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; - const mockUseWeb3React = useWeb3React as jest.Mock; - mockUseWeb3React.mockReturnValue({ account }); - - const limits = { - max: new BigNumber(20), - deposited: new BigNumber(10), - }; - - render( - - ); - - // Check deposit limit is displayed - expect( - screen.getByText('Max deposit total', { selector: 'th' }).nextElementSibling - ).toHaveTextContent(limits.max.toString()); - expect( - screen.getByText('Remaining available', { selector: 'th' }) - .nextElementSibling - ).toHaveTextContent(limits.max.minus(limits.deposited).toString()); - - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: '8' }, - }); - - await act(async () => { - fireEvent.click( - screen.getByText('Deposit', { selector: '[type="submit"]' }) - ); - }); - - expect(props.submitDeposit).toHaveBeenCalledWith({ - // @ts-ignore contract address definitely defined - assetSource: asset.source.contractAddress, - amount: '800', - vegaPublicKey: vegaKey, + await waitFor(() => { + expect(props.submitDeposit).toHaveBeenCalledWith({ + // @ts-ignore contract address definitely defined + assetSource: asset.source.contractAddress, + amount: '800', + vegaPublicKey: vegaKey, + }); + }); }); }); diff --git a/libs/deposits/src/lib/deposit-form.tsx b/libs/deposits/src/lib/deposit-form.tsx index eb2a3a209..149b961d1 100644 --- a/libs/deposits/src/lib/deposit-form.tsx +++ b/libs/deposits/src/lib/deposit-form.tsx @@ -1,7 +1,7 @@ import { removeDecimal, - t, ethereumAddress, + t, required, vegaPublicKey, minSafe, @@ -18,10 +18,10 @@ import { } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet } from '@vegaprotocol/wallet'; import { useWeb3React } from '@web3-react/core'; +import { Web3WalletInput } from '@vegaprotocol/web3'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; -import { useMemo } from 'react'; -import { useEffect } from 'react'; +import { useMemo, useEffect } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { DepositLimits } from './deposit-limits'; import type { Asset } from './deposit-manager'; @@ -141,9 +141,11 @@ export const DepositForm = ({ label={t('From (Ethereum address)')} labelFor="ethereum-address" > - {errors.from?.message && ( diff --git a/libs/ui-toolkit/src/components/select/select.tsx b/libs/ui-toolkit/src/components/select/select.tsx index 55f5547c8..a6767b90a 100644 --- a/libs/ui-toolkit/src/components/select/select.tsx +++ b/libs/ui-toolkit/src/components/select/select.tsx @@ -1,6 +1,7 @@ import type { SelectHTMLAttributes } from 'react'; import { forwardRef } from 'react'; import classNames from 'classnames'; +import { Icon } from '..'; import { defaultFormElement } from '../../utils/shared'; export interface SelectProps extends SelectHTMLAttributes { @@ -12,10 +13,17 @@ export interface SelectProps extends SelectHTMLAttributes { export const Select = forwardRef( ({ className, hasError, ...props }, ref) => ( - + +
) ); diff --git a/libs/web3/src/index.ts b/libs/web3/src/index.ts index cc2f7a4bc..397ed6f1f 100644 --- a/libs/web3/src/index.ts +++ b/libs/web3/src/index.ts @@ -9,3 +9,4 @@ export * from './lib/use-ethereum-transaction'; export * from './lib/transaction-dialog'; export * from './lib/web3-provider'; export * from './lib/web3-connect-dialog'; +export * from './lib/web3-wallet-input'; diff --git a/libs/web3/src/lib/web3-wallet-input.tsx b/libs/web3/src/lib/web3-wallet-input.tsx new file mode 100644 index 000000000..30885ce72 --- /dev/null +++ b/libs/web3/src/lib/web3-wallet-input.tsx @@ -0,0 +1,43 @@ +import type { ComponentProps } from 'react'; +import { useState } from 'react'; +import { useWeb3React } from '@web3-react/core'; +import { t } from '@vegaprotocol/react-helpers'; +import { Button, Input, Dialog } from '@vegaprotocol/ui-toolkit'; + +type Web3WalletInputProps = { + inputProps: Partial< + Omit< + ComponentProps, + 'appendIconName' | 'prependIconName' | 'appendElement' | 'prependElement' + > + >; +}; + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = () => {}; + +export const Web3WalletInput = ({ inputProps }: Web3WalletInputProps) => { + const [isDialogOpen, setDialogOpen] = useState(false); + const { account, connector } = useWeb3React(); + + return ( + <> + setDialogOpen(true)} + /> + +

+ {t('Connected with ')} + {account} +

+ +
+ + ); +}; diff --git a/libs/withdraws/src/lib/withdraw-form.spec.tsx b/libs/withdraws/src/lib/withdraw-form.spec.tsx index 3e98029f2..df6ad830b 100644 --- a/libs/withdraws/src/lib/withdraw-form.spec.tsx +++ b/libs/withdraws/src/lib/withdraw-form.spec.tsx @@ -1,14 +1,20 @@ import { act, fireEvent, render, screen } from '@testing-library/react'; import BigNumber from 'bignumber.js'; +import { useWeb3React } from '@web3-react/core'; import { WithdrawForm } from './withdraw-form'; -import type { WithdrawFormProps } from './withdraw-form'; import { generateAsset } from './test-helpers'; +import type { Asset } from './types'; +import type { WithdrawFormProps } from './withdraw-form'; -const ethereumAddress = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; +jest.mock('@web3-react/core'); + +const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853'; + +let assets: Asset[]; let props: WithdrawFormProps; beforeEach(() => { - const assets = [ + assets = [ generateAsset(), generateAsset({ id: 'asset-id-2', @@ -16,6 +22,7 @@ beforeEach(() => { name: 'asset-name-2', }), ]; + props = { assets, min: new BigNumber(0.00001), @@ -23,89 +30,95 @@ beforeEach(() => { limits: { max: new BigNumber(200), }, - ethereumAccount: undefined, selectedAsset: undefined, onSelectAsset: jest.fn(), submitWithdraw: jest.fn().mockReturnValue(Promise.resolve()), }; + (useWeb3React as jest.Mock).mockReturnValue({ account: MOCK_ETH_ADDRESS }); }); -const generateJsx = (props: WithdrawFormProps) => ; +describe('Withdrawal form', () => { + it('renders with default values', async () => { + render(); -it('Validation', async () => { - const { rerender } = render(generateJsx(props)); - - fireEvent.submit(screen.getByTestId('withdraw-form')); - - expect(await screen.findAllByRole('alert')).toHaveLength(3); - expect(screen.getAllByText('Required')).toHaveLength(3); - - // Selected asset state lives in state so rerender with it now selected - rerender(generateJsx({ ...props, selectedAsset: props.assets[0] })); - - fireEvent.change(screen.getByLabelText('Asset'), { - target: { value: props.assets[0].id }, + expect(screen.getByLabelText('Asset')).toHaveValue(''); + expect(screen.getByLabelText('To (Ethereum address)')).toHaveValue( + MOCK_ETH_ADDRESS + ); + expect(screen.getByLabelText('Amount')).toHaveValue(null); }); - fireEvent.change(screen.getByLabelText('To (Ethereum address)'), { - target: { value: 'invalid-address' }, + describe('field validation', () => { + it('fails when submitted with empty required fields', async () => { + render(); + + fireEvent.submit(screen.getByTestId('withdraw-form')); + + expect(await screen.findAllByRole('alert')).toHaveLength(2); + expect(screen.getAllByText('Required')).toHaveLength(2); + }); + + it('fails when submitted with invalid ethereum address', async () => { + (useWeb3React as jest.Mock).mockReturnValue({ account: '123' }); + render(); + + fireEvent.change(screen.getByLabelText('Asset'), { + target: { value: props.assets[0].id }, + }); + + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: '101' }, + }); + + fireEvent.submit(screen.getByTestId('withdraw-form')); + + expect( + await screen.findByText('Invalid Ethereum address') + ).toBeInTheDocument(); + expect(screen.getByText('Value is above maximum')).toBeInTheDocument(); + }); + + it('fails when submitted amount is less than the minimum limit', async () => { + render(); + + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: '0.000000000001' }, + }); + + fireEvent.submit(screen.getByTestId('withdraw-form')); + + expect( + await screen.findByText('Value is below minimum') + ).toBeInTheDocument(); + }); + + it('passes validation with correct field values', async () => { + render(); + + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: '40' }, + }); + + await act(async () => { + fireEvent.submit(screen.getByTestId('withdraw-form')); + }); + + expect(props.submitWithdraw).toHaveBeenCalledWith({ + asset: props.assets[0].id, + amount: '4000000', + receiverAddress: MOCK_ETH_ADDRESS, + }); + }); }); - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: '101' }, - }); + it('populates amount field with maximum value when clicking the "use maximum" button', () => { + const asset = props.assets[0]; + render(); - fireEvent.submit(screen.getByTestId('withdraw-form')); + fireEvent.click(screen.getByText('Use maximum')); - expect( - await screen.findByText('Invalid Ethereum address') - ).toBeInTheDocument(); - expect(screen.getByText('Value is above maximum')).toBeInTheDocument(); - - fireEvent.change(screen.getByLabelText('To (Ethereum address)'), { - target: { value: ethereumAddress }, - }); - - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: '0.000000000001' }, - }); - - fireEvent.submit(screen.getByTestId('withdraw-form')); - - expect(await screen.findByText('Value is below minimum')).toBeInTheDocument(); - - fireEvent.change(screen.getByLabelText('Amount'), { - target: { value: '40' }, - }); - - await act(async () => { - fireEvent.submit(screen.getByTestId('withdraw-form')); - }); - - expect(props.submitWithdraw).toHaveBeenCalledWith({ - asset: props.assets[0].id, - amount: '4000000', - receiverAddress: ethereumAddress, + expect(screen.getByLabelText('Amount')).toHaveValue( + Number(props.max.toFixed(asset.decimals)) + ); }); }); - -it('Use max button', () => { - const asset = props.assets[0]; - render(generateJsx({ ...props, selectedAsset: asset })); - - fireEvent.click(screen.getByText('Use maximum')); - - expect(screen.getByLabelText('Amount')).toHaveValue( - Number(props.max.toFixed(asset.decimals)) - ); -}); - -it('Use connected Ethereum account', () => { - render(generateJsx({ ...props, ethereumAccount: ethereumAddress })); - - fireEvent.click(screen.getByText('Use connected')); - - expect(screen.getByLabelText('To (Ethereum address)')).toHaveValue( - ethereumAddress - ); -}); diff --git a/libs/withdraws/src/lib/withdraw-form.tsx b/libs/withdraws/src/lib/withdraw-form.tsx index 64170030b..f1915d465 100644 --- a/libs/withdraws/src/lib/withdraw-form.tsx +++ b/libs/withdraws/src/lib/withdraw-form.tsx @@ -13,6 +13,8 @@ import { InputError, Select, } from '@vegaprotocol/ui-toolkit'; +import { Web3WalletInput } from '@vegaprotocol/web3'; +import { useWeb3React } from '@web3-react/core'; import type BigNumber from 'bignumber.js'; import type { ButtonHTMLAttributes, ReactNode } from 'react'; import { useForm, Controller } from 'react-hook-form'; @@ -31,7 +33,6 @@ export interface WithdrawFormProps { max: BigNumber; min: BigNumber; selectedAsset?: Asset; - ethereumAccount?: string; limits: { max: BigNumber; } | null; @@ -44,11 +45,11 @@ export const WithdrawForm = ({ max, min, selectedAsset, - ethereumAccount, limits, onSelectAsset, submitWithdraw, }: WithdrawFormProps) => { + const { account } = useWeb3React(); const { register, handleSubmit, @@ -59,7 +60,7 @@ export const WithdrawForm = ({ } = useForm({ defaultValues: { asset: selectedAsset?.id, - to: ethereumAccount, + to: account, }, }); const onSubmit = async (fields: FormFields) => { @@ -117,27 +118,17 @@ export const WithdrawForm = ({ labelFor="ethereum-address" className="relative" > - {errors.to?.message && ( {errors.to.message} )} - {ethereumAccount && ( - { - setValue('to', ethereumAccount); - clearErrors('to'); - }} - > - {t('Use connected')} - - )} {selectedAsset && limits && (
diff --git a/libs/withdraws/src/lib/withdraw-manager.tsx b/libs/withdraws/src/lib/withdraw-manager.tsx index dd5023808..7557966c2 100644 --- a/libs/withdraws/src/lib/withdraw-manager.tsx +++ b/libs/withdraws/src/lib/withdraw-manager.tsx @@ -9,7 +9,6 @@ import { addDecimal } from '@vegaprotocol/react-helpers'; import { AccountType } from '@vegaprotocol/types'; import BigNumber from 'bignumber.js'; import type { Account, Asset } from './types'; -import { useWeb3React } from '@web3-react/core'; import { useGetWithdrawLimits } from './use-get-withdraw-limits'; export interface WithdrawManagerProps { @@ -29,7 +28,6 @@ export const WithdrawManager = ({ const [dialogOpen, setDialogOpen] = useState(false); const [assetId, setAssetId] = useState(initialAssetId); - const { account: ethereumAccount } = useWeb3React(); const { ethTx, vegaTx, approval, submit, reset } = useWithdraw( dialogDismissed.current, isNewContract @@ -86,7 +84,6 @@ export const WithdrawManager = ({ return ( <> setAssetId(id)} assets={sortBy(assets, 'name')} From c0532a8507c1f9bc0a58ee679165714a583405e3 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Wed, 20 Jul 2022 18:40:28 +0200 Subject: [PATCH 11/19] Feat/470 edit order (#742) * feat: 470 edit orders hook and @vegaprotocol/vegawallet-service-api-client@0.4.14 * fix: 470 add methods for dialog intent and title * fix: #657 rename order-list lib to orders * chore: #657 move hooks to orders lib * chore: #657 vega tx dialog used for order cancellation and order submission * chore: #657 use client subscribe and unsubscribe on reset, refactor vegatxdialog * fix: #657 revert script src=./env-config.js ending * fix: #657 format project.json * Update project.json * fix: #657 cancel all subs and async tasks in useffect cleanup function * feat: #657 styling updates on vega order dialog * fix: #657 rename set dialog open and awaiting confirmation dialog update * fix: #657 updates on cancel order id check * fix: #657 fix vega tx dialog test * fix: #657 fix cypress trading-deal-tciket test * fix: #657 fix data-testid market test * fix: #470 add use order edit hook * fix: #470 edit order button * Update libs/orders/README.md Co-authored-by: Dexter Edwards * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards * Update libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx Co-authored-by: Dexter Edwards * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards * fix: #657 remove the magic string and use the ordertype enum from types package * fix: #657 guarantee that order.id is present at this point or we need to determine the id of the order * fix: #657 fix translation in dialog * fix: #657 rename wallet types, delete ticket query, set finalized order null in submit * fix: #657 fix deal ticket steps test * fix: #657 remove settings.json * fix: #657 use order submit in orders lib * fix: #470 open edit order modal and update storybook * feat: #470 edit modals set up * fix: #463 final modal links to block explorer * fix: #745 long/short instead of buy/sell * fix: #657 use only one vega tx dialog * fix: #657 keep ref of subscription and unsubscribe * fix: #657 hide cancelled orders * fix: #657 sub only when id set * fix: WIP: trying to unsub when order updated * fix: #745 long/short instead of buy/sell * fix: ensure observable defined * fix: #657 remove redundant test * fix: #470 merge with new order hooks * fix: #470 fix use-order-edit no red update order-list with code * fix: #470 invert order show price last in dialog * fix: #470 able to edit order * fix: #470 fix dialog transition * fix: #656 #609 show Continuous trading and market state from trade grid header * fix: #603 filter out rejected markets * fix: #603 filter out rejected markets * fix: #470 revert to 17.0.2 react * fix: #470 revert to 17.0.2 react * fix: #603 filter out rejected markets & dialog lg width * fix: #609 show trading mode Continuous Trading and hide market state * fix: #656 modify order validation to trade when suspended * fix: #656 fix use order validation tests * fix: #656 format volume no * fix: format volume with positionDecimalPlaces * fix: tests don't need to be async * fix: md:w-[720px] to prevent dialog overflow * fix: add market state translations * fix: imprt type validation props * fix: #470 working edit submit on GTC not on GTT as it is missing expiresAt * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.spec.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update apps/trading/pages/markets/__generated__/Market.ts Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * fix: fix warning messages based on feedback * fix: capitalize trading mode * fix: capitalize trading mode * fix: remove line 72 on markets.cy.ts * fix: don't show trigger if unspecified * fix: format last price and shrink 0 on warning icon * fix: order sizes must be whole numbers for this market and input warning size 20 * fix: order sizes must be whole numbers for this market and input warning size 20 * fix: format market list * fix: #470 fix expiresAt and price unmarshall values * fix: #470 fix expiresAt and price unmarshall values * fix: #470 add extra test on editing order * fix: pass child react node for order edit on vega tx default * fix: status and rejection reason optional * fix: add header transalations and remove commented line * fix: simplify get list of markets * fix: check if order.market undefined * fix: remove cast and check market id Co-authored-by: Dexter Edwards Co-authored-by: Matthew Russell Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> --- apps/trading/pages/index.page.tsx | 15 +- .../order-list/order-edit-dialog.tsx | 102 ++++++++ .../components/order-list/order-list.spec.tsx | 12 +- .../order-list/order-list.stories.tsx | 25 +- .../lib/components/order-list/order-list.tsx | 86 ++++++- .../order-hooks/__generated__/OrderEvent.ts | 4 + .../src/lib/order-hooks/order-event-query.ts | 2 + .../src/lib/order-hooks/use-order-cancel.tsx | 2 +- .../lib/order-hooks/use-order-edit.spec.tsx | 221 ++++++++++++++++++ .../src/lib/order-hooks/use-order-edit.tsx | 119 ++++++++++ .../orders/src/lib/utils/get-default-order.ts | 9 + libs/types/src/__generated__/globalTypes.ts | 10 - libs/types/src/index.ts | 1 + libs/types/src/pagination.ts | 6 + .../vega-transaction-dialog.tsx | 34 ++- libs/wallet/src/wallet-types.ts | 9 +- package.json | 2 +- yarn.lock | 8 +- 18 files changed, 618 insertions(+), 49 deletions(-) create mode 100644 libs/orders/src/lib/components/order-list/order-edit-dialog.tsx create mode 100644 libs/orders/src/lib/order-hooks/use-order-edit.spec.tsx create mode 100644 libs/orders/src/lib/order-hooks/use-order-edit.tsx create mode 100644 libs/types/src/pagination.ts diff --git a/apps/trading/pages/index.page.tsx b/apps/trading/pages/index.page.tsx index d33792245..08453670e 100644 --- a/apps/trading/pages/index.page.tsx +++ b/apps/trading/pages/index.page.tsx @@ -1,5 +1,4 @@ import { gql, useQuery } from '@apollo/client'; -import { MarketTradingMode } from '@vegaprotocol/types'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import orderBy from 'lodash/orderBy'; import { useRouter } from 'next/router'; @@ -20,15 +19,13 @@ const MARKETS_QUERY = gql` } `; -const marketList = ({ markets }: MarketsLanding) => - orderBy( - markets?.filter( - ({ marketTimestamps, tradingMode }) => - marketTimestamps.open && tradingMode === MarketTradingMode.Continuous - ) || [], - ['state', 'marketTimestamps.open', 'id'], +const getMarketList = ({ markets = [] }: MarketsLanding) => { + return orderBy( + markets, + ['marketTimestamps.open', 'id'], ['asc', 'asc', 'asc'] ); +}; export function Index() { const { replace } = useRouter(); @@ -39,7 +36,7 @@ export function Index() { useEffect(() => { if (data) { - const marketId = marketList(data)[0]?.id; + const marketId = getMarketList(data)[0]?.id; // If a default market is found, go to it with the landing dialog open if (marketId) { diff --git a/libs/orders/src/lib/components/order-list/order-edit-dialog.tsx b/libs/orders/src/lib/components/order-list/order-edit-dialog.tsx new file mode 100644 index 000000000..94bf5fd76 --- /dev/null +++ b/libs/orders/src/lib/components/order-list/order-edit-dialog.tsx @@ -0,0 +1,102 @@ +import { + addDecimal, + t, + addDecimalsFormatNumber, +} from '@vegaprotocol/react-helpers'; +import { OrderType } from '@vegaprotocol/types'; +import { FormGroup, Input, InputError, Button } from '@vegaprotocol/ui-toolkit'; +import { useForm } from 'react-hook-form'; +import Icon from 'react-syntax-highlighter'; +import { OrderDialogWrapper } from '@vegaprotocol/wallet'; +import type { Order } from '@vegaprotocol/wallet'; + +interface OrderEditDialogProps { + title: string; + order: Order | null; + edit: (body: Order) => Promise; +} + +interface FormFields { + entryPrice: string; +} + +export const OrderEditDialog = ({ + order, + title, + edit, +}: OrderEditDialogProps) => { + const headerClassName = 'text-h5 font-bold text-black dark:text-white'; + const { + register, + formState: { errors }, + handleSubmit, + } = useForm({ + defaultValues: { + entryPrice: order?.price + ? addDecimal(order?.price, order?.market?.decimalPlaces ?? 0) + : '', + }, + }); + if (!order) return null; + return ( + }> +
+ {order.market && ( +
+

{t(`Market`)}

+

{t(`${order.market.name}`)}

+
+ )} + {order.type === OrderType.Limit && order.market && ( +
+

{t(`Last price`)}

+

+ {addDecimalsFormatNumber(order.price, order.market.decimalPlaces)} +

+
+ )} +
+

{t(`Amount remaining`)}

+

+ {order.side === 'Buy' ? '+' : '-'} + {order.size} +

+
+
+ +
+
{ + await edit({ + ...order, + price: data.entryPrice, + }); + })} + data-testid="edit-order" + > + + + {errors.entryPrice?.message && ( + + {errors.entryPrice.message} + + )} + + +
+
+
+ ); +}; diff --git a/libs/orders/src/lib/components/order-list/order-list.spec.tsx b/libs/orders/src/lib/components/order-list/order-list.spec.tsx index 8115d87bf..8a3635c72 100644 --- a/libs/orders/src/lib/components/order-list/order-list.spec.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.spec.tsx @@ -16,7 +16,12 @@ const generateJsx = ( return ( - + ); @@ -36,7 +41,7 @@ describe('OrderListTable', () => { }); const headers = screen.getAllByRole('columnheader'); - expect(headers).toHaveLength(10); + expect(headers).toHaveLength(11); expect(headers.map((h) => h.textContent?.trim())).toEqual([ 'Market', 'Amount', @@ -47,6 +52,7 @@ describe('OrderListTable', () => { 'Time In Force', 'Created At', 'Updated At', + 'Edit', 'Cancel', ]); }); @@ -67,6 +73,7 @@ describe('OrderListTable', () => { marketOrder.timeInForce, getDateTimeFormat().format(new Date(marketOrder.createdAt)), '-', + 'Edit', 'Cancel', ]; cells.forEach((cell, i) => @@ -92,6 +99,7 @@ describe('OrderListTable', () => { )}`, getDateTimeFormat().format(new Date(limitOrder.createdAt)), '-', + 'Edit', 'Cancel', ]; cells.forEach((cell, i) => diff --git a/libs/orders/src/lib/components/order-list/order-list.stories.tsx b/libs/orders/src/lib/components/order-list/order-list.stories.tsx index 87ddc036e..c00a525c7 100644 --- a/libs/orders/src/lib/components/order-list/order-list.stories.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.stories.tsx @@ -1,5 +1,5 @@ import type { Story, Meta } from '@storybook/react'; -import { OrderType, OrderStatus } from '@vegaprotocol/types'; +import { OrderType, OrderStatus, OrderTimeInForce } from '@vegaprotocol/types'; import { OrderList, OrderListTable } from './order-list'; import { useState } from 'react'; import type { Order, VegaTxState } from '@vegaprotocol/wallet'; @@ -15,7 +15,16 @@ const Template: Story = (args) => { const cancel = () => Promise.resolve(); return (
- + { + return; + }} + setEditOrder={() => { + return; + }} + />
); }; @@ -39,12 +48,22 @@ const Template2: Story = (args) => { price: '1000', market: { name: 'ETH/DAI (30 Jun 2022)', decimalPlaces: 5 }, type: OrderType.Limit, + timeInForce: OrderTimeInForce.GTC, }; const reset = () => null; return ( <>
- + { + return; + }} + setEditOrder={() => { + return; + }} + />
( ({ data, showCancelled = true }, ref) => { const [cancelOrderDialogOpen, setCancelOrderDialogOpen] = useState(false); + const [editOrderDialogOpen, setEditOrderDialogOpen] = useState(false); + const [editOrder, setEditOrder] = useState( + null + ); + const { transaction, updatedOrder, reset, cancel } = useOrderCancel(); + const { + transaction: editTransaction, + updatedOrder: editedOrder, + reset: resetEdit, + edit, + } = useOrderEdit(); const ordersData = showCancelled ? data : data?.filter((o) => o.status !== OrderStatus.Cancelled) || null; - const getDialogTitle = (status?: string) => { + const getCancelDialogTitle = (status?: string) => { switch (status) { case OrderStatus.Cancelled: return 'Order cancelled'; @@ -37,18 +50,51 @@ export const OrderList = forwardRef( return 'Cancellation failed'; } }; + const getEditDialogTitle = () => + editedOrder + ? t( + `Order ${ + editOrder?.market?.tradableInstrument.instrument.code ?? '' + } updated` + ) + : t( + `Edit ${ + editOrder?.market?.tradableInstrument.instrument.code ?? '' + } order` + ); return ( <> - + + + + ); } @@ -57,10 +103,12 @@ export const OrderList = forwardRef( interface OrderListTableProps { data: Orders_party_orders[] | null; cancel: (body?: unknown) => Promise; + setEditOrderDialogOpen: (value: boolean) => void; + setEditOrder: (order: Orders_party_orders | null) => void; } export const OrderListTable = forwardRef( - ({ data, cancel }, ref) => { + ({ data, cancel, setEditOrderDialogOpen, setEditOrder }, ref) => { return ( ( return value ? getDateTimeFormat().format(new Date(value)) : '-'; }} /> + { + if ( + ![ + OrderStatus.Cancelled, + OrderStatus.Rejected, + OrderStatus.Expired, + OrderStatus.Filled, + OrderStatus.Stopped, + ].includes(data.status) + ) { + return ( + + ); + } + return null; + }} + /> { diff --git a/libs/orders/src/lib/order-hooks/__generated__/OrderEvent.ts b/libs/orders/src/lib/order-hooks/__generated__/OrderEvent.ts index 82ce6eb35..d633ec796 100644 --- a/libs/orders/src/lib/order-hooks/__generated__/OrderEvent.ts +++ b/libs/orders/src/lib/order-hooks/__generated__/OrderEvent.ts @@ -15,6 +15,10 @@ export interface OrderEvent_busEvents_event_TimeUpdate { export interface OrderEvent_busEvents_event_Order_market { __typename: "Market"; + /** + * Market ID + */ + id: string; /** * Market full name */ diff --git a/libs/orders/src/lib/order-hooks/order-event-query.ts b/libs/orders/src/lib/order-hooks/order-event-query.ts index a5ea71fa4..8b07cb111 100644 --- a/libs/orders/src/lib/order-hooks/order-event-query.ts +++ b/libs/orders/src/lib/order-hooks/order-event-query.ts @@ -14,8 +14,10 @@ export const ORDER_EVENT_SUB = gql` size price timeInForce + expiresAt side market { + id name decimalPlaces } diff --git a/libs/orders/src/lib/order-hooks/use-order-cancel.tsx b/libs/orders/src/lib/order-hooks/use-order-cancel.tsx index 66d55688a..12e5222f7 100644 --- a/libs/orders/src/lib/order-hooks/use-order-cancel.tsx +++ b/libs/orders/src/lib/order-hooks/use-order-cancel.tsx @@ -67,7 +67,7 @@ export const useOrderCancel = () => { if (res?.signature) { const resId = order.id ?? determineId(res.signature); setUpdatedOrder(null); - // setId(resId); + if (resId) { // Start a subscription looking for the newly created order subRef.current = client diff --git a/libs/orders/src/lib/order-hooks/use-order-edit.spec.tsx b/libs/orders/src/lib/order-hooks/use-order-edit.spec.tsx new file mode 100644 index 000000000..9676b7494 --- /dev/null +++ b/libs/orders/src/lib/order-hooks/use-order-edit.spec.tsx @@ -0,0 +1,221 @@ +import { act, renderHook } from '@testing-library/react-hooks'; +import type { + VegaKeyExtended, + VegaWalletContextShape, +} from '@vegaprotocol/wallet'; +import { + VegaWalletOrderSide, + VegaWalletOrderTimeInForce, + VegaWalletOrderType, +} from '@vegaprotocol/wallet'; +import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet'; +import type { ReactNode } from 'react'; +import { useOrderEdit } from './use-order-edit'; +import type { + OrderEvent, + OrderEvent_busEvents, +} from './__generated__/OrderEvent'; +import { ORDER_EVENT_SUB } from './order-event-query'; +import type { MockedResponse } from '@apollo/client/testing'; +import { MockedProvider } from '@apollo/client/testing'; +import { + MarketTradingMode, + MarketState, + OrderTimeInForce, +} from '@vegaprotocol/types'; +import type { + OrderAmendmentBodyOrderAmendment, + OrderAmendmentBody, +} from '@vegaprotocol/vegawallet-service-api-client'; + +const defaultWalletContext = { + keypair: null, + keypairs: [], + sendTx: jest.fn().mockReturnValue(Promise.resolve(null)), + connect: jest.fn(), + disconnect: jest.fn(), + selectPublicKey: jest.fn(), + connector: null, +}; + +function setup(context?: Partial) { + const mocks: MockedResponse = { + request: { + query: ORDER_EVENT_SUB, + variables: { + partyId: context?.keypair?.pub || '', + }, + }, + result: { + data: { + busEvents: [ + { + type: 'Order', + event: { + type: 'Limit', + id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62', + status: 'Active', + rejectionReason: null, + createdAt: '2022-07-05T14:25:47.815283706Z', + size: '10', + price: '300000', + timeInForce: 'GTC', + side: 'Buy', + market: { + name: 'UNIDAI Monthly (30 Jun 2022)', + decimalPlaces: 5, + __typename: 'Market', + }, + __typename: 'Order', + }, + __typename: 'BusEvent', + } as OrderEvent_busEvents, + ], + }, + }, + }; + const filterMocks: MockedResponse = { + request: { + query: ORDER_EVENT_SUB, + variables: { + partyId: context?.keypair?.pub || '', + }, + }, + result: { + data: { + busEvents: [ + { + type: 'Order', + event: { + type: 'Limit', + id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62', + status: 'Active', + rejectionReason: null, + createdAt: '2022-07-05T14:25:47.815283706Z', + size: '10', + price: '300000', + timeInForce: 'GTC', + side: 'Buy', + market: { + name: 'UNIDAI Monthly (30 Jun 2022)', + decimalPlaces: 5, + __typename: 'Market', + }, + __typename: 'Order', + }, + __typename: 'BusEvent', + } as OrderEvent_busEvents, + ], + }, + }, + }; + + const wrapper = ({ children }: { children: ReactNode }) => ( + + + {children} + + + ); + return renderHook(() => useOrderEdit(), { wrapper }); +} + +const defaultMarket = { + __typename: 'Market', + id: 'market-id', + decimalPlaces: 2, + positionDecimalPlaces: 1, + tradingMode: MarketTradingMode.Continuous, + state: MarketState.Active, + tradableInstrument: { + __typename: 'TradableInstrument', + instrument: { + __typename: 'Instrument', + product: { + __typename: 'Future', + quoteName: 'quote-name', + }, + }, + }, + depth: { + __typename: 'MarketDepth', + lastTrade: { + __typename: 'Trade', + price: '100', + }, + }, +}; + +const order = { + id: 'order-id', + type: VegaWalletOrderType.Limit, + size: '10', + timeInForce: OrderTimeInForce.GTT, // order timeInForce is transformed to wallet timeInForce + side: VegaWalletOrderSide.Buy, + price: '1234567.89', + expiration: new Date('2022-01-01'), + expiresAt: new Date('2022-01-01'), + status: VegaTxStatus.Pending, + rejectionReason: null, + market: { + id: 'market-id', + decimalPlaces: 2, + name: 'ETHDAI', + positionDecimalPlaces: 2, + }, +}; + +describe('useOrderEdit', () => { + it('should edit a correctly formatted order', async () => { + const mockSendTx = jest.fn().mockReturnValue(Promise.resolve({})); + const keypair = { + pub: '0x123', + } as VegaKeyExtended; + const { result } = setup({ + sendTx: mockSendTx, + keypairs: [keypair], + keypair, + }); + + await act(async () => { + result.current.edit(order); + }); + + expect(mockSendTx).toHaveBeenCalledWith({ + pubKey: keypair.pub, + propagate: true, + orderAmendment: { + orderId: 'order-id', + marketId: defaultMarket.id, // Market provided from hook argument + timeInForce: VegaWalletOrderTimeInForce.GTT, + price: { value: '123456789' }, // Decimal removed + sizeDelta: 0, + expiresAt: { value: order.expiration?.getTime() + '000000' }, // Nanoseconds append + } as unknown as OrderAmendmentBodyOrderAmendment, + } as OrderAmendmentBody); + }); + + it('has the correct default state', () => { + const { result } = setup(); + expect(typeof result.current.edit).toEqual('function'); + expect(typeof result.current.reset).toEqual('function'); + expect(result.current.transaction.status).toEqual(VegaTxStatus.Default); + expect(result.current.transaction.txHash).toEqual(null); + expect(result.current.transaction.error).toEqual(null); + }); + + it('should not sendTx if no keypair', async () => { + const mockSendTx = jest.fn(); + const { result } = setup({ + sendTx: mockSendTx, + keypairs: [], + keypair: null, + }); + await act(async () => { + result.current.edit(order); + }); + expect(mockSendTx).not.toHaveBeenCalled(); + }); +}); diff --git a/libs/orders/src/lib/order-hooks/use-order-edit.tsx b/libs/orders/src/lib/order-hooks/use-order-edit.tsx new file mode 100644 index 000000000..4c81abf44 --- /dev/null +++ b/libs/orders/src/lib/order-hooks/use-order-edit.tsx @@ -0,0 +1,119 @@ +import { useApolloClient } from '@apollo/client'; +import { determineId, removeDecimal } from '@vegaprotocol/react-helpers'; +import { useState, useCallback, useEffect, useRef } from 'react'; +import type { Order } from '@vegaprotocol/wallet'; +import { VegaWalletOrderTimeInForce } from '@vegaprotocol/wallet'; +import { useVegaTransaction, useVegaWallet } from '@vegaprotocol/wallet'; +import { ORDER_EVENT_SUB } from './order-event-query'; +import type { Subscription } from 'zen-observable-ts'; +import type { + OrderEvent_busEvents_event_Order, + OrderEvent, + OrderEventVariables, +} from './__generated__'; +import * as Sentry from '@sentry/react'; + +export const useOrderEdit = () => { + const { keypair } = useVegaWallet(); + const { send, transaction, reset: resetTransaction } = useVegaTransaction(); + const [updatedOrder, setUpdatedOrder] = + useState(null); + const client = useApolloClient(); + const subRef = useRef(null); + + const reset = useCallback(() => { + resetTransaction(); + setUpdatedOrder(null); + subRef.current?.unsubscribe(); + }, [resetTransaction]); + + useEffect(() => { + return () => { + resetTransaction(); + setUpdatedOrder(null); + subRef.current?.unsubscribe(); + }; + }, [resetTransaction]); + + const edit = useCallback( + async (order: Order) => { + if (!keypair || !order.market || !order.market.id) { + return; + } + + setUpdatedOrder(null); + + try { + const res = await send({ + pubKey: keypair.pub, + propagate: true, + orderAmendment: { + orderId: order.id, + marketId: order.market.id, + price: { + value: removeDecimal(order.price, order.market?.decimalPlaces), + }, + timeInForce: VegaWalletOrderTimeInForce[order.timeInForce], + sizeDelta: 0, + expiresAt: order.expiresAt + ? { + value: + // Wallet expects timestamp in nanoseconds, + // we don't have that level of accuracy so just append 6 zeroes + new Date(order.expiresAt).getTime().toString() + '000000', + } + : undefined, + }, + }); + + if (res?.signature) { + const resId = order.id ?? determineId(res.signature); + setUpdatedOrder(null); + + if (resId) { + // Start a subscription looking for the newly created order + subRef.current = client + .subscribe({ + query: ORDER_EVENT_SUB, + variables: { partyId: keypair?.pub || '' }, + }) + .subscribe(({ data }) => { + if (!data?.busEvents?.length) { + return; + } + + // No types available for the subscription result + const matchingOrderEvent = data.busEvents.find((e) => { + if (e.event.__typename !== 'Order') { + return false; + } + + return e.event.id === resId; + }); + + if ( + matchingOrderEvent && + matchingOrderEvent.event.__typename === 'Order' + ) { + setUpdatedOrder(matchingOrderEvent.event); + subRef.current?.unsubscribe(); + } + }); + } + } + return res; + } catch (e) { + Sentry.captureException(e); + return; + } + }, + [client, keypair, send] + ); + + return { + transaction, + updatedOrder, + edit, + reset, + }; +}; diff --git a/libs/orders/src/lib/utils/get-default-order.ts b/libs/orders/src/lib/utils/get-default-order.ts index 026af7202..5ea2e99c3 100644 --- a/libs/orders/src/lib/utils/get-default-order.ts +++ b/libs/orders/src/lib/utils/get-default-order.ts @@ -5,6 +5,7 @@ import { } from '@vegaprotocol/wallet'; import { toDecimal } from '@vegaprotocol/react-helpers'; import type { Market } from '../market'; +import type { OrderStatus } from '@vegaprotocol/types'; export type Order = | { @@ -14,6 +15,9 @@ export type Order = side: VegaWalletOrderSide; price?: never; expiration?: never; + rejectionReason: string | null; + status?: OrderStatus; + market?: Market | null; } | { size: string; @@ -22,6 +26,9 @@ export type Order = side: VegaWalletOrderSide; price?: string; expiration?: Date; + rejectionReason: string | null; + status?: OrderStatus; + market?: Market | null; }; export const getDefaultOrder = (market: Market): Order => ({ @@ -29,4 +36,6 @@ export const getDefaultOrder = (market: Market): Order => ({ side: VegaWalletOrderSide.Buy, timeInForce: VegaWalletOrderTimeInForce.IOC, size: String(toDecimal(market.positionDecimalPlaces)), + rejectionReason: null, + market: null, }); diff --git a/libs/types/src/__generated__/globalTypes.ts b/libs/types/src/__generated__/globalTypes.ts index aaf291df8..7ef4cc642 100644 --- a/libs/types/src/__generated__/globalTypes.ts +++ b/libs/types/src/__generated__/globalTypes.ts @@ -293,16 +293,6 @@ export enum WithdrawalStatus { Rejected = "Rejected", } -/** - * Pagination constructs to support cursor based pagination in the API - */ -export interface Pagination { - first?: number | null; - after?: string | null; - last?: number | null; - before?: string | null; -} - //============================================================== // END Enums and Input Objects //============================================================== diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts index b4f74515a..e13521c83 100644 --- a/libs/types/src/index.ts +++ b/libs/types/src/index.ts @@ -1,2 +1,3 @@ export * from './__generated__/globalTypes'; export * from './candle'; +export * from './pagination'; diff --git a/libs/types/src/pagination.ts b/libs/types/src/pagination.ts new file mode 100644 index 000000000..a178c09a7 --- /dev/null +++ b/libs/types/src/pagination.ts @@ -0,0 +1,6 @@ +export interface Pagination { + first?: number; + after?: string; + last?: number; + before?: string; +} diff --git a/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx b/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx index 20a395b70..c9298b102 100644 --- a/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx +++ b/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx @@ -17,6 +17,7 @@ export interface VegaTransactionDialogProps { transaction: VegaTxState; reset: () => void; title?: string; + children?: ReactNode; } const getDialogIntent = ( @@ -45,6 +46,7 @@ export const VegaTransactionDialog = ({ transaction, reset, title = '', + children, }: VegaTransactionDialogProps) => { // open / close dialog useEffect(() => { @@ -75,6 +77,7 @@ export const VegaTransactionDialog = ({ transaction={transaction} finalizedOrder={finalizedOrder} title={title} + children={children} /> ); @@ -84,15 +87,22 @@ interface VegaDialogProps { transaction: VegaTxState; finalizedOrder: Order | null; title: string; + children?: ReactNode; } export const VegaDialog = ({ transaction, finalizedOrder, title, + children, }: VegaDialogProps) => { const { VEGA_EXPLORER_URL } = useEnvironment(); const headerClassName = 'text-h5 font-bold text-black dark:text-white'; + + if (children && transaction.status === VegaTxStatus.Default) { + return
{children}
; + } + // Rejected by wallet if (transaction.status === VegaTxStatus.Requested) { return ( @@ -178,6 +188,17 @@ export const VegaDialog = ({

{t(`Status`)}

{t(`${finalizedOrder.status}`)}

+ {finalizedOrder.type === OrderType.Limit && finalizedOrder.market && ( +
+

{t(`Price`)}

+

+ {addDecimalsFormatNumber( + finalizedOrder.price, + finalizedOrder.market.decimalPlaces + )} +

+
+ )}

{t(`Amount`)}

- {finalizedOrder.type === OrderType.Limit && finalizedOrder.market && ( -
-

{t(`Price`)}

-

- {addDecimalsFormatNumber( - finalizedOrder.price, - finalizedOrder.market.decimalPlaces - )} -

-
- )}
{transaction.txHash && ( @@ -231,7 +241,7 @@ interface OrderDialogWrapperProps { title: string; } -const OrderDialogWrapper = ({ +export const OrderDialogWrapper = ({ children, icon, title, diff --git a/libs/wallet/src/wallet-types.ts b/libs/wallet/src/wallet-types.ts index 1cfce934f..480e1eb4b 100644 --- a/libs/wallet/src/wallet-types.ts +++ b/libs/wallet/src/wallet-types.ts @@ -1,3 +1,4 @@ +import type { OrderTimeInForce } from '@vegaprotocol/types'; import type { DelegateSubmissionBody, OrderCancellationBody, @@ -41,14 +42,18 @@ export interface Market { name: string; positionDecimalPlaces?: number; decimalPlaces: number; + id?: string; } export interface Order { - status: string; - rejectionReason: string | null; + id?: string; + status?: string; + rejectionReason?: string | null; size: string; price: string; market: Market | null; type: string | null; side?: string; + timeInForce: OrderTimeInForce; + expiresAt?: Date | string | null; } diff --git a/package.json b/package.json index adec06216..1fac35868 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@sentry/react": "^6.19.2", "@sentry/tracing": "^6.19.2", "@testing-library/user-event": "^14.2.1", - "@vegaprotocol/vegawallet-service-api-client": "0.4.14", + "@vegaprotocol/vegawallet-service-api-client": "0.4.15", "@walletconnect/ethereum-provider": "^1.7.5", "@web3-react/core": "8.0.20-beta.0", "@web3-react/metamask": "8.0.16-beta.0", diff --git a/yarn.lock b/yarn.lock index f30a920cb..8f46d5c8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6693,10 +6693,10 @@ "@typescript-eslint/types" "5.22.0" eslint-visitor-keys "^3.0.0" -"@vegaprotocol/vegawallet-service-api-client@0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.14.tgz#cdec296644380f95397688e10b753af328c38147" - integrity sha512-xQ/Dg4Bg+3LSHybYHV83i3G7i407Jj8ROElblZ2TTHTW9iHbBhbd/EHtWfUF2C6R6U27+JUZExnFPcZlvNXprA== +"@vegaprotocol/vegawallet-service-api-client@^0.4.15": + version "0.4.15" + resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.15.tgz#b303fec121b9b334a678161a6f66b360aeed5f0d" + integrity sha512-YwJkUgFvFqpA1xPYQ30ILGddgzjwD9lclsu1GvwK2AUX/8e3iUcXyr37wLd/t8mDZ7P3Zb2AsuLJP8uZ6E1GHQ== dependencies: es6-promise "^4.2.4" url-parse "^1.4.3" From ec47d1e953392e05f8cd01cdd436da6877f4ad06 Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Wed, 20 Jul 2022 18:05:43 +0000 Subject: [PATCH 12/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 102 +++++++++++++----- apps/static/src/assets/stagnet1-tranches.json | 4 +- apps/static/src/assets/testnet-tranches.json | 2 +- yarn.lock | 2 +- 4 files changed, 78 insertions(+), 32 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index f9d4e452f..3ab0b8014 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119204.90105771935588971", + "locked_amount": "119145.454710481301725035", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60151.105121713407225726", + "locked_amount": "60092.793792750596992242", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82216.17604630659251819174664", + "locked_amount": "82136.47452664541080873966644", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25304.53117068063800536", + "locked_amount": "25280.000601568772568284", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8236.379346698325276688", + "locked_amount": "8228.39488449142135621", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12655.838145380436", + "locked_amount": "12625.1953124999985", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1682139.173927239343147728", + "locked_amount": "1680807.318607751114092338", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11167.5294767313934686410850097325", + "locked_amount": "11128.3881320616222256392450007992", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3023.45470918949705478036572574200913234", + "locked_amount": "3006.42761472602864637036366609589041111", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2609363.57639846592051001108", + "locked_amount": "2607316.97847518480617530587", "deposits": [ { "amount": "1998.95815", @@ -16811,8 +16811,8 @@ "tranche_start": "2022-06-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", - "total_removed": "65238.81558128788608", - "locked_amount": "14477820.2329651353662326198608863168329278", + "total_removed": "65454.451699833603555", + "locked_amount": "14470600.2820974924262487960225664853041363", "deposits": [ { "amount": "16249.93", @@ -17391,6 +17391,11 @@ "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "tx": "0xe11650c233bf405e04b6087b25afcb96ef70e476200328345729f87af5c718eb" }, + { + "amount": "215.636118545717475", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tx": "0xb305732b2d00b6af088d3b5f48620b1ac92ee9687167b3ab3d97a9a95c9344d6" + }, { "amount": "579.636872035866225", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -17629,6 +17634,12 @@ "tranche_id": 2, "tx": "0xe11650c233bf405e04b6087b25afcb96ef70e476200328345729f87af5c718eb" }, + { + "amount": "215.636118545717475", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tranche_id": 2, + "tx": "0xb305732b2d00b6af088d3b5f48620b1ac92ee9687167b3ab3d97a9a95c9344d6" + }, { "amount": "579.636872035866225", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -17769,8 +17780,8 @@ } ], "total_tokens": "194999.1675", - "withdrawn_tokens": "16001.2271163772044", - "remaining_tokens": "178997.9403836227956" + "withdrawn_tokens": "16216.863234922921875", + "remaining_tokens": "178782.304265077078125" }, { "address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c", @@ -19209,8 +19220,8 @@ "tranche_start": "2021-11-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", - "total_removed": "2417759.129125450953301297", - "locked_amount": "7713166.71794103455990502998443401", + "total_removed": "2418157.485286254868183047", + "locked_amount": "7706467.00509735747822506379279468", "deposits": [ { "amount": "129284.449", @@ -19534,6 +19545,11 @@ "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "tx": "0xcb6935b6ef6fc175efbeda721ef7b1a443763afd5099a5a274a4bc97a27b74aa" }, + { + "amount": "398.35616080391488175", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0x8c9968a8228537544f18188becde255515115ec9aaeea5846281aff807904ab5" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -21310,6 +21326,12 @@ "tranche_id": 3, "tx": "0xcb6935b6ef6fc175efbeda721ef7b1a443763afd5099a5a274a4bc97a27b74aa" }, + { + "amount": "398.35616080391488175", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0x8c9968a8228537544f18188becde255515115ec9aaeea5846281aff807904ab5" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -22656,8 +22678,8 @@ } ], "total_tokens": "359123.469575", - "withdrawn_tokens": "169016.152176872410605", - "remaining_tokens": "190107.317398127589395" + "withdrawn_tokens": "169414.50833767632548675", + "remaining_tokens": "189708.96123732367451325" }, { "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", @@ -23721,7 +23743,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2730615.41556224638408058729949625", + "locked_amount": "2727968.3184981850054142301459967", "deposits": [ { "amount": "552496.6455", @@ -24953,8 +24975,8 @@ "tranche_start": "2022-06-05T00:00:00.000Z", "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", - "total_removed": "423.0179607209", - "locked_amount": "413468.57443843763399729271841704", + "total_removed": "447.9623988229", + "locked_amount": "413144.2792986412522608642009132", "deposits": [ { "amount": "3000", @@ -31603,6 +31625,11 @@ "user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392", "tx": "0x3a09ab938efeceee72457dc54bbfacc522155a7dcd126f7ccdff28110dce5fbc" }, + { + "amount": "24.944438102", + "user": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", + "tx": "0x3c8ec5a3dfac440713b7f8808731c216f62df7ec02fdce5a730ef2c7e89abbd8" + }, { "amount": "11.163032724", "user": "0xF5037DDA4A660d67560200f45380FF8364e35540", @@ -48686,10 +48713,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "24.944438102", + "user": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", + "tranche_id": 5, + "tx": "0x3c8ec5a3dfac440713b7f8808731c216f62df7ec02fdce5a730ef2c7e89abbd8" + } + ], "total_tokens": "200", - "withdrawn_tokens": "0", - "remaining_tokens": "200" + "withdrawn_tokens": "24.944438102", + "remaining_tokens": "175.055561898" }, { "address": "0x3437f6ED844013852650851D769ae7939B5e0aec", @@ -50764,7 +50798,7 @@ "tranche_start": "2021-12-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z", "total_added": "171288.42", - "total_removed": "39680.6212136932989", + "total_removed": "39720.6212136932989", "locked_amount": "0", "deposits": [ { @@ -55129,6 +55163,11 @@ "user": "0x27049a430Df8b89Ae4f899b0383B0C876F9cAcEb", "tx": "0x27055acf670d336b247b0abb16a5a9809e12aad5d96338ef8c7ecd4b8ea94db8" }, + { + "amount": "40", + "user": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", + "tx": "0x8d4388a2dfad5249133cad6c48669948cad556fa44f1ad4ebcc9003cc5c768e9" + }, { "amount": "60.4448387275", "user": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0", @@ -69179,10 +69218,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "40", + "user": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", + "tranche_id": 6, + "tx": "0x8d4388a2dfad5249133cad6c48669948cad556fa44f1ad4ebcc9003cc5c768e9" + } + ], "total_tokens": "40", - "withdrawn_tokens": "0", - "remaining_tokens": "40" + "withdrawn_tokens": "40", + "remaining_tokens": "0" }, { "address": "0xc612391BD2bc1173BF969167d0c71DC6E3E434AE", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index 3ac695e09..a1f306263 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.362125824454593", + "locked_amount": "35.293474124809743", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "251.745338660578356", + "locked_amount": "250.990169964484995", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 4a224bf2b..807f9d409 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "231.14799277016739960022885939878234396", + "locked_amount": "230.45461060375440450022817288178589545", "deposits": [ { "amount": "1000", diff --git a/yarn.lock b/yarn.lock index 8f46d5c8c..f612475e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6693,7 +6693,7 @@ "@typescript-eslint/types" "5.22.0" eslint-visitor-keys "^3.0.0" -"@vegaprotocol/vegawallet-service-api-client@^0.4.15": +"@vegaprotocol/vegawallet-service-api-client@0.4.15": version "0.4.15" resolved "https://registry.yarnpkg.com/@vegaprotocol/vegawallet-service-api-client/-/vegawallet-service-api-client-0.4.15.tgz#b303fec121b9b334a678161a6f66b360aeed5f0d" integrity sha512-YwJkUgFvFqpA1xPYQ30ILGddgzjwD9lclsu1GvwK2AUX/8e3iUcXyr37wLd/t8mDZ7P3Zb2AsuLJP8uZ6E1GHQ== From 307281de42c94b95e8a21fd9df2cd6260b2ed1ed Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Thu, 21 Jul 2022 00:10:30 +0000 Subject: [PATCH 13/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 45 ++++++++++++------- apps/static/src/assets/stagnet1-tranches.json | 4 +- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 3ab0b8014..3dbb3d8b6 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119145.454710481301725035", + "locked_amount": "119085.35764113823226763", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60092.793792750596992242", + "locked_amount": "60033.844166020284736284", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82136.47452664541080873966644", + "locked_amount": "82055.90056413355450578079836", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25280.000601568772568284", + "locked_amount": "25255.201511608934555226", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8228.39488449142135621", + "locked_amount": "8220.323021354166305557", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12625.1953124999985", + "locked_amount": "12594.2170516304355", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1680807.318607751114092338", + "locked_amount": "1679460.88429834479757811", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11128.3881320616222256392450007992", + "locked_amount": "11088.818331492037521038105403135", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "3006.42761472602864637036366609589041111", + "locked_amount": "2989.21413527397184965036158390410958895", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2607316.97847518480617530587", + "locked_amount": "2605247.97772026414466691579", "deposits": [ { "amount": "1998.95815", @@ -16812,7 +16812,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "65454.451699833603555", - "locked_amount": "14470600.2820974924262487960225664853041363", + "locked_amount": "14463301.2989285394388270162675910681995134", "deposits": [ { "amount": "16249.93", @@ -19220,8 +19220,8 @@ "tranche_start": "2021-11-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", - "total_removed": "2418157.485286254868183047", - "locked_amount": "7706467.00509735747822506379279468", + "total_removed": "2418281.701339586123510047", + "locked_amount": "7699693.95467371253433958972627272", "deposits": [ { "amount": "129284.449", @@ -19550,6 +19550,11 @@ "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "tx": "0x8c9968a8228537544f18188becde255515115ec9aaeea5846281aff807904ab5" }, + { + "amount": "124.216053331255327", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0xcf8cdc81ee193ba4bce80db2062d482669154ae4ec5d80ddf4a475d20e32ed9e" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -21332,6 +21337,12 @@ "tranche_id": 3, "tx": "0x8c9968a8228537544f18188becde255515115ec9aaeea5846281aff807904ab5" }, + { + "amount": "124.216053331255327", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0xcf8cdc81ee193ba4bce80db2062d482669154ae4ec5d80ddf4a475d20e32ed9e" + }, { "amount": "509.31853983395369725", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -22678,8 +22689,8 @@ } ], "total_tokens": "359123.469575", - "withdrawn_tokens": "169414.50833767632548675", - "remaining_tokens": "189708.96123732367451325" + "withdrawn_tokens": "169538.72439100758081375", + "remaining_tokens": "189584.74518399241918625" }, { "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", @@ -23743,7 +23754,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2727968.3184981850054142301459967", + "locked_amount": "2725292.245312734283726696965951825", "deposits": [ { "amount": "552496.6455", @@ -24976,7 +24987,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "447.9623988229", - "locked_amount": "413144.2792986412522608642009132", + "locked_amount": "412816.43430211149625119377168952", "deposits": [ { "amount": "3000", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index a1f306263..d2cb696b4 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.293474124809743", + "locked_amount": "35.22408675799087", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "250.990169964484995", + "locked_amount": "250.226908929477392", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 807f9d409..0c00e125e 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "230.45461060375440450022817288178589545", + "locked_amount": "229.75383022577371730022747903982749873", "deposits": [ { "amount": "1000", From 5b730d85c8367a3ff7a5ac8285cbe05eb7e6f5fa Mon Sep 17 00:00:00 2001 From: mattrussell36 Date: Thu, 21 Jul 2022 06:04:31 +0000 Subject: [PATCH 14/19] chore: update tranches Signed-off-by: github-actions[bot] --- apps/static/src/assets/mainnet-tranches.json | 48 ++++++++++++------- apps/static/src/assets/stagnet1-tranches.json | 4 +- apps/static/src/assets/testnet-tranches.json | 2 +- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 3dbb3d8b6..02ac933bc 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -71,7 +71,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "119085.35764113823226763", + "locked_amount": "119027.03701568518786269", "deposits": [ { "amount": "129999.45", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "60033.844166020284736284", + "locked_amount": "59976.637065262864166124", "deposits": [ { "amount": "97499.58", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "82055.90056413355450578079836", + "locked_amount": "81977.70833379207840893149176", "deposits": [ { "amount": "135173.4239508", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "25255.201511608934555226", + "locked_amount": "25231.135472234075823402", "deposits": [ { "amount": "32499.86", @@ -620,7 +620,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "8220.323021354166305557", + "locked_amount": "8212.489759068967612383", "deposits": [ { "amount": "10833.29", @@ -708,7 +708,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "12594.2170516304355", + "locked_amount": "12564.154495018116", "deposits": [ { "amount": "15000", @@ -794,7 +794,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "179856.049568108351", - "locked_amount": "1679460.88429834479757811", + "locked_amount": "1678154.250016267696756174", "deposits": [ { "amount": "1852091.69", @@ -1846,7 +1846,7 @@ "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", "total_removed": "19238.601152747179372649", - "locked_amount": "11088.818331492037521038105403135", + "locked_amount": "11050.4181974506175267769666097872", "deposits": [ { "amount": "2833.333333", @@ -5417,7 +5417,7 @@ "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "24801.000000000000000003", "total_removed": "7821.05162209938", - "locked_amount": "2989.21413527397184965036158390410958895", + "locked_amount": "2972.50947897640675449035956326103500747", "deposits": [ { "amount": "25", @@ -16066,7 +16066,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "144843.5352821052414", - "locked_amount": "2605247.97772026414466691579", + "locked_amount": "2603240.13575045129543246595", "deposits": [ { "amount": "1998.95815", @@ -16812,7 +16812,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "65454.451699833603555", - "locked_amount": "14463301.2989285394388270162675910681995134", + "locked_amount": "14456218.0706074620330102207620852061057042", "deposits": [ { "amount": "16249.93", @@ -19221,7 +19221,7 @@ "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", "total_removed": "2418281.701339586123510047", - "locked_amount": "7699693.95467371253433958972627272", + "locked_amount": "7693121.11274897416980976059276093", "deposits": [ { "amount": "129284.449", @@ -23754,7 +23754,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "1576644.495385096585953516", - "locked_amount": "2725292.245312734283726696965951825", + "locked_amount": "2722695.275716053465499410185669762", "deposits": [ { "amount": "552496.6455", @@ -24987,7 +24987,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "447.9623988229", - "locked_amount": "412816.43430211149625119377168952", + "locked_amount": "412498.28026468064445331084830036", "deposits": [ { "amount": "3000", @@ -50809,7 +50809,7 @@ "tranche_start": "2021-12-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z", "total_added": "171288.42", - "total_removed": "39720.6212136932989", + "total_removed": "39740.6212136932989", "locked_amount": "0", "deposits": [ { @@ -55179,6 +55179,11 @@ "user": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46", "tx": "0x8d4388a2dfad5249133cad6c48669948cad556fa44f1ad4ebcc9003cc5c768e9" }, + { + "amount": "20", + "user": "0x709565FbAe7Df190b8f7FC955E32aB467256fE47", + "tx": "0x2d9dbd0c7de15499e328f933cd278023bf92701bc7d366abc59fee1da0e9f6d8" + }, { "amount": "60.4448387275", "user": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0", @@ -66099,10 +66104,17 @@ "tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "20", + "user": "0x709565FbAe7Df190b8f7FC955E32aB467256fE47", + "tranche_id": 6, + "tx": "0x2d9dbd0c7de15499e328f933cd278023bf92701bc7d366abc59fee1da0e9f6d8" + } + ], "total_tokens": "20", - "withdrawn_tokens": "0", - "remaining_tokens": "20" + "withdrawn_tokens": "20", + "remaining_tokens": "0" }, { "address": "0xA9848843a69D535404cF6E7017D563E1AB7d627A", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index d2cb696b4..967161bf4 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "35.22408675799087", + "locked_amount": "35.156712962962966", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1100", "total_removed": "673.04388635", - "locked_amount": "250.226908929477392", + "locked_amount": "249.485797184170459", "deposits": [ { "amount": "1000", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 0c00e125e..fb9d63860 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -69,7 +69,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "1010.000000000000000001", "total_removed": "668.4622323651", - "locked_amount": "229.75383022577371730022747903982749873", + "locked_amount": "229.07332286910196690022680527016742769", "deposits": [ { "amount": "1000", From 164108e6aa04c1eccd9fc49568e2e7a498c0f586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?= Date: Thu, 21 Jul 2022 09:39:03 +0200 Subject: [PATCH 15/19] feat(#297): use 40% opacity colors in white themed orderbook (#821) --- .../src/lib/grid/cumulative-vol-cell.tsx | 4 ++-- libs/react-helpers/src/lib/grid/vol-cell.tsx | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libs/react-helpers/src/lib/grid/cumulative-vol-cell.tsx b/libs/react-helpers/src/lib/grid/cumulative-vol-cell.tsx index e106d8ec3..955e69aa0 100644 --- a/libs/react-helpers/src/lib/grid/cumulative-vol-cell.tsx +++ b/libs/react-helpers/src/lib/grid/cumulative-vol-cell.tsx @@ -30,7 +30,7 @@ export const CumulativeVol = React.memo( const askBar = relativeAsk ? (
Date: Thu, 21 Jul 2022 09:10:37 +0100 Subject: [PATCH 16/19] chore(#819): ui tweaks (#826) * chore: tweak panel layout/styling * chore: tweak panel layout/styling * chore: add thicker borders --- apps/trading/pages/markets/trade-grid.tsx | 19 +++++++++---------- apps/trading/pages/styles.css | 2 ++ libs/ui-toolkit/src/components/tabs/tabs.tsx | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx index 9a07c2945..ec9c38be0 100644 --- a/apps/trading/pages/markets/trade-grid.tsx +++ b/apps/trading/pages/markets/trade-grid.tsx @@ -65,7 +65,7 @@ export const TradeMarketHeader = ({ const itemValueClassName = 'font-sans tracking-tighter text-black dark:text-white text-ui'; const headerClassName = classNames( - 'w-full p-8 bg-white dark:bg-black', + 'w-full p-8 mb-4 bg-white dark:bg-black', className ); return ( @@ -140,7 +140,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => { - + @@ -153,13 +153,13 @@ export const TradeGrid = ({ market }: TradeGridProps) => { - + - + @@ -169,10 +169,10 @@ export const TradeGrid = ({ market }: TradeGridProps) => { - + @@ -186,12 +186,11 @@ export const TradeGrid = ({ market }: TradeGridProps) => { - + diff --git a/apps/trading/pages/styles.css b/apps/trading/pages/styles.css index 86bf033f2..e80c5f53a 100644 --- a/apps/trading/pages/styles.css +++ b/apps/trading/pages/styles.css @@ -10,8 +10,10 @@ body, html > body { --focus-border: theme('colors.vega.pink'); + --separator-border: theme('colors.black.10'); } html.dark > body { --focus-border: theme('colors.vega.yellow'); + --separator-border: theme('colors.black.70'); } diff --git a/libs/ui-toolkit/src/components/tabs/tabs.tsx b/libs/ui-toolkit/src/components/tabs/tabs.tsx index a69c3ba9c..22b083160 100644 --- a/libs/ui-toolkit/src/components/tabs/tabs.tsx +++ b/libs/ui-toolkit/src/components/tabs/tabs.tsx @@ -60,7 +60,7 @@ export const Tabs = ({ children }: TabsProps) => { return ( {child.props.children} From 065b48535b6c2f9b7bffa5d34e1cb7fbc039b25f Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Thu, 21 Jul 2022 09:18:57 +0100 Subject: [PATCH 17/19] fix: withdrawal max threshold (#830) * chore: fix remaning text for large number * feat: make use max only use account balance, add custom max messages * fix: withdraw threshold limit display * fix: assertions in deposit withdrawy e2e tests --- .../trading-e2e/src/integration/deposit.cy.ts | 2 +- .../src/integration/withdraw.cy.ts | 2 +- libs/deposits/src/lib/deposit-form.spec.tsx | 20 +++++-- libs/deposits/src/lib/deposit-form.tsx | 24 ++++---- libs/deposits/src/lib/deposit-limits.tsx | 56 ++++++++++--------- libs/deposits/src/lib/deposit-manager.tsx | 2 +- .../src/lib/use-get-withdraw-limits.tsx | 8 ++- libs/withdraws/src/lib/withdraw-form.spec.tsx | 13 +++-- libs/withdraws/src/lib/withdraw-form.tsx | 30 +++++++--- libs/withdraws/src/lib/withdraw-limits.tsx | 26 +++++---- .../src/lib/withdraw-manager.spec.tsx | 4 +- libs/withdraws/src/lib/withdraw-manager.tsx | 25 ++++++--- 12 files changed, 129 insertions(+), 83 deletions(-) diff --git a/apps/trading-e2e/src/integration/deposit.cy.ts b/apps/trading-e2e/src/integration/deposit.cy.ts index b9d8abf40..60c600aa7 100644 --- a/apps/trading-e2e/src/integration/deposit.cy.ts +++ b/apps/trading-e2e/src/integration/deposit.cy.ts @@ -53,6 +53,6 @@ describe('deposit form validation', () => { .clear() .type('100') .next(`[data-testid="${formFieldError}"]`) - .should('have.text', 'Amount is above approved amount'); + .should('have.text', 'Insufficient amount in Ethereum wallet'); }); }); diff --git a/apps/trading-e2e/src/integration/withdraw.cy.ts b/apps/trading-e2e/src/integration/withdraw.cy.ts index 1b0e39248..580b35060 100644 --- a/apps/trading-e2e/src/integration/withdraw.cy.ts +++ b/apps/trading-e2e/src/integration/withdraw.cy.ts @@ -58,7 +58,7 @@ describe('withdraw', () => { .clear() .type('1') // Will be above maximum because the vega wallet doesnt have any collateral .next('[data-testid="input-error-text"]') - .should('contain.text', 'Value is above maximum'); + .should('contain.text', 'Insufficient amount in account'); }); it('can set amount using use maximum button', () => { diff --git a/libs/deposits/src/lib/deposit-form.spec.tsx b/libs/deposits/src/lib/deposit-form.spec.tsx index feb18597a..0fae80b5b 100644 --- a/libs/deposits/src/lib/deposit-form.spec.tsx +++ b/libs/deposits/src/lib/deposit-form.spec.tsx @@ -33,7 +33,7 @@ beforeEach(() => { assets: [asset], selectedAsset: undefined, onSelectAsset: jest.fn(), - available: new BigNumber(5), + balance: new BigNumber(5), submitApprove: jest.fn(), submitDeposit: jest.fn(), requestFaucet: jest.fn(), @@ -125,7 +125,7 @@ describe('Deposit form', () => { fireEvent.submit(screen.getByTestId('deposit-form')); expect( - await screen.findByText('Amount is above permitted maximum') + await screen.findByText('Insufficient amount in Ethereum wallet') ).toBeInTheDocument(); }); @@ -133,6 +133,7 @@ describe('Deposit form', () => { render( ); @@ -216,12 +217,13 @@ describe('Deposit form', () => { max: new BigNumber(20), deposited: new BigNumber(10), }; + const balance = new BigNumber(50); render( @@ -229,12 +231,18 @@ describe('Deposit form', () => { // Check deposit limit is displayed expect( - screen.getByText('Max deposit total', { selector: 'th' }) + screen.getByText('Balance available', { selector: 'th' }) + .nextElementSibling + ).toHaveTextContent(balance.toString()); + expect( + screen.getByText('Maximum total deposit amount', { selector: 'th' }) .nextElementSibling ).toHaveTextContent(limits.max.toString()); expect( - screen.getByText('Remaining available', { selector: 'th' }) - .nextElementSibling + screen.getByText('Deposited', { selector: 'th' }).nextElementSibling + ).toHaveTextContent(limits.deposited.toString()); + expect( + screen.getByText('Remaining', { selector: 'th' }).nextElementSibling ).toHaveTextContent(limits.max.minus(limits.deposited).toString()); fireEvent.change(screen.getByLabelText('Amount'), { diff --git a/libs/deposits/src/lib/deposit-form.tsx b/libs/deposits/src/lib/deposit-form.tsx index 149b961d1..f93694d3b 100644 --- a/libs/deposits/src/lib/deposit-form.tsx +++ b/libs/deposits/src/lib/deposit-form.tsx @@ -37,7 +37,7 @@ export interface DepositFormProps { assets: Asset[]; selectedAsset?: Asset; onSelectAsset: (assetId: string) => void; - available: BigNumber | undefined; + balance: BigNumber | undefined; submitApprove: () => Promise; submitDeposit: (args: { assetSource: string; @@ -57,7 +57,7 @@ export const DepositForm = ({ assets, selectedAsset, onSelectAsset, - available, + balance, submitApprove, submitDeposit, requestFaucet, @@ -99,7 +99,7 @@ export const DepositForm = ({ const max = useMemo(() => { const maxApproved = allowance ? allowance : new BigNumber(0); - const maxAvailable = available ? available : new BigNumber(0); + const maxAvailable = balance ? balance : new BigNumber(0); // limits.max is a lifetime deposit limit, so the actual max value for form // input is the max minus whats already been deposited @@ -116,7 +116,7 @@ export const DepositForm = ({ limit: maxLimit, amount: BigNumber.minimum(maxLimit, maxApproved, maxAvailable), }; - }, [limits, allowance, available]); + }, [limits, allowance, balance]); const min = useMemo(() => { // Min viable amount given asset decimals EG for WEI 0.000000000000000001 @@ -198,7 +198,7 @@ export const DepositForm = ({ {selectedAsset && limits && (
- +
)} @@ -212,12 +212,12 @@ export const DepositForm = ({ minSafe: (value) => minSafe(new BigNumber(min))(value), maxSafe: (v) => { const value = new BigNumber(v); - if (value.isGreaterThan(max.approved)) { - return t('Amount is above approved amount'); - } else if (value.isGreaterThan(max.limit)) { - return t('Amount is above permitted maximum'); - } else if (value.isGreaterThan(max.available)) { + if (value.isGreaterThan(max.available)) { return t('Insufficient amount in Ethereum wallet'); + } else if (value.isGreaterThan(max.limit)) { + return t('Amount is above temporary deposit limit'); + } else if (value.isGreaterThan(max.approved)) { + return t('Amount is above approved amount'); } return maxSafe(max.amount)(v); }, @@ -229,10 +229,10 @@ export const DepositForm = ({ {errors.amount.message} )} - {account && selectedAsset && available && ( + {selectedAsset && balance && ( { - setValue('amount', max.amount.toFixed(selectedAsset.decimals)); + setValue('amount', balance.toFixed(selectedAsset.decimals)); clearErrors('amount'); }} > diff --git a/libs/deposits/src/lib/deposit-limits.tsx b/libs/deposits/src/lib/deposit-limits.tsx index 9b80bc72c..b27ab8ced 100644 --- a/libs/deposits/src/lib/deposit-limits.tsx +++ b/libs/deposits/src/lib/deposit-limits.tsx @@ -6,11 +6,11 @@ interface DepositLimitsProps { max: BigNumber; deposited: BigNumber; }; + balance?: BigNumber; } -export const DepositLimits = ({ limits }: DepositLimitsProps) => { +export const DepositLimits = ({ limits, balance }: DepositLimitsProps) => { let maxLimit = ''; - if (limits.max.isEqualTo(Infinity)) { maxLimit = t('No limit'); } else if (limits.max.isGreaterThan(1_000_000)) { @@ -19,29 +19,35 @@ export const DepositLimits = ({ limits }: DepositLimitsProps) => { maxLimit = limits.max.toString(); } + let remaining = ''; + if (limits.deposited.isEqualTo(0)) { + remaining = maxLimit; + } else { + remaining = limits.max.minus(limits.deposited).toString(); + } + return ( - <> -

{t('Deposit limits')}

- - - - - - - - - - - - - - - -
{t('Max deposit total')}{maxLimit}
{t('Deposited')}{limits.deposited.toString()}
- {t('Remaining available')} - - {limits.max.minus(limits.deposited).toString()} -
- + + + + + + + + + + + + + + + + + + + +
{t('Balance available')}{balance ? balance.toString() : 0}
+ {t('Maximum total deposit amount')} + {maxLimit}
{t('Deposited')}{limits.deposited.toString()}
{t('Remaining')}{remaining}
); }; diff --git a/libs/deposits/src/lib/deposit-manager.tsx b/libs/deposits/src/lib/deposit-manager.tsx index 230a23fe3..8a51882e0 100644 --- a/libs/deposits/src/lib/deposit-manager.tsx +++ b/libs/deposits/src/lib/deposit-manager.tsx @@ -105,7 +105,7 @@ export const DepositManager = ({ return ( <> setAssetId(id)} assets={sortBy(assets, 'name')} diff --git a/libs/withdraws/src/lib/use-get-withdraw-limits.tsx b/libs/withdraws/src/lib/use-get-withdraw-limits.tsx index 46013e988..19b582019 100644 --- a/libs/withdraws/src/lib/use-get-withdraw-limits.tsx +++ b/libs/withdraws/src/lib/use-get-withdraw-limits.tsx @@ -20,9 +20,11 @@ export const useGetWithdrawLimits = (asset?: Asset) => { if (!data || !asset) return null; - const max = new BigNumber(addDecimal(data.toString(), asset.decimals)); - + const value = new BigNumber(addDecimal(data.toString(), asset.decimals)); + const max = value.isEqualTo(0) + ? new BigNumber(Infinity) + : value.minus(new BigNumber(addDecimal('1', asset.decimals))); return { - max: max.isEqualTo(0) ? new BigNumber(Infinity) : max, + max, }; }; diff --git a/libs/withdraws/src/lib/withdraw-form.spec.tsx b/libs/withdraws/src/lib/withdraw-form.spec.tsx index df6ad830b..31cda4804 100644 --- a/libs/withdraws/src/lib/withdraw-form.spec.tsx +++ b/libs/withdraws/src/lib/withdraw-form.spec.tsx @@ -26,7 +26,10 @@ beforeEach(() => { props = { assets, min: new BigNumber(0.00001), - max: new BigNumber(100), + max: { + balance: new BigNumber(100), + threshold: new BigNumber(200), + }, limits: { max: new BigNumber(200), }, @@ -75,7 +78,9 @@ describe('Withdrawal form', () => { expect( await screen.findByText('Invalid Ethereum address') ).toBeInTheDocument(); - expect(screen.getByText('Value is above maximum')).toBeInTheDocument(); + expect( + screen.getByText('Insufficient amount in account') + ).toBeInTheDocument(); }); it('fails when submitted amount is less than the minimum limit', async () => { @@ -111,14 +116,14 @@ describe('Withdrawal form', () => { }); }); - it('populates amount field with maximum value when clicking the "use maximum" button', () => { + it('populates amount field with balance value when clicking the "use maximum" button', () => { const asset = props.assets[0]; render(); fireEvent.click(screen.getByText('Use maximum')); expect(screen.getByLabelText('Amount')).toHaveValue( - Number(props.max.toFixed(asset.decimals)) + Number(props.max.balance.toFixed(asset.decimals)) ); }); }); diff --git a/libs/withdraws/src/lib/withdraw-form.tsx b/libs/withdraws/src/lib/withdraw-form.tsx index f1915d465..ae0d6db5d 100644 --- a/libs/withdraws/src/lib/withdraw-form.tsx +++ b/libs/withdraws/src/lib/withdraw-form.tsx @@ -1,10 +1,10 @@ import { ethereumAddress, - maxSafe, minSafe, t, removeDecimal, required, + maxSafe, } from '@vegaprotocol/react-helpers'; import { Button, @@ -15,7 +15,7 @@ import { } from '@vegaprotocol/ui-toolkit'; import { Web3WalletInput } from '@vegaprotocol/web3'; import { useWeb3React } from '@web3-react/core'; -import type BigNumber from 'bignumber.js'; +import BigNumber from 'bignumber.js'; import type { ButtonHTMLAttributes, ReactNode } from 'react'; import { useForm, Controller } from 'react-hook-form'; import type { WithdrawalFields } from './use-withdraw'; @@ -30,7 +30,10 @@ interface FormFields { export interface WithdrawFormProps { assets: Asset[]; - max: BigNumber; + max: { + balance: BigNumber; + threshold: BigNumber; + }; min: BigNumber; selectedAsset?: Asset; limits: { @@ -49,7 +52,7 @@ export const WithdrawForm = ({ onSelectAsset, submitWithdraw, }: WithdrawFormProps) => { - const { account } = useWeb3React(); + const { account: address } = useWeb3React(); const { register, handleSubmit, @@ -60,7 +63,7 @@ export const WithdrawForm = ({ } = useForm({ defaultValues: { asset: selectedAsset?.id, - to: account, + to: address, }, }); const onSubmit = async (fields: FormFields) => { @@ -106,7 +109,6 @@ export const WithdrawForm = ({ )} /> - {errors.asset?.message && ( {errors.asset.message} @@ -132,7 +134,7 @@ export const WithdrawForm = ({
{selectedAsset && limits && (
- +
)} @@ -143,7 +145,17 @@ export const WithdrawForm = ({ {...register('amount', { validate: { required, - maxSafe: (value) => maxSafe(max)(value), + maxSafe: (v) => { + const value = new BigNumber(v); + if (value.isGreaterThan(max.balance)) { + return t('Insufficient amount in account'); + } else if (value.isGreaterThan(max.threshold)) { + return t('Amount is above temporary withdrawal limit'); + } + return maxSafe(BigNumber.minimum(max.balance, max.threshold))( + v + ); + }, minSafe: (value) => minSafe(min)(value), }, })} @@ -157,7 +169,7 @@ export const WithdrawForm = ({ { - setValue('amount', max.toFixed(selectedAsset.decimals)); + setValue('amount', max.balance.toFixed(selectedAsset.decimals)); clearErrors('amount'); }} > diff --git a/libs/withdraws/src/lib/withdraw-limits.tsx b/libs/withdraws/src/lib/withdraw-limits.tsx index 9c9bc26fd..768d9c02c 100644 --- a/libs/withdraws/src/lib/withdraw-limits.tsx +++ b/libs/withdraws/src/lib/withdraw-limits.tsx @@ -5,9 +5,10 @@ interface WithdrawLimitsProps { limits: { max: BigNumber; }; + balance: BigNumber; } -export const WithdrawLimits = ({ limits }: WithdrawLimitsProps) => { +export const WithdrawLimits = ({ limits, balance }: WithdrawLimitsProps) => { let maxLimit = ''; if (limits.max.isEqualTo(Infinity)) { @@ -19,16 +20,17 @@ export const WithdrawLimits = ({ limits }: WithdrawLimitsProps) => { } return ( - <> -

{t('Withdraw limits')}

- - - - - - - -
{t('Maximum')}{maxLimit}
- + + + + + + + + + + + +
{t('Balance available')}{balance.toString()}
{t('Maximum withdrawal')}{maxLimit}
); }; diff --git a/libs/withdraws/src/lib/withdraw-manager.spec.tsx b/libs/withdraws/src/lib/withdraw-manager.spec.tsx index 6835fb691..83b5caa8e 100644 --- a/libs/withdraws/src/lib/withdraw-manager.spec.tsx +++ b/libs/withdraws/src/lib/withdraw-manager.spec.tsx @@ -111,7 +111,9 @@ it('Correct min max values provided to form', async () => { target: { value: '2' }, }); fireEvent.submit(screen.getByTestId('withdraw-form')); - expect(await screen.findByText('Value is above maximum')).toBeInTheDocument(); + expect( + await screen.findByText('Insufficient amount in account') + ).toBeInTheDocument(); expect(mockSubmit).not.toBeCalled(); }); diff --git a/libs/withdraws/src/lib/withdraw-manager.tsx b/libs/withdraws/src/lib/withdraw-manager.tsx index 7557966c2..056bbe09a 100644 --- a/libs/withdraws/src/lib/withdraw-manager.tsx +++ b/libs/withdraws/src/lib/withdraw-manager.tsx @@ -38,22 +38,31 @@ export const WithdrawManager = ({ return assets?.find((a) => a.id === assetId); }, [assets, assetId]); + const account = useMemo(() => { + return accounts.find( + (a) => a.type === AccountType.General && a.asset.id === asset?.id + ); + }, [asset, accounts]); + const limits = useGetWithdrawLimits(asset); const max = useMemo(() => { if (!asset) { - return new BigNumber(0); + return { + balance: new BigNumber(0), + threshold: new BigNumber(0), + }; } - const account = accounts.find( - (a) => a.type === AccountType.General && a.asset.id === asset.id - ); - - const v = account + const balance = account ? new BigNumber(addDecimal(account.balance, asset.decimals)) : new BigNumber(0); - return BigNumber.minimum(v, limits ? limits.max : new BigNumber(Infinity)); - }, [asset, accounts, limits]); + + return { + balance, + threshold: limits ? limits.max : new BigNumber(Infinity), + }; + }, [asset, account, limits]); const min = useMemo(() => { return asset From f7c869674a4b591ad8ef32d79724c53702eb23a6 Mon Sep 17 00:00:00 2001 From: Joe Tsang <30622993+jtsang586@users.noreply.github.com> Date: Thu, 21 Jul 2022 10:15:56 +0100 Subject: [PATCH 18/19] Chore/switch capsule version to use main (#832) * fix: switch to main * chore: update to use capsule v0.2.1 --- .github/workflows/capsule-cypress-manual-trigger.yml | 2 +- .github/workflows/capsule-cypress-nightly.yml | 2 +- .github/workflows/capsule-cypress.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/capsule-cypress-manual-trigger.yml b/.github/workflows/capsule-cypress-manual-trigger.yml index feacec4c4..5856baa6a 100644 --- a/.github/workflows/capsule-cypress-manual-trigger.yml +++ b/.github/workflows/capsule-cypress-manual-trigger.yml @@ -52,7 +52,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: v0.2.0 + ref: v0.2.1 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' diff --git a/.github/workflows/capsule-cypress-nightly.yml b/.github/workflows/capsule-cypress-nightly.yml index a810de612..ab9de0041 100644 --- a/.github/workflows/capsule-cypress-nightly.yml +++ b/.github/workflows/capsule-cypress-nightly.yml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: v0.2.0 + ref: v0.2.1 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' diff --git a/.github/workflows/capsule-cypress.yml b/.github/workflows/capsule-cypress.yml index d2af41c37..ee233b9bd 100644 --- a/.github/workflows/capsule-cypress.yml +++ b/.github/workflows/capsule-cypress.yml @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v2 with: repository: vegaprotocol/vegacapsule - ref: v0.2.0 + ref: v0.2.1 token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} path: './capsule' From 8ce51dddbc8a259f44eb54c5b79555bc854b4fb8 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Thu, 21 Jul 2022 11:57:30 +0200 Subject: [PATCH 19/19] Feat/625 market info more details - open interest, liquidity info, market and instrument ids, price and settlement asset in the header - Part 1 (#823) * fix: #625 update the info market query with new fields * fix: #625 generate code for info market query and fix pagination being removed from globalTypes * fix: add id and filter no trading auctions * fix: add id and format labels * fix: add name and code to instrument * fix: add name and code to instrument * fix: format check * feat: add settlement asset and mark price * feat: add liquidity parameters * fix: remove settings.json * fix: price instead of mark price label * fix: add instrument metadata * fix: remove unused set lodash * fix: move Liquidity monitoring parameters after price monitoring * fix: rename instrument metadata to metadata * fix: settlement asset could be undefined * Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo into feat/625-market-info-more-details-open-interest-liquidity-ids-risk * fix: format market id and trading mode label in key details * fix: display settlement asset if defined * fix: format trade-grid.tsx --- .../trading/pages/markets/[marketId].page.tsx | 10 ++ .../pages/markets/__generated__/Market.ts | 32 +++++ apps/trading/pages/markets/trade-grid.tsx | 31 ++++- .../__generated__/MarketInfoQuery.ts | 124 +++++++++++++++++- .../src/components/info-market.tsx | 109 ++++++++++++--- .../__generated__/MarketList.ts | 6 +- .../markets-data-provider.ts | 1 + .../src/lib/utils/market-list.utils.ts | 8 +- .../components/order-list/order-list.spec.tsx | 8 +- .../lib/components/order-list/order-list.tsx | 9 +- libs/types/src/__generated__/index.ts | 1 + libs/types/src/index.ts | 2 +- .../vega-transaction-dialog.tsx | 9 +- 13 files changed, 317 insertions(+), 33 deletions(-) create mode 100644 libs/types/src/__generated__/index.ts diff --git a/apps/trading/pages/markets/[marketId].page.tsx b/apps/trading/pages/markets/[marketId].page.tsx index 9b27255ea..7f56ad4b8 100644 --- a/apps/trading/pages/markets/[marketId].page.tsx +++ b/apps/trading/pages/markets/[marketId].page.tsx @@ -41,6 +41,16 @@ const MARKET_QUERY = gql` metadata { tags } + product { + ... on Future { + quoteName + settlementAsset { + id + symbol + name + } + } + } } } marketTimestamps { diff --git a/apps/trading/pages/markets/__generated__/Market.ts b/apps/trading/pages/markets/__generated__/Market.ts index 8394d1307..cf1a57310 100644 --- a/apps/trading/pages/markets/__generated__/Market.ts +++ b/apps/trading/pages/markets/__generated__/Market.ts @@ -61,6 +61,34 @@ export interface Market_market_tradableInstrument_instrument_metadata { tags: string[] | null; } +export interface Market_market_tradableInstrument_instrument_product_settlementAsset { + __typename: "Asset"; + /** + * The id of the asset + */ + id: string; + /** + * The symbol of the asset (e.g: GBP) + */ + symbol: string; + /** + * The full name of the asset (e.g: Great British Pound) + */ + name: string; +} + +export interface Market_market_tradableInstrument_instrument_product { + __typename: "Future"; + /** + * String representing the quote (e.g. BTCUSD -> USD is quote) + */ + quoteName: string; + /** + * The name of the asset (string) + */ + settlementAsset: Market_market_tradableInstrument_instrument_product_settlementAsset; +} + export interface Market_market_tradableInstrument_instrument { __typename: "Instrument"; /** @@ -75,6 +103,10 @@ export interface Market_market_tradableInstrument_instrument { * Metadata for this instrument */ metadata: Market_market_tradableInstrument_instrument_metadata; + /** + * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union) + */ + product: Market_market_tradableInstrument_instrument_product; } export interface Market_market_tradableInstrument { diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx index ec9c38be0..adf274aa9 100644 --- a/apps/trading/pages/markets/trade-grid.tsx +++ b/apps/trading/pages/markets/trade-grid.tsx @@ -85,14 +85,14 @@ export const TradeMarketHeader = ({ className="flex flex-auto items-start gap-64 overflow-x-auto whitespace-nowrap" >
- Change (24h) + {t('Change (24h)')}
- Volume + {t('Volume')} {market.data && market.data.indicativeVolume !== '0' ? addDecimalsFormatNumber( @@ -103,7 +103,7 @@ export const TradeMarketHeader = ({
- Trading mode + {t('Trading mode')} {market.tradingMode === MarketTradingMode.MonitoringAuction && market.data?.trigger && @@ -114,6 +114,29 @@ export const TradeMarketHeader = ({ : formatLabel(market.tradingMode)}
+
+ {t('Price')} + + {market.data && market.data.markPrice !== '0' + ? addDecimalsFormatNumber( + market.data.markPrice, + market.decimalPlaces + ) + : '-'} + +
+ {market.tradableInstrument.instrument.product?.settlementAsset + ?.symbol && ( +
+ {t('Settlement asset')} + + { + market.tradableInstrument.instrument.product?.settlementAsset + ?.symbol + } + +
+ )}
@@ -159,7 +182,7 @@ export const TradeGrid = ({ market }: TradeGridProps) => { - + diff --git a/libs/deal-ticket/src/components/__generated__/MarketInfoQuery.ts b/libs/deal-ticket/src/components/__generated__/MarketInfoQuery.ts index c45bd414a..a029d366f 100644 --- a/libs/deal-ticket/src/components/__generated__/MarketInfoQuery.ts +++ b/libs/deal-ticket/src/components/__generated__/MarketInfoQuery.ts @@ -3,12 +3,36 @@ // @generated // This file was automatically generated and should not be edited. -import { MarketState, MarketTradingMode } from "@vegaprotocol/types"; +import { MarketState, MarketTradingMode, AccountType } from "@vegaprotocol/types"; // ==================================================== // GraphQL query operation: MarketInfoQuery // ==================================================== +export interface MarketInfoQuery_market_accounts_asset { + __typename: "Asset"; + /** + * The id of the asset + */ + id: string; +} + +export interface MarketInfoQuery_market_accounts { + __typename: "Account"; + /** + * Account type (General, Margin, etc) + */ + type: AccountType; + /** + * Asset, the 'currency' + */ + asset: MarketInfoQuery_market_accounts_asset; + /** + * Balance as string - current account balance (approx. as balances can be updated several times per second) + */ + balance: string; +} + export interface MarketInfoQuery_market_fees_factors { __typename: "FeeFactors"; /** @@ -125,6 +149,42 @@ export interface MarketInfoQuery_market_data { * the aggregated volume being offered at the best static offer price, excluding pegged orders. */ bestStaticOfferVolume: string; + /** + * the sum of the size of all positions greater than 0. + */ + openInterest: string; +} + +export interface MarketInfoQuery_market_liquidityMonitoringParameters_targetStakeParameters { + __typename: "TargetStakeParameters"; + /** + * Specifies length of time window expressed in seconds for target stake calculation + */ + timeWindow: number; + /** + * Specifies scaling factors used in target stake calculation + */ + scalingFactor: number; +} + +export interface MarketInfoQuery_market_liquidityMonitoringParameters { + __typename: "LiquidityMonitoringParameters"; + /** + * Specifies the triggering ratio for entering liquidity auction + */ + triggeringRatio: number; + /** + * Specifies parameters related to target stake calculation + */ + targetStakeParameters: MarketInfoQuery_market_liquidityMonitoringParameters_targetStakeParameters; +} + +export interface MarketInfoQuery_market_tradableInstrument_instrument_metadata { + __typename: "InstrumentMetadata"; + /** + * An arbitrary list of tags to associated to associate to the Instrument (string list) + */ + tags: string[] | null; } export interface MarketInfoQuery_market_tradableInstrument_instrument_product_settlementAsset { @@ -143,6 +203,28 @@ export interface MarketInfoQuery_market_tradableInstrument_instrument_product_se name: string; } +export interface MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecForSettlementPrice { + __typename: "OracleSpec"; + /** + * id is a hash generated from the OracleSpec data. + */ + id: string; +} + +export interface MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination { + __typename: "OracleSpec"; + /** + * id is a hash generated from the OracleSpec data. + */ + id: string; +} + +export interface MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecBinding { + __typename: "OracleSpecToFutureBinding"; + settlementPriceProperty: string; + tradingTerminationProperty: string; +} + export interface MarketInfoQuery_market_tradableInstrument_instrument_product { __typename: "Future"; /** @@ -153,10 +235,38 @@ export interface MarketInfoQuery_market_tradableInstrument_instrument_product { * The name of the asset (string) */ settlementAsset: MarketInfoQuery_market_tradableInstrument_instrument_product_settlementAsset; + /** + * The oracle spec describing the oracle data of interest for settlement price. + */ + oracleSpecForSettlementPrice: MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecForSettlementPrice; + /** + * The oracle spec describing the oracle data of interest for trading termination. + */ + oracleSpecForTradingTermination: MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecForTradingTermination; + /** + * The binding between the oracle spec and the settlement price + */ + oracleSpecBinding: MarketInfoQuery_market_tradableInstrument_instrument_product_oracleSpecBinding; } export interface MarketInfoQuery_market_tradableInstrument_instrument { __typename: "Instrument"; + /** + * Uniquely identify an instrument across all instruments available on Vega (string) + */ + id: string; + /** + * Full and fairly descriptive name for the instrument + */ + name: string; + /** + * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string) + */ + code: string; + /** + * Metadata for this instrument + */ + metadata: MarketInfoQuery_market_tradableInstrument_instrument_metadata; /** * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union) */ @@ -258,14 +368,14 @@ export interface MarketInfoQuery_market { /** * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct * number denominated in the currency of the Market. (uint64) - * + * * Examples: * Currency Balance decimalPlaces Real Balance * GBP 100 0 GBP 100 * GBP 100 2 GBP 1.00 * GBP 100 4 GBP 0.01 * GBP 1 4 GBP 0.0001 ( 0.01p ) - * + * * GBX (pence) 100 0 GBP 1.00 (100p ) * GBX (pence) 100 2 GBP 0.01 ( 1p ) * GBX (pence) 100 4 GBP 0.0001 ( 0.01p ) @@ -286,6 +396,10 @@ export interface MarketInfoQuery_market { * Current mode of execution of the market */ tradingMode: MarketTradingMode; + /** + * Get account for a party or market + */ + accounts: MarketInfoQuery_market_accounts[] | null; /** * Fees related data */ @@ -302,6 +416,10 @@ export interface MarketInfoQuery_market { * marketData for the given market */ data: MarketInfoQuery_market_data | null; + /** + * Liquidity monitoring parameters for the market + */ + liquidityMonitoringParameters: MarketInfoQuery_market_liquidityMonitoringParameters; /** * An instance of or reference to a tradable instrument. */ diff --git a/libs/deal-ticket/src/components/info-market.tsx b/libs/deal-ticket/src/components/info-market.tsx index e749fd630..45cabc56c 100644 --- a/libs/deal-ticket/src/components/info-market.tsx +++ b/libs/deal-ticket/src/components/info-market.tsx @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { addDecimalsFormatNumber, + formatLabel, formatNumber, formatNumberPercentage, t, @@ -15,10 +16,7 @@ import { import startCase from 'lodash/startCase'; import pick from 'lodash/pick'; import omit from 'lodash/omit'; -import type { - MarketInfoQuery, - MarketInfoQuery_market, -} from './__generated__/MarketInfoQuery'; +import type { MarketInfoQuery, MarketInfoQuery_market } from './__generated__'; import BigNumber from 'bignumber.js'; import { gql, useQuery } from '@apollo/client'; @@ -31,6 +29,13 @@ const MARKET_INFO_QUERY = gql` positionDecimalPlaces state tradingMode + accounts { + type + asset { + id + } + balance + } fees { factors { makerFee @@ -53,6 +58,13 @@ const MARKET_INFO_QUERY = gql` short long } + accounts { + type + asset { + id + } + balance + } data { market { id @@ -64,9 +76,23 @@ const MARKET_INFO_QUERY = gql` bestStaticBidVolume bestStaticOfferVolume indicativeVolume + openInterest + } + liquidityMonitoringParameters { + triggeringRatio + targetStakeParameters { + timeWindow + scalingFactor + } } tradableInstrument { instrument { + id + name + code + metadata { + tags + } product { ... on Future { quoteName @@ -75,6 +101,16 @@ const MARKET_INFO_QUERY = gql` symbol name } + oracleSpecForSettlementPrice { + id + } + oracleSpecForTradingTermination { + id + } + oracleSpecBinding { + settlementPriceProperty + tradingTerminationProperty + } } } } @@ -159,19 +195,28 @@ export const Info = ({ market }: InfoProps) => { ), }, ]; + + const keyDetails = pick( + market, + 'name', + 'decimalPlaces', + 'positionDecimalPlaces', + 'tradingMode', + 'state', + 'id' as 'marketId' + ); const marketSpecPanels = [ { title: t('Key details'), content: ( ), }, @@ -180,8 +225,28 @@ export const Info = ({ market }: InfoProps) => { content: ( + ), + }, + { + title: t('Metadata'), + content: ( + { + const [key, value] = tag.split(':'); + return { [key]: value }; + }) + .reduce((acc, curr) => ({ ...acc, ...curr }), {}), }} /> ), @@ -212,6 +277,18 @@ export const Info = ({ market }: InfoProps) => { content: , }) ), + { + title: t('Liquidity monitoring parameters'), + content: ( + + ), + }, ]; return ( @@ -261,7 +338,7 @@ const Row = ({ ? decimalPlaces ? addDecimalsFormatNumber(value, decimalPlaces) : asPercentage - ? formatNumberPercentage(new BigNumber(value)) + ? formatNumberPercentage(new BigNumber(value * 100)) : formatNumber(Number(value)) : value} @@ -283,7 +360,7 @@ export const MarketInfoTable = ({ decimalPlaces, asPercentage, unformatted, - omits = ['id', '__typename'], + omits = ['__typename'], }: MarketInfoTableProps) => { return ( diff --git a/libs/market-list/src/lib/components/markets-container/__generated__/MarketList.ts b/libs/market-list/src/lib/components/markets-container/__generated__/MarketList.ts index e5bdaaee7..ba2894224 100644 --- a/libs/market-list/src/lib/components/markets-container/__generated__/MarketList.ts +++ b/libs/market-list/src/lib/components/markets-container/__generated__/MarketList.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { Interval, MarketState } from "@vegaprotocol/types"; +import { Interval, MarketState, MarketTradingMode } from "@vegaprotocol/types"; // ==================================================== // GraphQL query operation: MarketList @@ -112,6 +112,10 @@ export interface MarketList_markets { * Current state of the market */ state: MarketState; + /** + * Current mode of execution of the market + */ + tradingMode: MarketTradingMode; /** * marketData for the given market */ diff --git a/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts b/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts index 32121c091..b8ac8c99f 100644 --- a/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts +++ b/libs/market-list/src/lib/components/markets-container/markets-data-provider.ts @@ -54,6 +54,7 @@ export const MARKET_LIST_QUERY = gql` id decimalPlaces state + tradingMode data { market { id diff --git a/libs/market-list/src/lib/utils/market-list.utils.ts b/libs/market-list/src/lib/utils/market-list.utils.ts index 7a04d36d1..7b3ce4790 100644 --- a/libs/market-list/src/lib/utils/market-list.utils.ts +++ b/libs/market-list/src/lib/utils/market-list.utils.ts @@ -1,4 +1,4 @@ -import { MarketState } from '@vegaprotocol/types'; +import { MarketState, MarketTradingMode } from '@vegaprotocol/types'; import orderBy from 'lodash/orderBy'; import type { MarketList, @@ -13,7 +13,11 @@ export const lastPrice = ({ candles }: MarketList_markets) => export const mapDataToMarketList = ({ markets }: MarketList) => orderBy( markets - ?.filter((m) => m.state !== MarketState.Rejected) + ?.filter( + (m) => + m.state !== MarketState.Rejected && + m.tradingMode !== MarketTradingMode.NoTrading + ) .map((m) => { return { id: m.id, diff --git a/libs/orders/src/lib/components/order-list/order-list.spec.tsx b/libs/orders/src/lib/components/order-list/order-list.spec.tsx index 8a3635c72..4ab543b4f 100644 --- a/libs/orders/src/lib/components/order-list/order-list.spec.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.spec.tsx @@ -1,5 +1,9 @@ import { act, render, screen } from '@testing-library/react'; -import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers'; +import { + addDecimal, + formatLabel, + getDateTimeFormat, +} from '@vegaprotocol/react-helpers'; import type { Orders_party_orders } from '../__generated__/Orders'; import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types'; import { OrderListTable } from './order-list'; @@ -118,7 +122,7 @@ describe('OrderListTable', () => { }); const cells = screen.getAllByRole('gridcell'); expect(cells[3]).toHaveTextContent( - `${rejectedOrder.status}: ${rejectedOrder.rejectionReason}` + `${rejectedOrder.status}: ${formatLabel(rejectedOrder.rejectionReason)}` ); }); }); diff --git a/libs/orders/src/lib/components/order-list/order-list.tsx b/libs/orders/src/lib/components/order-list/order-list.tsx index 4af3e103f..6cc16a331 100644 --- a/libs/orders/src/lib/components/order-list/order-list.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.tsx @@ -1,6 +1,11 @@ import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types'; import type { Orders_party_orders } from '../__generated__/Orders'; -import { addDecimal, getDateTimeFormat, t } from '@vegaprotocol/react-helpers'; +import { + addDecimal, + formatLabel, + getDateTimeFormat, + t, +} from '@vegaprotocol/react-helpers'; import { AgGridDynamic as AgGrid, Button } from '@vegaprotocol/ui-toolkit'; import type { ICellRendererParams, @@ -139,7 +144,7 @@ export const OrderListTable = forwardRef( field="status" valueFormatter={({ value, data }: ValueFormatterParams) => { if (value === OrderStatus.Rejected) { - return `${value}: ${data.rejectionReason}`; + return `${value}: ${formatLabel(data.rejectionReason)}`; } return value; diff --git a/libs/types/src/__generated__/index.ts b/libs/types/src/__generated__/index.ts new file mode 100644 index 000000000..1eba4846b --- /dev/null +++ b/libs/types/src/__generated__/index.ts @@ -0,0 +1 @@ +export * from './globalTypes'; diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts index e13521c83..f01cb9e4c 100644 --- a/libs/types/src/index.ts +++ b/libs/types/src/index.ts @@ -1,3 +1,3 @@ -export * from './__generated__/globalTypes'; +export * from './__generated__'; export * from './candle'; export * from './pagination'; diff --git a/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx b/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx index c9298b102..7f142d263 100644 --- a/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx +++ b/libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx @@ -4,7 +4,11 @@ import type { VegaTxState } from '../use-vega-transaction'; import { VegaTxStatus } from '../use-vega-transaction'; import { Icon, Loader } from '@vegaprotocol/ui-toolkit'; import type { ReactNode } from 'react'; -import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers'; +import { + addDecimalsFormatNumber, + formatLabel, + t, +} from '@vegaprotocol/react-helpers'; import { useEnvironment } from '@vegaprotocol/environment'; import { OrderType } from '@vegaprotocol/types'; import type { Order } from '../wallet-types'; @@ -169,7 +173,8 @@ export const VegaDialog = ({ icon={} >

- {t(`Reason: ${finalizedOrder.rejectionReason}`)} + {finalizedOrder.rejectionReason && + t(`Reason: ${formatLabel(finalizedOrder.rejectionReason)}`)}

);