Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo

This commit is contained in:
madalinaraicu 2022-06-27 15:38:20 +01:00
commit e6eda21a33
167 changed files with 3228 additions and 1550 deletions

View File

@ -5,12 +5,14 @@ Feature: Transactions Page
When I navigate to the transactions page When I navigate to the transactions page
Then transactions page is correctly displayed Then transactions page is correctly displayed
@ignore
Scenario: Navigate to transaction details page Scenario: Navigate to transaction details page
Given I am on the homepage Given I am on the homepage
When I navigate to the transactions page When I navigate to the transactions page
And I click on the top transaction And I click on the top transaction
Then transaction details are displayed Then transaction details are displayed
@ignore
Scenario: Navigate to transactions page using mobile Scenario: Navigate to transactions page using mobile
Given I am on mobile and open the toggle menu Given I am on mobile and open the toggle menu
When I navigate to the transactions page When I navigate to the transactions page

View File

@ -36,7 +36,12 @@ export const JumpTo = ({
placeholder={placeholder} placeholder={placeholder}
className="max-w-[200px]" className="max-w-[200px]"
/> />
<Button data-testid="go-submit" variant="secondary" type="submit"> <Button
data-testid="go-submit"
variant="secondary"
boxShadow={false}
type="submit"
>
{t('Go')} {t('Go')}
</Button> </Button>
</div> </div>

View File

@ -75,7 +75,12 @@ export const Search = () => {
</InputError> </InputError>
)} )}
</FormGroup> </FormGroup>
<Button type="submit" variant="secondary" data-testid="search-button"> <Button
type="submit"
boxShadow={false}
variant="secondary"
data-testid="search-button"
>
{t('Search')} {t('Search')}
</Button> </Button>
</form> </form>

View File

@ -7,7 +7,6 @@ import { DealTicketSteps } from './deal-ticket-steps';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { gql, useQuery } from '@apollo/client'; import { gql, useQuery } from '@apollo/client';
import { DealTicketBalance } from './deal-ticket-balance'; import { DealTicketBalance } from './deal-ticket-balance';
import * as React from 'react';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery'; import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
const tempEmptyText = <p>Please select a market from the markets page</p>; const tempEmptyText = <p>Please select a market from the markets page</p>;

View File

@ -35,7 +35,7 @@ export const DrawerToggle = ({
}, [variant]); }, [variant]);
return ( return (
<Button variant="inline" className={classes} onClick={onToggle}> <Button variant="inline-link" className={classes} onClick={onToggle}>
<Icon name={iconName as IconName} /> <Icon name={iconName as IconName} />
</Button> </Button>
); );

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import { makeDataProvider } from '@vegaprotocol/react-helpers'; import { makeDataProvider } from '@vegaprotocol/react-helpers';
import type { import type {
@ -87,15 +88,16 @@ export const FILTERS_QUERY = gql`
`; `;
const update = ( const update = (
draft: SimpleMarkets_markets[], data: SimpleMarkets_markets[],
delta: SimpleMarketDataSub_marketData delta: SimpleMarketDataSub_marketData
) => { ) =>
const index = draft.findIndex((m) => m.id === delta.market.id); produce(data, (draft) => {
if (index !== -1) { const index = draft.findIndex((m) => m.id === delta.market.id);
draft[index].data = delta; if (index !== -1) {
} draft[index].data = delta;
// @TODO - else push new market to draft }
}; // @TODO - else push new market to draft
});
const getData = (responseData: SimpleMarkets) => responseData.markets; const getData = (responseData: SimpleMarkets) => responseData.markets;
const getDelta = ( const getDelta = (

View File

@ -101,7 +101,7 @@ const SimpleMarketList = () => {
<div className="absolute right-16 top-1/2 -translate-y-1/2"> <div className="absolute right-16 top-1/2 -translate-y-1/2">
<Button <Button
onClick={() => onClick(market.id)} onClick={() => onClick(market.id)}
variant="inline" variant="inline-link"
prependIconName="chevron-right" prependIconName="chevron-right"
/> />
</div> </div>

View File

@ -70,7 +70,7 @@ export default ({ steps }: StepperProps) => {
{index === steps.length - 1 ? 'Finish' : 'Continue'} {index === steps.length - 1 ? 'Finish' : 'Continue'}
</Button> </Button>
<Button <Button
variant="inline" variant="inline-link"
disabled={index === 0} disabled={index === 0}
onClick={handleBack} onClick={handleBack}
> >

View File

@ -38,7 +38,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45", "total_added": "129999.45",
"total_removed": "0", "total_removed": "0",
"locked_amount": "125847.249906896297139465", "locked_amount": "124661.013711767544930765",
"deposits": [ "deposits": [
{ {
"amount": "129999.45", "amount": "129999.45",
@ -488,7 +488,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58", "total_added": "97499.58",
"total_removed": "0", "total_removed": "0",
"locked_amount": "66666.63057997215661197", "locked_amount": "65503.04337545080622283",
"deposits": [ "deposits": [
{ {
"amount": "97499.58", "amount": "97499.58",
@ -521,7 +521,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508", "total_added": "135173.4239508",
"total_removed": "0", "total_removed": "0",
"locked_amount": "91121.77448920241599915064844", "locked_amount": "89531.35166557213976323285608",
"deposits": [ "deposits": [
{ {
"amount": "135173.4239508", "amount": "135173.4239508",
@ -554,7 +554,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86", "total_added": "32499.86",
"total_removed": "0", "total_removed": "0",
"locked_amount": "28045.500213863789588014", "locked_amount": "27555.99917099787937361",
"deposits": [ "deposits": [
{ {
"amount": "32499.86", "amount": "32499.86",
@ -587,7 +587,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29", "total_added": "10833.29",
"total_removed": "0", "total_removed": "0",
"locked_amount": "9128.538172520424723312", "locked_amount": "8969.210333073256934278",
"deposits": [ "deposits": [
{ {
"amount": "10833.29", "amount": "10833.29",
@ -675,7 +675,7 @@
"tranche_end": "2022-11-01T00:00:00.000Z", "tranche_end": "2022-11-01T00:00:00.000Z",
"total_added": "22500", "total_added": "22500",
"total_removed": "0", "total_removed": "0",
"locked_amount": "16079.77241847825975", "locked_amount": "15468.3027626811615",
"deposits": [ "deposits": [
{ {
"amount": "15000", "amount": "15000",
@ -761,7 +761,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z", "tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38", "total_added": "1939928.38",
"total_removed": "0", "total_removed": "0",
"locked_amount": "1830956.520319353226245706", "locked_amount": "1804379.69836047439128314",
"deposits": [ "deposits": [
{ {
"amount": "1852091.69", "amount": "1852091.69",
@ -1776,8 +1776,8 @@
"tranche_start": "2021-09-05T00:00:00.000Z", "tranche_start": "2021-09-05T00:00:00.000Z",
"tranche_end": "2022-09-30T00:00:00.000Z", "tranche_end": "2022-09-30T00:00:00.000Z",
"total_added": "60916.66666633337", "total_added": "60916.66666633337",
"total_removed": "18323.723696937179372649", "total_removed": "18705.279504739679372649",
"locked_amount": "15541.060867673757124652184217191", "locked_amount": "14760.0056484630890178248654459722",
"deposits": [ "deposits": [
{ {
"amount": "2833.333333", "amount": "2833.333333",
@ -1876,6 +1876,11 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "381.5558078025",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
"tx": "0xdf5387ab07596bf2a4a608ea38d3f98f8020941f4fa7ad88f1ccd223669ac2c7"
},
{ {
"amount": "327.532400005", "amount": "327.532400005",
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2", "user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
@ -2591,6 +2596,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "381.5558078025",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
"tranche_id": 13,
"tx": "0xdf5387ab07596bf2a4a608ea38d3f98f8020941f4fa7ad88f1ccd223669ac2c7"
},
{ {
"amount": "490.9767850775", "amount": "490.9767850775",
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05", "user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
@ -2635,8 +2646,8 @@
} }
], ],
"total_tokens": "4250", "total_tokens": "4250",
"withdrawn_tokens": "2838.102475055", "withdrawn_tokens": "3219.6582828575",
"remaining_tokens": "1411.897524945" "remaining_tokens": "1030.3417171425"
}, },
{ {
"address": "0xDFaF6D0a0102ea5e4688F95Eb22Dc353751a7563", "address": "0xDFaF6D0a0102ea5e4688F95Eb22Dc353751a7563",
@ -3228,8 +3239,8 @@
"tranche_id": 10, "tranche_id": 10,
"tranche_start": "2021-07-15T23:37:11.000Z", "tranche_start": "2021-07-15T23:37:11.000Z",
"tranche_end": "2021-07-15T23:37:11.000Z", "tranche_end": "2021-07-15T23:37:11.000Z",
"total_added": "3653968.150000000000000001", "total_added": "3710968.150000000000000001",
"total_removed": "3645778.640000000000000001", "total_removed": "3702778.640000000000000001",
"locked_amount": "0", "locked_amount": "0",
"deposits": [ "deposits": [
{ {
@ -3242,6 +3253,11 @@
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0x7b0e95e1914579c79eff111dd0df59e767321858e552cbd2a65e5f0ea8c60868" "tx": "0x7b0e95e1914579c79eff111dd0df59e767321858e552cbd2a65e5f0ea8c60868"
}, },
{
"amount": "57000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0x66d007fc762ef9b7d2c0b57434295d06dfbafb9aeb5884cedc650f5f0c98d68f"
},
{ {
"amount": "40000", "amount": "40000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@ -3669,6 +3685,11 @@
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0xc5ad7140387b5c2eeeae5c158d97a8df0d4c0bbae4a61b3c03c784f11272c89a" "tx": "0xc5ad7140387b5c2eeeae5c158d97a8df0d4c0bbae4a61b3c03c784f11272c89a"
}, },
{
"amount": "57000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0x25c796bbfae4d91dca908b1ea7c50f6ea26b38d5350862b154a7963802bc3497"
},
{ {
"amount": "40000", "amount": "40000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@ -4041,6 +4062,12 @@
"tranche_id": 10, "tranche_id": 10,
"tx": "0x7b0e95e1914579c79eff111dd0df59e767321858e552cbd2a65e5f0ea8c60868" "tx": "0x7b0e95e1914579c79eff111dd0df59e767321858e552cbd2a65e5f0ea8c60868"
}, },
{
"amount": "57000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0x66d007fc762ef9b7d2c0b57434295d06dfbafb9aeb5884cedc650f5f0c98d68f"
},
{ {
"amount": "40000", "amount": "40000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@ -4127,6 +4154,12 @@
"tranche_id": 10, "tranche_id": 10,
"tx": "0xc5ad7140387b5c2eeeae5c158d97a8df0d4c0bbae4a61b3c03c784f11272c89a" "tx": "0xc5ad7140387b5c2eeeae5c158d97a8df0d4c0bbae4a61b3c03c784f11272c89a"
}, },
{
"amount": "57000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0x25c796bbfae4d91dca908b1ea7c50f6ea26b38d5350862b154a7963802bc3497"
},
{ {
"amount": "40000", "amount": "40000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f", "user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@ -4194,8 +4227,8 @@
"tx": "0xac16a4ce688d40a482a59914d68c3a676592f8804ee8f0781b66a4ba5ccfbdfc" "tx": "0xac16a4ce688d40a482a59914d68c3a676592f8804ee8f0781b66a4ba5ccfbdfc"
} }
], ],
"total_tokens": "507651", "total_tokens": "564651",
"withdrawn_tokens": "507651", "withdrawn_tokens": "564651",
"remaining_tokens": "0" "remaining_tokens": "0"
}, },
{ {
@ -5194,8 +5227,8 @@
"tranche_start": "2021-09-03T00:00:00.000Z", "tranche_start": "2021-09-03T00:00:00.000Z",
"tranche_end": "2022-09-03T00:00:00.000Z", "tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "19455.000000000000000003", "total_added": "19455.000000000000000003",
"total_removed": "5052.45813105178", "total_removed": "5056.88782409978",
"locked_amount": "3864.17904680365295175059586415525114155", "locked_amount": "3597.64752092846370375055476445966514475",
"deposits": [ "deposits": [
{ {
"amount": "75", "amount": "75",
@ -8249,6 +8282,11 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "4.429693048",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
"tx": "0xfbca378f6263977884e6eebc653c8d68f39b6c8eb6ce2f6668767ed63bfcc55b"
},
{ {
"amount": "8.25227042", "amount": "8.25227042",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
@ -13997,6 +14035,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "4.429693048",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
"tranche_id": 11,
"tx": "0xfbca378f6263977884e6eebc653c8d68f39b6c8eb6ce2f6668767ed63bfcc55b"
},
{ {
"amount": "8.25227042", "amount": "8.25227042",
"user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b",
@ -14053,8 +14097,8 @@
} }
], ],
"total_tokens": "200", "total_tokens": "200",
"withdrawn_tokens": "157.871905124", "withdrawn_tokens": "162.301598172",
"remaining_tokens": "42.128094876" "remaining_tokens": "37.698401828"
}, },
{ {
"address": "0x1775cc97E5c05Fde8b571ef75CA52d0A9ff19025", "address": "0x1775cc97E5c05Fde8b571ef75CA52d0A9ff19025",
@ -14079,7 +14123,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z", "tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671", "total_added": "3732368.4671",
"total_removed": "74162.9780761646031", "total_removed": "74162.9780761646031",
"locked_amount": "2838044.0251062046717742172", "locked_amount": "2797204.7028220379638062054",
"deposits": [ "deposits": [
{ {
"amount": "1998.95815", "amount": "1998.95815",
@ -14791,8 +14835,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z", "tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15788853.065470999700000001", "total_added": "15788853.065470999700000001",
"total_removed": "5923.782558947259825", "total_removed": "7926.7627792759659",
"locked_amount": "15284554.9536832239111747473350177541524737", "locked_amount": "15140482.7365636033722263648641426217383077",
"deposits": [ "deposits": [
{ {
"amount": "16249.93", "amount": "16249.93",
@ -15291,6 +15335,31 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb11f9f763196e40be412b1c6992f64d3a73b094833f57eb7783a050c64ec159d" "tx": "0xb11f9f763196e40be412b1c6992f64d3a73b094833f57eb7783a050c64ec159d"
}, },
{
"amount": "605.52572395991115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x7bf7930893a08f0c18416bb3c9470c5671a1a10a9251d4ab5eb392ed90124a60"
},
{
"amount": "460.287027070071975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xf2208f6e0a6d6b7f5e52e128632ac52b8473b408421318b9a28460aa19feca9c"
},
{
"amount": "358.037173900154925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x2a92ef6c2eb5779e6219058a74210fcd461484d42d623dd06dcb9886683d7b50"
},
{
"amount": "226.8137106757935",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xcb5d0a1e6fdae5bed77fe2f8a29df14985d9f21d0e681416c618104b530fab36"
},
{
"amount": "352.316584722774525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{ {
"amount": "2446.31552516990115", "amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15383,6 +15452,36 @@
"tranche_id": 2, "tranche_id": 2,
"tx": "0xb11f9f763196e40be412b1c6992f64d3a73b094833f57eb7783a050c64ec159d" "tx": "0xb11f9f763196e40be412b1c6992f64d3a73b094833f57eb7783a050c64ec159d"
}, },
{
"amount": "605.52572395991115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x7bf7930893a08f0c18416bb3c9470c5671a1a10a9251d4ab5eb392ed90124a60"
},
{
"amount": "460.287027070071975",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xf2208f6e0a6d6b7f5e52e128632ac52b8473b408421318b9a28460aa19feca9c"
},
{
"amount": "358.037173900154925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x2a92ef6c2eb5779e6219058a74210fcd461484d42d623dd06dcb9886683d7b50"
},
{
"amount": "226.8137106757935",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xcb5d0a1e6fdae5bed77fe2f8a29df14985d9f21d0e681416c618104b530fab36"
},
{
"amount": "352.316584722774525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{ {
"amount": "2446.31552516990115", "amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15427,8 +15526,8 @@
} }
], ],
"total_tokens": "194999.1675", "total_tokens": "194999.1675",
"withdrawn_tokens": "5923.782558947259825", "withdrawn_tokens": "7926.7627792759659",
"remaining_tokens": "189075.384941052740175" "remaining_tokens": "187072.4047207240341"
}, },
{ {
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c", "address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@ -16832,8 +16931,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z", "tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999", "total_added": "14597706.0446472999",
"total_removed": "2068423.077146290360122272", "total_removed": "2113641.840890679071831632",
"locked_amount": "8461771.6695419306722268828527521", "locked_amount": "8328080.66510881884167481291924447",
"deposits": [ "deposits": [
{ {
"amount": "129284.449", "amount": "129284.449",
@ -17047,6 +17146,41 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xa7bccea82ef34f1943bc5243ef75909d8ae81c45e67862b9c68b0a7532edc833" "tx": "0xa7bccea82ef34f1943bc5243ef75909d8ae81c45e67862b9c68b0a7532edc833"
}, },
{
"amount": "582.7808762737973505",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xcdbefdbd89a68d401dd21553cded39ee57bb5a7c6f3203ed6971e625f90c8b9d"
},
{
"amount": "539.09172135635481275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x091d995cd60f0a5a93eccb13e0ca0e39fc513c28c860bd5bc6369da3994a09d0"
},
{
"amount": "845.684674761873869",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xcb8ecc71a9024bab7454a33f6adf7bfcd0f25b3dc1919f92ca362a25b71f2a4d"
},
{
"amount": "41515.95004024647383586",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tx": "0xd950c34bf5f503998bddc8f9fdeb7cdc4ae1e76c5e9b1564795c306c2e8cad63"
},
{
"amount": "657.674387973356598",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xd0f13de38f21ba54433885d7e3e3db7438b9bcc92dbd52a75e0b7f772018ab1c"
},
{
"amount": "424.7645385693677305",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x77d249279d8ea7554d19c557311687d57884b5e08537ac9574897ca58a13d880"
},
{
"amount": "652.81750520748751275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{ {
"amount": "652.48254356494551875", "amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -18629,6 +18763,42 @@
"tranche_id": 3, "tranche_id": 3,
"tx": "0xa7bccea82ef34f1943bc5243ef75909d8ae81c45e67862b9c68b0a7532edc833" "tx": "0xa7bccea82ef34f1943bc5243ef75909d8ae81c45e67862b9c68b0a7532edc833"
}, },
{
"amount": "582.7808762737973505",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xcdbefdbd89a68d401dd21553cded39ee57bb5a7c6f3203ed6971e625f90c8b9d"
},
{
"amount": "539.09172135635481275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x091d995cd60f0a5a93eccb13e0ca0e39fc513c28c860bd5bc6369da3994a09d0"
},
{
"amount": "845.684674761873869",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xcb8ecc71a9024bab7454a33f6adf7bfcd0f25b3dc1919f92ca362a25b71f2a4d"
},
{
"amount": "657.674387973356598",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xd0f13de38f21ba54433885d7e3e3db7438b9bcc92dbd52a75e0b7f772018ab1c"
},
{
"amount": "424.7645385693677305",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x77d249279d8ea7554d19c557311687d57884b5e08537ac9574897ca58a13d880"
},
{
"amount": "652.81750520748751275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{ {
"amount": "652.48254356494551875", "amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -19867,8 +20037,8 @@
} }
], ],
"total_tokens": "359123.469575", "total_tokens": "359123.469575",
"withdrawn_tokens": "150390.162087073825163", "withdrawn_tokens": "154092.9757912160630365",
"remaining_tokens": "208733.307487926174837" "remaining_tokens": "205030.4937837839369635"
}, },
{ {
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@ -20055,6 +20225,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "41515.95004024647383586",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
"tranche_id": 3,
"tx": "0xd950c34bf5f503998bddc8f9fdeb7cdc4ae1e76c5e9b1564795c306c2e8cad63"
},
{ {
"amount": "17786.1881146007542887", "amount": "17786.1881146007542887",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8", "user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@ -20261,8 +20437,8 @@
} }
], ],
"total_tokens": "1266324.603486", "total_tokens": "1266324.603486",
"withdrawn_tokens": "497356.38070819243695426", "withdrawn_tokens": "538872.33074843891079012",
"remaining_tokens": "768968.22277780756304574" "remaining_tokens": "727452.27273756108920988"
}, },
{ {
"address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296", "address": "0xC5d9221EB9c28A69859264c0A2Fe0d3272228296",
@ -20859,7 +21035,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303", "total_added": "5778205.3912159303",
"total_removed": "1390546.591547348229906227", "total_removed": "1390546.591547348229906227",
"locked_amount": "3026393.75946481397995019109643517", "locked_amount": "2973571.63505682836710488797665665",
"deposits": [ "deposits": [
{ {
"amount": "552496.6455", "amount": "552496.6455",
@ -21981,8 +22157,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z", "tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z", "tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996", "total_added": "472355.6199999996",
"total_removed": "0", "total_removed": "34.173053016",
"locked_amount": "449704.29872935272705903095281584", "locked_amount": "443233.07466590775804275636428212",
"deposits": [ "deposits": [
{ {
"amount": "3000", "amount": "3000",
@ -28600,7 +28776,18 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8" "tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "11.163032724",
"user": "0xF5037DDA4A660d67560200f45380FF8364e35540",
"tx": "0x0db96c68606a35c59786e3b1ff4f8e939a56af133709dd154718eb03138fe227"
},
{
"amount": "23.010020292",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tx": "0x8daf320262d0384cf5f9c290cc33721587beabd5e93026b3e9b76dc3fcd6659c"
}
],
"users": [ "users": [
{ {
"address": "0xD18ffAa4a1d16f9eD9d3BE4078738Eeda3f160FD", "address": "0xD18ffAa4a1d16f9eD9d3BE4078738Eeda3f160FD",
@ -47113,10 +47300,17 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8" "tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "23.010020292",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tranche_id": 5,
"tx": "0x8daf320262d0384cf5f9c290cc33721587beabd5e93026b3e9b76dc3fcd6659c"
}
],
"total_tokens": "400", "total_tokens": "400",
"withdrawn_tokens": "0", "withdrawn_tokens": "23.010020292",
"remaining_tokens": "400" "remaining_tokens": "376.989979708"
}, },
{ {
"address": "0xF5037DDA4A660d67560200f45380FF8364e35540", "address": "0xF5037DDA4A660d67560200f45380FF8364e35540",
@ -47128,10 +47322,17 @@
"tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8" "tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "11.163032724",
"user": "0xF5037DDA4A660d67560200f45380FF8364e35540",
"tranche_id": 5,
"tx": "0x0db96c68606a35c59786e3b1ff4f8e939a56af133709dd154718eb03138fe227"
}
],
"total_tokens": "200", "total_tokens": "200",
"withdrawn_tokens": "0", "withdrawn_tokens": "11.163032724",
"remaining_tokens": "200" "remaining_tokens": "188.836967276"
}, },
{ {
"address": "0x2843a3A61C7a129e432455c02101Cec3fA428E60", "address": "0x2843a3A61C7a129e432455c02101Cec3fA428E60",
@ -47635,7 +47836,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z", "tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42", "total_added": "171288.42",
"total_removed": "29185.5825162206377", "total_removed": "32094.1716569431377",
"locked_amount": "0", "locked_amount": "0",
"deposits": [ "deposits": [
{ {
@ -51870,6 +52071,71 @@
"user": "0x7fAA8d1Cb6Ab8A9fC039b66fe844575557389CD4", "user": "0x7fAA8d1Cb6Ab8A9fC039b66fe844575557389CD4",
"tx": "0xb45e62f845fe45f11bb86fdffbc1af3e737573726b3c2c8cc6ba39ab8127369d" "tx": "0xb45e62f845fe45f11bb86fdffbc1af3e737573726b3c2c8cc6ba39ab8127369d"
}, },
{
"amount": "250",
"user": "0xEF5D4Be15019F9b972E54b6B904076617aDAfb1C",
"tx": "0x44d207a93bdb1ef457d5a31207d3ccbda3e9fce6528f3a6c0d04f9153e064a2a"
},
{
"amount": "249.9",
"user": "0xcad0D46627628A8bAF524BF202669e8B9f04250f",
"tx": "0xa403d8af5a940c4abd4aadcc579e279218ae73b9c7fc3d7e4216d6aab5ba70eb"
},
{
"amount": "250",
"user": "0xEc925Fd72B46E2101D20686800343220c12Dedde",
"tx": "0xeaf8173b80d3ea3eb5c440a6edcc4fbf6d8624fa437203b66f6acb72d7462b5e"
},
{
"amount": "250",
"user": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
"tx": "0x1ea368a1c1f000d36d93627ccda127da12e4beade77d869aab7cadb7edfa354f"
},
{
"amount": "250",
"user": "0xDA83Ff6862ed0579248CEd14422C8B782732233b",
"tx": "0x7eb0ec3a5e3d8792b341881083f9046d550fdf0e92429b53bbdddfe439e83983"
},
{
"amount": "250",
"user": "0x2b28Fd98804e19cFe8d46f2d729C2A5195339fA4",
"tx": "0xd8c87e89222f0fea8933a403cd39756c6721d1e3d86a0bb7747b428f50c7f790"
},
{
"amount": "250",
"user": "0x0941FC05E3DA8d9dD29bCBB15F9FF3A16F342612",
"tx": "0xb3cffc52e62679835e65be5472f80bcfa949f7eb0c4c9948efc3271cca02fdae"
},
{
"amount": "100",
"user": "0x4796314cC5bDa80Ee2E8b119004b1fc72Cfb1783",
"tx": "0xbfdbd5e3712968f2726e8dba1f226944cfa637f8738f25c3a5b12a1c6a8e7197"
},
{
"amount": "250",
"user": "0xC26B3b40DC383d97B69Eedcd4E78a9e4eEb73499",
"tx": "0x2c9f57d5e697c29b67034cea8738939ec13b20b5a7d7fcb995fe83f16b4abf75"
},
{
"amount": "250",
"user": "0x9f6b5A46593b991dC78a0e1907a07517A1F80CC2",
"tx": "0x55b21517cf5fd148e596d9e4b63964ab1df82534fd76b6d9dc9d7cfd00971da0"
},
{
"amount": "250",
"user": "0x696971413b8Ae5342277AfeC16591271648B4FfF",
"tx": "0xa55b22bf465927c195b424ad7ea893c1c497da34290d5261be205c760fc11cf5"
},
{
"amount": "250",
"user": "0x8FC36B7695965Bc39BCB8a3679529f1825e472aE",
"tx": "0x7072667bf81811acda07177c1ace2edcfe19ce68711fa349ad94c1f8aa3d0b25"
},
{
"amount": "58.6891407225",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
},
{ {
"amount": "183.6335597275", "amount": "183.6335597275",
"user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786", "user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786",
@ -57843,10 +58109,17 @@
"tx": "0x75de6ca47e0da361181d14aceb5d08e572754861465caa67a12b528886d55307" "tx": "0x75de6ca47e0da361181d14aceb5d08e572754861465caa67a12b528886d55307"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "249.9",
"user": "0xcad0D46627628A8bAF524BF202669e8B9f04250f",
"tranche_id": 6,
"tx": "0xa403d8af5a940c4abd4aadcc579e279218ae73b9c7fc3d7e4216d6aab5ba70eb"
}
],
"total_tokens": "249.9", "total_tokens": "249.9",
"withdrawn_tokens": "0", "withdrawn_tokens": "249.9",
"remaining_tokens": "249.9" "remaining_tokens": "0"
}, },
{ {
"address": "0x783C204FfCC479cec7807C37480A4D4C77FB1F22", "address": "0x783C204FfCC479cec7807C37480A4D4C77FB1F22",
@ -58384,6 +58657,12 @@
} }
], ],
"withdrawals": [ "withdrawals": [
{
"amount": "58.6891407225",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
"tranche_id": 6,
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
},
{ {
"amount": "65.928596865", "amount": "65.928596865",
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d", "user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
@ -58398,8 +58677,8 @@
} }
], ],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "191.3108592775", "withdrawn_tokens": "250",
"remaining_tokens": "58.6891407225" "remaining_tokens": "0"
}, },
{ {
"address": "0x243e23c83135cA0fed2F9f5dF9068dE644929433", "address": "0x243e23c83135cA0fed2F9f5dF9068dE644929433",
@ -59330,10 +59609,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9" "tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0xEc925Fd72B46E2101D20686800343220c12Dedde",
"tranche_id": 6,
"tx": "0xeaf8173b80d3ea3eb5c440a6edcc4fbf6d8624fa437203b66f6acb72d7462b5e"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x08e9b80fCa090CB3E50d77964dc8777cD828253D", "address": "0x08e9b80fCa090CB3E50d77964dc8777cD828253D",
@ -59570,10 +59856,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9" "tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0x2b28Fd98804e19cFe8d46f2d729C2A5195339fA4",
"tranche_id": 6,
"tx": "0xd8c87e89222f0fea8933a403cd39756c6721d1e3d86a0bb7747b428f50c7f790"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x61f908D9ee13a68983e5FECc8D9467232F5E9cB4", "address": "0x61f908D9ee13a68983e5FECc8D9467232F5E9cB4",
@ -59630,10 +59923,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9" "tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0xDA83Ff6862ed0579248CEd14422C8B782732233b",
"tranche_id": 6,
"tx": "0x7eb0ec3a5e3d8792b341881083f9046d550fdf0e92429b53bbdddfe439e83983"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238", "address": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
@ -59645,10 +59945,17 @@
"tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9" "tx": "0x96a22c369645e8945b4047374a05332f4054b50a492c43092e8bb8e974a006b9"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0xbC10086CB362caE3071f2e09DF2aEE5265FdA238",
"tranche_id": 6,
"tx": "0x1ea368a1c1f000d36d93627ccda127da12e4beade77d869aab7cadb7edfa354f"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0", "address": "0xEe3183EcE9ee7d73Fb7bA7F4eB262A2dE68C42B0",
@ -61749,10 +62056,17 @@
"tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f" "tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0x9f6b5A46593b991dC78a0e1907a07517A1F80CC2",
"tranche_id": 6,
"tx": "0x55b21517cf5fd148e596d9e4b63964ab1df82534fd76b6d9dc9d7cfd00971da0"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x758E9AA35F2feA08aEc1613A0F0d9Ebe4d374152", "address": "0x758E9AA35F2feA08aEc1613A0F0d9Ebe4d374152",
@ -61804,10 +62118,17 @@
"tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f" "tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0x8FC36B7695965Bc39BCB8a3679529f1825e472aE",
"tranche_id": 6,
"tx": "0x7072667bf81811acda07177c1ace2edcfe19ce68711fa349ad94c1f8aa3d0b25"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x8186CDe8f7A2f95b50f847e2a3301c28bB61Fa8A", "address": "0x8186CDe8f7A2f95b50f847e2a3301c28bB61Fa8A",
@ -62231,10 +62552,17 @@
"tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148" "tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0x0941FC05E3DA8d9dD29bCBB15F9FF3A16F342612",
"tranche_id": 6,
"tx": "0xb3cffc52e62679835e65be5472f80bcfa949f7eb0c4c9948efc3271cca02fdae"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x0a5688ed5b0b804db2ee18767476bb60598EC398", "address": "0x0a5688ed5b0b804db2ee18767476bb60598EC398",
@ -63811,10 +64139,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0x696971413b8Ae5342277AfeC16591271648B4FfF",
"tranche_id": 6,
"tx": "0xa55b22bf465927c195b424ad7ea893c1c497da34290d5261be205c760fc11cf5"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0xcbEca0abcc675A0F977c4Eb9a45bB4153C0246C3", "address": "0xcbEca0abcc675A0F977c4Eb9a45bB4153C0246C3",
@ -63848,10 +64183,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0xC26B3b40DC383d97B69Eedcd4E78a9e4eEb73499",
"tranche_id": 6,
"tx": "0x2c9f57d5e697c29b67034cea8738939ec13b20b5a7d7fcb995fe83f16b4abf75"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0xd866082E19c9E36bcA4fA649d530166303B656f2", "address": "0xd866082E19c9E36bcA4fA649d530166303B656f2",
@ -64126,10 +64468,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "250",
"user": "0xEF5D4Be15019F9b972E54b6B904076617aDAfb1C",
"tranche_id": 6,
"tx": "0x44d207a93bdb1ef457d5a31207d3ccbda3e9fce6528f3a6c0d04f9153e064a2a"
}
],
"total_tokens": "250", "total_tokens": "250",
"withdrawn_tokens": "0", "withdrawn_tokens": "250",
"remaining_tokens": "250" "remaining_tokens": "0"
}, },
{ {
"address": "0x660Bc2766867E1E19252ad30A413fe20d08A85c4", "address": "0x660Bc2766867E1E19252ad30A413fe20d08A85c4",
@ -64156,10 +64505,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
} }
], ],
"withdrawals": [], "withdrawals": [
{
"amount": "100",
"user": "0x4796314cC5bDa80Ee2E8b119004b1fc72Cfb1783",
"tranche_id": 6,
"tx": "0xbfdbd5e3712968f2726e8dba1f226944cfa637f8738f25c3a5b12a1c6a8e7197"
}
],
"total_tokens": "100", "total_tokens": "100",
"withdrawn_tokens": "0", "withdrawn_tokens": "100",
"remaining_tokens": "100" "remaining_tokens": "0"
}, },
{ {
"address": "0x351cc2560C870f01B099B106Be22C0073Fce10B2", "address": "0x351cc2560C870f01B099B106Be22C0073Fce10B2",

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z", "tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100", "total_added": "100",
"total_removed": "0", "total_removed": "0",
"locked_amount": "43.033418949771685", "locked_amount": "41.663429096905125",
"deposits": [ "deposits": [
{ {
"amount": "100", "amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z", "tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100", "total_added": "1100",
"total_removed": "673.04388635", "total_removed": "673.04388635",
"locked_amount": "336.1295630390665", "locked_amount": "321.05967465753423",
"deposits": [ "deposits": [
{ {
"amount": "1000", "amount": "1000",

View File

@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z", "tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001", "total_added": "1010.000000000000000001",
"total_removed": "668.4622323651", "total_removed": "668.4622323651",
"locked_amount": "308.628053335870150000305572330035515", "locked_amount": "294.7911558219177930002918724315068493",
"deposits": [ "deposits": [
{ {
"amount": "1000", "amount": "1000",

View File

@ -1,29 +1,17 @@
# React Environment Variables
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
# Netlify Environment Variables
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
REACT_APP_VERSION=$npm_package_version
REACT_APP_REPOSITORY_URL=$REPOSITORY_URL
REACT_APP_BRANCH=$BRANCH
REACT_APP_PULL_REQUEST=$PULL_REQUEST
REACT_APP_HEAD=$HEAD
REACT_APP_COMMIT_REF=$COMMIT_REF
REACT_APP_CONTEXT=$CONTEXT
REACT_APP_REVIEW_ID=$REVIEW_ID
REACT_APP_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
REACT_APP_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
REACT_APP_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
REACT_APP_URL=$URL
REACT_APP_DEPLOY_URL=$DEPLOY_URL
REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables # App configuration variables
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_VEGA_URL=https://lb.testnet.vega.xyz/query NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_REST=http://localhost:3029
NX_ETHEREUM_CHAIN_ID=1440
NX_ETH_URL_CONNECT=1
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
#Test configuration variables #Test configuration variables
CYPRESS_FAIRGROUND=false CYPRESS_FAIRGROUND=false

View File

@ -0,0 +1,4 @@
{
"tokenAddress": "0x67175Da1D5e966e40D11c4B2519392B2058373de",
"vestingContract": "0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7"
}

View File

@ -1,4 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io"
}

View File

@ -0,0 +1,124 @@
import navigation from '../../locators/navigation.locators';
import home from '../../locators/home.locators';
import vegaToken from '../../data/vegaToken.json';
context('Home Page - verify elements on page', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('with wallets disconnected', function () {
before('wait for page to load', function () {
cy.get(navigation.section, { timeout: 10000 }).should('be.visible');
});
describe('Navigation tabs', function () {
it('should have HOME tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.home).should('be.visible');
});
});
it('should have VESTING tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.vesting).should('be.visible');
});
});
it('should have STAKING tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.staking).should('be.visible');
});
});
it('should have REWARDS tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.rewards).should('be.visible');
});
});
it('should have WITHDRAW tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.withdraw).should('be.visible');
});
});
it('should have GOVERNANCE tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.governance).should('be.visible');
});
});
});
describe('THE $VEGA TOKEN table', function () {
it('should have TOKEN ADDRESS', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.address)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaToken.tokenAddress);
});
});
it('should have VESTING CONTRACT', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.contract)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaToken.vestingContract);
});
});
it('should have TOTAL SUPPLY', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.totalSupply).should('be.visible');
});
});
it('should have CIRCULATING SUPPLY', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.circulatingSupply).should('be.visible');
});
});
it('should have STAKED $VEGA', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.staked).should('be.visible');
});
});
});
describe('links and buttons', function () {
it('should have TRANCHES link', function () {
cy.get(home.tranchesLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/tranches');
});
it('should have REDEEM button', function () {
cy.get(home.redeemBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/vesting');
});
it('should have GET VEGA WALLET link', function () {
cy.get(home.getVegaWalletLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', 'https://vega.xyz/wallet');
});
it('should have ASSOCIATE VEGA TOKENS link', function () {
cy.get(home.associateVegaLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/staking/associate');
});
it('should have STAKING button', function () {
cy.get(home.stakingBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/staking');
});
it('should have GOVERNANCE button', function () {
cy.get(home.governanceBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/governance');
});
});
});
});

View File

@ -0,0 +1,15 @@
export default {
tokenDetailsTable: '.token-details',
address: '[data-testid="token-address"]',
contract: '[data-testid="token-contract"]',
totalSupply: '[data-testid="total-supply"]',
circulatingSupply: '[data-testid="circulating-supply"]',
staked: '[data-testid="staked"]',
tranchesLink: '[data-testid="tranches-link"]',
redeemBtn: '[data-testid="check-vesting-page-btn"]',
getVegaWalletLink: '[data-testid="get-vega-wallet-link"]',
associateVegaLink: '[data-testid="associate-vega-tokens-link-on-homepage"]',
stakingBtn: '[data-testid="staking-button-on-homepage"]',
governanceBtn: '[data-testid="governance-button-on-homepage"]',
};

View File

@ -0,0 +1,9 @@
export default {
section: 'nav',
home: '[href="/"]',
vesting: '[href="/vesting"]',
staking: '[href="/staking"]',
rewards: '[href="/rewards"]',
withdraw: '[href="/withdraw"]',
governance: '[href="/governance"]',
};

View File

@ -1,23 +1,3 @@
# React Environment Variables
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
# Netlify Environment Variables
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
REACT_APP_VERSION=$npm_package_version
REACT_APP_REPOSITORY_URL=$REPOSITORY_URL
REACT_APP_BRANCH=$BRANCH
REACT_APP_PULL_REQUEST=$PULL_REQUEST
REACT_APP_HEAD=$HEAD
REACT_APP_COMMIT_REF=$COMMIT_REF
REACT_APP_CONTEXT=$CONTEXT
REACT_APP_REVIEW_ID=$REVIEW_ID
REACT_APP_INCOMING_HOOK_TITLE=$INCOMING_HOOK_TITLE
REACT_APP_INCOMING_HOOK_URL=$INCOMING_HOOK_URL
REACT_APP_INCOMING_HOOK_BODY=$INCOMING_HOOK_BODY
REACT_APP_URL=$URL
REACT_APP_DEPLOY_URL=$DEPLOY_URL
REACT_APP_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
# App configuration variables # App configuration variables
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json

17
apps/token/.env.capsule Normal file
View File

@ -0,0 +1,17 @@
# App configuration variables
NX_VEGA_ENV=TESTNET
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_FAIRGROUND=false
NX_IS_NEW_BRIDGE_CONTRACT=true
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.token.vega.xyz","STAGNET2":"staging2.token.vega.xyz","TESTNET":"token.fairground.wtf","MAINNET":"token.vega.xyz"}'
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_REST=http://localhost:3029
NX_ETHEREUM_CHAIN_ID=1440
NX_ETH_URL_CONNECT=1
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -38,23 +38,19 @@ yarn nx run token:serve --env={env} # e.g. stagnet1
There are a few different configuration options offered for this app: There are a few different configuration options offered for this app:
| **Flag** | **Purpose** | | **Flag** | **Purpose** |
| ------------------------------ | ---------------------------------------------------------------------------------------------------- | | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NX_APP_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. | | `NX_APP_SENTRY_DSN` | The sentry endpoint to report to. Should be off in dev but set in live. |
| `NX_APP_CHAIN` | The ETH chain for the app to work on. Should be mainnet for live, but ropsten for preview deploys. | | `NX_APP_CHAIN` | The ETH chain for the app to work on. Should be mainnet for live, but ropsten for preview deploys. |
| `NX_APP_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) | | `NX_APP_VEGA_URL` | The GraphQL query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_APP_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message | | `NX_APP_DEX_STAKING_DISABLED` | Disable the dex liquidity page an show a coming soon message |
| `NX_APP_FAIRGROUND` | Change styling to be themed as the fairground version of the website | | `NX_APP_FAIRGROUND` | Change styling to be themed as the fairground version of the website |
| `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser | | `NX_APP_INFURA_ID` | Infura fallback for if the user does not have a web3 compatible browser |
| `NX_APP_HOSTED_WALLET_ENABLED` | If the hosted wallet is enabled or not. If so then allow users to login using the hosted wallet | | `NX_APP_HOSTED_WALLET_ENABLED` | If the hosted wallet is enabled or not. If so then allow users to login using the hosted wallet |
| `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters | | `NX_APP_ENV` | Change network to connect to. When set to CUSTOM use CUSTOM\_\* vars for network parameters |
| `NX_CUSTOM_URLS` | When NX_APP_ENV=CUSTOM use these Data Node REST URLs, optional if CUSTOM_URLS_WITH_GRAPHQL is used. | | `NX_ETH_URL_CONNECT` (optional) | If set to true the below two must also be set. This allows siging transactions in brower to allow to connect to a local ganache node through cypress |
| `NX_CUSTOM_URLS_WITH_GRAPHQL` | When NX_APP_ENV=CUSTOM use these Data Node GraphQL URLs, optional if CUSTOM_URLS is used. | | `NX_ETH_WALLET_MNEMONIC` (optional) | The mnemonic to be used to sign transactions with in browser |
| `NX_CUSTOM_TOKEN_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega token address. | | `NX_LOCAL_PROVIDER_URL` (optional) | The local node to use to send transaction to when signing in browser |
| `NX_CUSTOM_CLAIM_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega claim address. |
| `NX_CUSTOM_LOCKED_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega locked address. |
| `NX_CUSTOM_VESTING_ADDRESS` | When NX_APP_ENV=CUSTOM specify Vega vesting address. |
| `NX_CUSTOM_STAKING_BRIDGE` | When NX_APP_ENV=CUSTOM specify Vega staking bridge address. |
## Example configs: ## Example configs:

View File

@ -2,6 +2,7 @@ import { useWeb3React } from '@web3-react/core';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { getButtonClasses, Button } from '@vegaprotocol/ui-toolkit';
import { import {
AppStateActionType, AppStateActionType,
@ -70,7 +71,7 @@ const AssociatedAmounts = ({
rightLabel={t('notAssociated')} rightLabel={t('notAssociated')}
leftColor={Colors.white.DEFAULT} leftColor={Colors.white.DEFAULT}
rightColor={Colors.black.DEFAULT} rightColor={Colors.black.DEFAULT}
light={true} light={false}
/> />
{vestingAssociationByVegaKey.length ? ( {vestingAssociationByVegaKey.length ? (
<> <>
@ -129,6 +130,7 @@ const ConnectedKey = () => {
name="VEGA" name="VEGA"
symbol="In vesting contract" symbol="In vesting contract"
balance={totalInVestingContract} balance={totalInVestingContract}
dark={true}
/> />
<LockedProgress <LockedProgress
locked={totalLockedBalance} locked={totalLockedBalance}
@ -136,7 +138,7 @@ const ConnectedKey = () => {
total={totalVestedBalance.plus(totalLockedBalance)} total={totalVestedBalance.plus(totalLockedBalance)}
leftLabel={t('Locked')} leftLabel={t('Locked')}
rightLabel={t('Unlocked')} rightLabel={t('Unlocked')}
light={true} light={false}
/> />
</> </>
)} )}
@ -153,6 +155,7 @@ const ConnectedKey = () => {
name="VEGA" name="VEGA"
symbol="In Wallet" symbol="In Wallet"
balance={walletWithAssociations} balance={walletWithAssociations}
dark={true}
/> />
{!Object.keys( {!Object.keys(
appState.associationBreakdown.stakingAssociations appState.associationBreakdown.stakingAssociations
@ -163,15 +166,17 @@ const ConnectedKey = () => {
/> />
)} )}
<WalletCardActions> <WalletCardActions>
<Link className="flex-1" to={`${Routes.STAKING}/associate`}> <Link
<span className="flex items-center justify-center w-full text-center px-28 border h-28"> className={getButtonClasses('flex-1 mr-4', 'secondary')}
{t('associate')} to={`${Routes.STAKING}/associate`}
</span> >
{t('associate')}
</Link> </Link>
<Link className="flex-1" to={`${Routes.STAKING}/disassociate`}> <Link
<span className="flex items-center justify-center w-full px-28 border h-28"> className={getButtonClasses('flex-1 ml-4', 'secondary')}
{t('disassociate')} to={`${Routes.STAKING}/disassociate`}
</span> >
{t('disassociate')}
</Link> </Link>
</WalletCardActions> </WalletCardActions>
</> </>
@ -185,7 +190,7 @@ export const EthWallet = () => {
const pendingTxs = usePendingTransactions(); const pendingTxs = usePendingTransactions();
return ( return (
<WalletCard> <WalletCard dark={true}>
<WalletCardHeader> <WalletCardHeader>
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1> <h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
{account && ( {account && (
@ -203,7 +208,7 @@ export const EthWallet = () => {
}) })
} }
> >
<Loader size="small" forceTheme="light" /> <Loader size="small" forceTheme="dark" />
{t('pendingTransactions')} {t('pendingTransactions')}
</button> </button>
</div> </div>
@ -215,7 +220,8 @@ export const EthWallet = () => {
{account ? ( {account ? (
<ConnectedKey /> <ConnectedKey />
) : ( ) : (
<button <Button
variant={'secondary'}
className="w-full px-28 border h-28" className="w-full px-28 border h-28"
onClick={() => onClick={() =>
appDispatch({ appDispatch({
@ -226,7 +232,7 @@ export const EthWallet = () => {
data-test-id="connect-to-eth-wallet-button" data-test-id="connect-to-eth-wallet-button"
> >
{t('connectEthWalletToAssociate')} {t('connectEthWalletToAssociate')}
</button> </Button>
)} )}
{account && ( {account && (
<WalletCardActions> <WalletCardActions>

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { formatNumber } from '../../lib/format-number'; import { formatNumber } from '../../lib/format-number';
import type { BigNumber } from '../../lib/bignumber'; import type { BigNumber } from '../../lib/bignumber';
import { theme } from '@vegaprotocol/tailwindcss-config'; import { theme } from '@vegaprotocol/tailwindcss-config';
import classnames from 'classnames';
const Colors = theme.colors; const Colors = theme.colors;
@ -14,9 +14,10 @@ const ProgressContents = ({
children: React.ReactNode; children: React.ReactNode;
}) => ( }) => (
<div <div
className={`flex justify-between py-2 font-mono ${ className={classnames('flex justify-between py-2 font-mono', {
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4' 'gap-0 px-0 text-black': light,
}`} 'gap-y-0 gap-x-4': !light,
})}
> >
{children} {children}
</div> </div>
@ -25,17 +26,22 @@ const ProgressContents = ({
const ProgressIndicator = ({ const ProgressIndicator = ({
bgColor, bgColor,
side, side,
light,
}: { }: {
bgColor: string; bgColor: string;
side: 'left' | 'right'; side: 'left' | 'right';
light: boolean;
}) => ( }) => (
<span <span
style={{ style={{
backgroundColor: bgColor, backgroundColor: bgColor,
}} }}
className={`inline-block w-12 h-12 border border-black ${ className={classnames('inline-block w-12 h-12 border', {
side === 'left' ? 'mr-8' : 'ml-8' 'mr-8': side === 'left',
}`} 'ml-8': side === 'right',
'border-black': light,
'border-white': !light,
})}
/> />
); );
@ -64,6 +70,7 @@ export interface LockedProgressProps {
leftColor?: string; leftColor?: string;
rightColor?: string; rightColor?: string;
light?: boolean; light?: boolean;
decimals?: number;
} }
export const LockedProgress = ({ export const LockedProgress = ({
@ -73,8 +80,9 @@ export const LockedProgress = ({
leftLabel, leftLabel,
rightLabel, rightLabel,
leftColor = Colors.vega.pink, leftColor = Colors.vega.pink,
rightColor = Colors.green.DEFAULT, rightColor = Colors.vega.green,
light = false, light = false,
decimals = 2,
}: LockedProgressProps) => { }: LockedProgressProps) => {
const lockedPercentage = React.useMemo(() => { const lockedPercentage = React.useMemo(() => {
return locked.div(total).times(100); return locked.div(total).times(100);
@ -85,26 +93,35 @@ export const LockedProgress = ({
}, [total, unlocked]); }, [total, unlocked]);
return ( return (
<div className="border-x border-x-white"> <>
<div className={`flex ${light && 'border border-black'}`}> <div
className={classnames('flex border', {
'border-black': light,
'border-white': !light,
})}
>
<ProgressBar percentage={lockedPercentage} bgColor={leftColor} /> <ProgressBar percentage={lockedPercentage} bgColor={leftColor} />
<ProgressBar percentage={unlockedPercentage} bgColor={rightColor} /> <ProgressBar percentage={unlockedPercentage} bgColor={rightColor} />
</div> </div>
<ProgressContents light={light}> <ProgressContents light={light}>
<span> <span>
<ProgressIndicator bgColor={leftColor} side={'left'} /> <ProgressIndicator bgColor={leftColor} side={'left'} light={false} />
{leftLabel} {leftLabel}
</span> </span>
<span> <span>
{rightLabel} {rightLabel}
<ProgressIndicator bgColor={rightColor} side={'right'} /> <ProgressIndicator
bgColor={rightColor}
side={'right'}
light={false}
/>
</span> </span>
</ProgressContents> </ProgressContents>
<ProgressContents light={light}> <ProgressContents light={light}>
<span>{formatNumber(locked, 2)}</span> <span>{formatNumber(locked, decimals)}</span>
<span>{formatNumber(unlocked, 2)}</span> <span>{formatNumber(unlocked, decimals)}</span>
</ProgressContents> </ProgressContents>
</div> </>
); );
}; };

View File

@ -12,7 +12,7 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
<div className="border-b border-white lg:grid lg:grid-rows-[auto_minmax(600px,_1fr)] lg:grid-cols-[1fr_450px]"> <div className="border-b border-white lg:grid lg:grid-rows-[auto_minmax(600px,_1fr)] lg:grid-cols-[1fr_450px]">
<Nav /> <Nav />
<main className="p-20">{children}</main> <main className="p-20">{children}</main>
<aside className="hidden lg:block lg:col-start-2 lg:col-end-3 lg:row-start-1 lg: row-end-3 p-20 bg-banner bg-contain border-l border-white"> <aside className="hidden lg:block lg:col-start-2 lg:col-end-4 lg:row-start-1 lg: row-end-4 p-20 bg-banner bg-contain border-l border-white">
{sidebar.map((Component, i) => ( {sidebar.map((Component, i) => (
<section className="mb-20 last:mb-0" key={i}> <section className="mb-20 last:mb-0" key={i}>
{Component} {Component}

View File

@ -180,15 +180,15 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
</div> </div>
))} ))}
<WalletCardActions> <WalletCardActions>
<Link style={{ flex: 1 }} to={Routes.GOVERNANCE}> <Link className="flex-1 pr-8" to={Routes.GOVERNANCE}>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black"> <Button variant={'secondary'} className="w-full">
{t('governance')} {t('governance')}
</span> </Button>
</Link> </Link>
<Link style={{ flex: 1 }} to={Routes.STAKING}> <Link className="flex-1 pl-8" to={Routes.STAKING}>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black"> <Button variant={'secondary'} className="w-full">
{t('staking')} {t('staking')}
</span> </Button>
</Link> </Link>
</WalletCardActions> </WalletCardActions>
<VegaWalletAssetList accounts={accounts} /> <VegaWalletAssetList accounts={accounts} />

View File

@ -55,6 +55,9 @@ export const ENV = {
commit: windowOrDefault('NX_COMMIT_REF'), commit: windowOrDefault('NX_COMMIT_REF'),
branch: windowOrDefault('NX_BRANCH'), branch: windowOrDefault('NX_BRANCH'),
vegaUrl: windowOrDefault('NX_VEGA_URL'), vegaUrl: windowOrDefault('NX_VEGA_URL'),
urlConnect: TRUTHY.includes(windowOrDefault('NX_ETH_URL_CONNECT')),
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
flags: { flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')), NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
HOSTED_WALLET_ENABLED: TRUTHY.includes( HOSTED_WALLET_ENABLED: TRUTHY.includes(

View File

@ -1,10 +1,8 @@
import React from 'react'; import React from 'react';
import { useContracts } from '../../contexts/contracts/contracts-context';
import mock from './tranches-mock'; import mock from './tranches-mock';
import type { Tranche } from '@vegaprotocol/smart-contracts'; import type { Tranche } from '@vegaprotocol/smart-contracts';
export function useTranches() { export function useTranches() {
const { vesting } = useContracts();
const [tranches, setTranches] = React.useState<Tranche[] | null>(null); const [tranches, setTranches] = React.useState<Tranche[] | null>(null);
const [error, setError] = React.useState<string | null>(null); const [error, setError] = React.useState<string | null>(null);
@ -17,7 +15,7 @@ export function useTranches() {
} }
}; };
run(); run();
}, [vesting]); }, []);
return { tranches, error }; return { tranches, error };
} }

View File

@ -1,3 +1,4 @@
import { toBigNum } from '@vegaprotocol/react-helpers';
import React from 'react'; import React from 'react';
import { import {
@ -7,23 +8,26 @@ import {
import { useContracts } from '../contexts/contracts/contracts-context'; import { useContracts } from '../contexts/contracts/contracts-context';
export function useRefreshAssociatedBalances() { export function useRefreshAssociatedBalances() {
const { appDispatch } = useAppState(); const {
appDispatch,
appState: { decimals },
} = useAppState();
const { staking, vesting } = useContracts(); const { staking, vesting } = useContracts();
return React.useCallback( return React.useCallback(
async (ethAddress: string, vegaKey: string) => { async (ethAddress: string, vegaKey: string) => {
const [walletAssociatedBalance, vestingAssociatedBalance] = const [walletAssociatedBalance, vestingAssociatedBalance] =
await Promise.all([ await Promise.all([
staking.stakeBalance(ethAddress, `0x${vegaKey}`), staking.stakeBalance(ethAddress, vegaKey),
vesting.stakeBalance(ethAddress, `0x${vegaKey}`), vesting.stakeBalance(ethAddress, vegaKey),
]); ]);
appDispatch({ appDispatch({
type: AppStateActionType.REFRESH_ASSOCIATED_BALANCES, type: AppStateActionType.REFRESH_ASSOCIATED_BALANCES,
walletAssociatedBalance, walletAssociatedBalance: toBigNum(walletAssociatedBalance, decimals),
vestingAssociatedBalance, vestingAssociatedBalance: toBigNum(vestingAssociatedBalance, decimals),
}); });
}, },
[staking, vesting, appDispatch] [staking, vesting, appDispatch, decimals]
); );
} }

View File

@ -193,6 +193,7 @@
"noGovernanceTokens": "You need some VEGA tokens to participate in governance", "noGovernanceTokens": "You need some VEGA tokens to participate in governance",
"youVoted": "You voted", "youVoted": "You voted",
"changeVote": "Change vote", "changeVote": "Change vote",
"voteRequested": "Please confirm transaction in wallet",
"votePending": "Casting vote", "votePending": "Casting vote",
"voteError": "Something went wrong, and your vote was not seen by the network", "voteError": "Something went wrong, and your vote was not seen by the network",
"back": "back", "back": "back",
@ -280,6 +281,7 @@
"stakingHasAssociated": "You have {{tokens}} $VEGA tokens associated.", "stakingHasAssociated": "You have {{tokens}} $VEGA tokens associated.",
"stakingAssociateMoreButton": "Associate more $VEGA tokens with wallet", "stakingAssociateMoreButton": "Associate more $VEGA tokens with wallet",
"stakingDisassociateButton": "Disassociate $VEGA tokens from wallet", "stakingDisassociateButton": "Disassociate $VEGA tokens from wallet",
"stakingConfirm": "Open you wallet app to confirm",
"associateButton": "Associate $VEGA tokens with wallet", "associateButton": "Associate $VEGA tokens with wallet",
"nodeQueryFailed": "Could not get data for validator {{node}}", "nodeQueryFailed": "Could not get data for validator {{node}}",
"stakeAddPendingTitle": "Adding {{amount}} $VEGA to validator {{node}}", "stakeAddPendingTitle": "Adding {{amount}} $VEGA to validator {{node}}",
@ -540,5 +542,8 @@
"numberOfAgainstVotes": "Number of votes against", "numberOfAgainstVotes": "Number of votes against",
"yesPercentage": "Yes percentage", "yesPercentage": "Yes percentage",
"noPercentage": "No percentage", "noPercentage": "No percentage",
"proposalTerms": "Proposal terms" "proposalTerms": "Proposal terms",
"currentlySetTo": "Currently set to ",
"pass": "Pass",
"fail": "Fail"
} }

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="bg-black w-full h-full"> <html lang="en" class="dark bg-black w-full h-full">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="stylesheet" href="//static.vega.xyz/fonts.css" /> <link rel="stylesheet" href="//static.vega.xyz/fonts.css" />

View File

@ -0,0 +1,150 @@
import { ethers, Wallet } from 'ethers';
import { Connector } from '@web3-react/types';
import { Eip1193Bridge } from '@ethersproject/experimental';
import type { ConnectionInfo } from '@ethersproject/web';
import type { Actions } from '@web3-react/types';
import { ENV } from '../config/env';
export class CustomizedBridge extends Eip1193Bridge {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async sendAsync(...args: any) {
console.debug('sendAsync called', ...args);
return this.send(...args);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
override async send(...args: any) {
console.debug('send called', ...args);
const isCallbackForm =
typeof args[0] === 'object' && typeof args[1] === 'function';
let callback;
let method;
let params;
if (isCallbackForm) {
callback = args[1];
method = args[0].method;
params = args[0].params;
} else {
method = args[0];
params = args[1];
}
try {
// Hacky, https://github.com/ethers-io/ethers.js/issues/1683#issuecomment-1016227588
// If from is present on eth_call it errors, removing it makes the library set
// from as the connected wallet which works fine
if (params && params.length && params[0].from && method === 'eth_call')
delete params[0].from;
let result;
// For sending a transaction if we call send it will error
// as it wants gasLimit in sendTransaction but hexlify sets the property gas
// to gasLimit which makes sensd transaction error.
// This has taken the code from the super method for sendTransaction and altered
// it slightly to make it work with the gas limit issues.
if (
params &&
params.length &&
params[0].from &&
method === 'eth_sendTransaction'
) {
// Hexlify will not take gas, must be gasLimit, set this property to be gasLimit
params[0].gasLimit = params[0].gas;
delete params[0].gas;
// If from is present on eth_sendTransaction it errors, removing it makes the library set
// from as the connected wallet which works fine
delete params[0].from;
const req = ethers.providers.JsonRpcProvider.hexlifyTransaction(
params[0]
);
// Hexlify sets the gasLimit property to be gas again and send transaction requires gasLimit
req['gasLimit'] = req['gas'];
delete req['gas'];
if (!this.signer) {
throw new Error('No signer');
}
// Send the transaction
const tx = await this.signer.sendTransaction(req);
result = tx.hash;
} else {
// All other transactions the base class works for
result = await super.send(method, params);
}
console.debug('result received', method, params, result);
if (isCallbackForm) {
callback(null, { result });
} else {
return result;
}
} catch (error) {
console.error(error);
if (isCallbackForm) {
callback(error, null);
} else {
throw error;
}
}
}
}
type url = string | ConnectionInfo;
export class Url extends Connector {
/** {@inheritdoc Connector.provider} */
public override provider: Eip1193Bridge | undefined;
private eagerConnection?: Promise<void>;
private url: url;
/**
* @param url - An RPC url.
* @param connectEagerly - A flag indicating whether connection should be initiated when the class is constructed.
*/
constructor(actions: Actions, url: url, connectEagerly = false) {
super(actions);
if (connectEagerly && typeof window === 'undefined') {
throw new Error(
'connectEagerly = true is invalid for SSR, instead use the activate method in a useEffect'
);
}
this.url = url;
if (connectEagerly) void this.activate();
}
private async isomorphicInitialize() {
if (this.eagerConnection) return this.eagerConnection;
await (this.eagerConnection = import('@ethersproject/providers')
.then(({ JsonRpcProvider }) => JsonRpcProvider)
.then((JsonRpcProvider) => {
const provider = new JsonRpcProvider(this.url);
const privateKey = Wallet.fromMnemonic(
ENV.ethWalletMnemonic,
`m/44'/60'/0'/0/0`
).privateKey;
const signer = new Wallet(privateKey, provider);
this.provider = new CustomizedBridge(signer, provider);
this.actions.update({ accounts: [signer.address], chainId: 1440 });
}));
}
/** {@inheritdoc Connector.activate} */
public async activate(): Promise<void> {
this.actions.startActivation();
await this.isomorphicInitialize();
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const chainId = await this.provider!.request({ method: 'eth_chainId' });
this.actions.update({ chainId: Number(chainId) });
} catch (error) {
this.actions.reportError(error as Error);
}
}
}

View File

@ -3,11 +3,18 @@ import type { Web3ReactHooks } from '@web3-react/core';
import { initializeConnector } from '@web3-react/core'; import { initializeConnector } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask'; import { MetaMask } from '@web3-react/metamask';
import { WalletConnect } from '@web3-react/walletconnect'; import { WalletConnect } from '@web3-react/walletconnect';
import { Url } from './url-connector';
import type { Connector } from '@web3-react/types';
import { ENV } from '../config/env';
const [metamask, metamaskHooks] = initializeConnector<MetaMask>( const [metamask, metamaskHooks] = initializeConnector<MetaMask>(
(actions) => new MetaMask(actions) (actions) => new MetaMask(actions)
); );
const [urlConnector, urlHooks] = initializeConnector<Url>(
(actions) => new Url(actions, ENV.localProviderUrl)
);
export const createDefaultProvider = (providerUrl: string, chainId: number) => { export const createDefaultProvider = (providerUrl: string, chainId: number) => {
return new ethers.providers.JsonRpcProvider(providerUrl, chainId); return new ethers.providers.JsonRpcProvider(providerUrl, chainId);
}; };
@ -27,7 +34,8 @@ export const createConnectors = (providerUrl: string, chainId: number) => {
[chainId] [chainId]
); );
return [ return [
ENV.urlConnect ? [urlConnector, urlHooks] : null,
[metamask, metamaskHooks], [metamask, metamaskHooks],
[walletconnect, walletconnectHooks], [walletconnect, walletconnectHooks],
] as [MetaMask | WalletConnect, Web3ReactHooks][]; ].filter(Boolean) as [Connector, Web3ReactHooks][];
}; };

View File

@ -25,7 +25,7 @@ export const TargetAddressMismatch = ({
}} }}
components={{ components={{
bold: <strong />, bold: <strong />,
red: <span className={'text-red'} />, red: <span className={'text-vega-red'} />,
}} }}
/> />
</p> </p>

View File

@ -43,7 +43,7 @@ const Contracts = () => {
title={t('View address on Etherscan')} title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${contract.address}`} href={`${ETHERSCAN_URL}/address/${contract.address}`}
> >
{config.collateral_bridge_contract.address} {contract.address}
</Link> </Link>
</div> </div>
); );
@ -58,7 +58,6 @@ const Contracts = () => {
title={t('View address on Etherscan')} title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${value}`} href={`${ETHERSCAN_URL}/address/${value}`}
> >
asdfasd
{value} {value}
</Link> </Link>
</div> </div>

View File

@ -34,30 +34,22 @@ export const CurrentProposalStatus = ({
{ addSuffix: true } { addSuffix: true }
); );
if (proposal.state === ProposalState.Open && willPass) { if (proposal.state === ProposalState.Open) {
return <StatusPass>{t('shouldPass')}</StatusPass>; if (willPass) {
} return (
<>
if (!participationMet) { {t('currentlySetTo')}
return ( <StatusPass>{t('pass')}</StatusPass>
<> </>
<span>{t('voteFailedReason')}</span> );
<span className="current-proposal-status__fail"> } else {
{t('participationNotMet')} return (
</span> <>
<span>&nbsp;{daysClosedAgo}.</span> {t('currentlySetTo')}
</> <StatusFail>{t('fail')}</StatusFail>
); </>
} );
}
if (!majorityMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('majorityNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
} }
if ( if (
@ -65,6 +57,26 @@ export const CurrentProposalStatus = ({
proposal.state === ProposalState.Declined || proposal.state === ProposalState.Declined ||
proposal.state === ProposalState.Rejected proposal.state === ProposalState.Rejected
) { ) {
if (!participationMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('participationNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
}
if (!majorityMet) {
return (
<>
<span>{t('voteFailedReason')}</span>
<StatusFail>{t('majorityNotMet')}</StatusFail>
<span>&nbsp;{daysClosedAgo}.</span>
</>
);
}
return ( return (
<> <>
<span>{t('voteFailedReason')}</span> <span>{t('voteFailedReason')}</span>

View File

@ -17,6 +17,7 @@ export enum VoteState {
NotCast = 'NotCast', NotCast = 'NotCast',
Yes = 'Yes', Yes = 'Yes',
No = 'No', No = 'No',
Requested = 'Requested',
Pending = 'Pending', Pending = 'Pending',
Failed = 'Failed', Failed = 'Failed',
} }
@ -69,7 +70,7 @@ export function useUserVote(
} }
}, [userVote]); }, [userVote]);
// Start a starts a timeout of 30s to set a failed message if // Starts a timeout of 30s to set a failed message if
// the vote is not seen by the time the callback is invoked // the vote is not seen by the time the callback is invoked
React.useEffect(() => { React.useEffect(() => {
// eslint-disable-next-line // eslint-disable-next-line
@ -93,7 +94,7 @@ export function useUserVote(
async function castVote(value: VoteValue) { async function castVote(value: VoteValue) {
if (!proposalId || !keypair) return; if (!proposalId || !keypair) return;
setVoteState(VoteState.Pending); setVoteState(VoteState.Requested);
try { try {
const variables = { const variables = {
@ -105,6 +106,7 @@ export function useUserVote(
}, },
}; };
await sendTx(variables); await sendTx(variables);
setVoteState(VoteState.Pending);
// Now await vote via poll in parent component // Now await vote via poll in parent component
} catch (err) { } catch (err) {

View File

@ -124,6 +124,10 @@ export const VoteButtons = ({
return <p>{cantVoteUI}</p>; return <p>{cantVoteUI}</p>;
} }
if (voteState === VoteState.Requested) {
return <p>{t('voteRequested')}...</p>;
}
if (voteState === VoteState.Pending) { if (voteState === VoteState.Pending) {
return <p>{t('votePending')}...</p>; return <p>{t('votePending')}...</p>;
} }

View File

@ -49,7 +49,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
<span> <span>
<CurrentProposalStatus proposal={proposal} /> <CurrentProposalStatus proposal={proposal} />
</span> </span>
.&nbsp; {'. '}
{proposal.state === ProposalState.Open ? daysLeft : null} {proposal.state === ProposalState.Open ? daysLeft : null}
</p> </p>
<table className="w-full font-normal mb-12"> <table className="w-full font-normal mb-12">

View File

@ -24,6 +24,7 @@ export const ProposalsContainer = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, { const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
pollInterval: 5000, pollInterval: 5000,
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
}); });
const proposals = React.useMemo(() => { const proposals = React.useMemo(() => {

View File

@ -52,7 +52,13 @@ const Home = ({ name }: RouteChildProps) => {
<Trans <Trans
i18nKey="Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked." i18nKey="Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked."
components={{ components={{
trancheLink: <Link to={Routes.TRANCHES} />, trancheLink: (
<Link
data-testid="tranches-link"
to={Routes.TRANCHES}
className="underline text-white"
/>
),
}} }}
/> />
</p> </p>
@ -62,7 +68,7 @@ const Home = ({ name }: RouteChildProps) => {
)} )}
</p> </p>
<Link to={Routes.VESTING}> <Link to={Routes.VESTING}>
<Button variant="secondary" data-test-id="check-vesting-page-btn"> <Button variant="secondary" data-testid="check-vesting-page-btn">
{t('Check to see if you can redeem unlocked VEGA tokens')} {t('Check to see if you can redeem unlocked VEGA tokens')}
</Button> </Button>
</Link> </Link>
@ -81,7 +87,7 @@ const Home = ({ name }: RouteChildProps) => {
</p> </p>
<p> <p>
<a <a
data-test-id="get-vega-wallet-link" data-testid="get-vega-wallet-link"
href={Links.WALLET_GUIDE} href={Links.WALLET_GUIDE}
className="underline text-white" className="underline text-white"
target="_blank" target="_blank"
@ -90,8 +96,9 @@ const Home = ({ name }: RouteChildProps) => {
{t('Get a Vega wallet')} {t('Get a Vega wallet')}
</a> </a>
</p> </p>
<p data-test-id="associate-vega-tokens-link-on-homepage"> <p>
<Link <Link
data-testid="associate-vega-tokens-link-on-homepage"
to={`${Routes.STAKING}/associate`} to={`${Routes.STAKING}/associate`}
className="underline text-white" className="underline text-white"
> >
@ -110,7 +117,12 @@ const Home = ({ name }: RouteChildProps) => {
</p> </p>
<p> <p>
<Link to={Routes.STAKING}> <Link to={Routes.STAKING}>
<Button variant="secondary">{t('Nominate a validator')}</Button> <Button
variant="secondary"
data-testid="staking-button-on-homepage"
>
{t('Nominate a validator')}
</Button>
</Link> </Link>
</p> </p>
</HomeSection> </HomeSection>
@ -125,7 +137,10 @@ const Home = ({ name }: RouteChildProps) => {
</p> </p>
<p> <p>
<Link to={Routes.GOVERNANCE}> <Link to={Routes.GOVERNANCE}>
<Button variant="secondary"> <Button
variant="secondary"
data-testid="governance-button-on-homepage"
>
{t('View Governance proposals')} {t('View Governance proposals')}
</Button> </Button>
</Link> </Link>

View File

@ -8,7 +8,6 @@ import { Link } from 'react-router-dom';
import { EthConnectPrompt } from '../../components/eth-connect-prompt'; import { EthConnectPrompt } from '../../components/eth-connect-prompt';
import { SplashLoader } from '../../components/splash-loader'; import { SplashLoader } from '../../components/splash-loader';
import { useAppState } from '../../contexts/app-state/app-state-context'; import { useAppState } from '../../contexts/app-state/app-state-context';
import { useContracts } from '../../contexts/contracts/contracts-context';
import { useTranches } from '../../hooks/use-tranches'; import { useTranches } from '../../hooks/use-tranches';
import { Routes as RoutesConfig } from '../router-config'; import { Routes as RoutesConfig } from '../router-config';
import { import {
@ -19,7 +18,6 @@ import {
const RedemptionRouter = () => { const RedemptionRouter = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { vesting } = useContracts();
const [state, dispatch] = React.useReducer( const [state, dispatch] = React.useReducer(
redemptionReducer, redemptionReducer,
initialRedemptionState initialRedemptionState
@ -49,7 +47,7 @@ const RedemptionRouter = () => {
if (account) { if (account) {
run(account); run(account);
} }
}, [account, tranches, vesting]); }, [account, tranches]);
if (error) { if (error) {
return ( return (

View File

@ -76,6 +76,7 @@ export const TrancheItem = ({
total={total} total={total}
leftLabel={t('Locked')} leftLabel={t('Locked')}
rightLabel={t('Unlocked')} rightLabel={t('Unlocked')}
decimals={18}
/> />
<div className="text-right" data-testid="tranche-item-footer"> <div className="text-right" data-testid="tranche-item-footer">

View File

@ -12,7 +12,7 @@ import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber'; import { BigNumber } from '../../../lib/bignumber';
import { AssociateInfo } from './associate-info'; import { AssociateInfo } from './associate-info';
import type { VegaKeyExtended } from '@vegaprotocol/wallet'; import type { VegaKeyExtended } from '@vegaprotocol/wallet';
import { toBigNum } from '@vegaprotocol/react-helpers'; import { removeDecimal, toBigNum } from '@vegaprotocol/react-helpers';
import type { EthereumConfig } from '@vegaprotocol/web3'; import type { EthereumConfig } from '@vegaprotocol/web3';
export const WalletAssociate = ({ export const WalletAssociate = ({
@ -35,19 +35,18 @@ export const WalletAssociate = ({
appDispatch, appDispatch,
appState: { walletBalance, allowance, walletAssociatedBalance, decimals }, appState: { walletBalance, allowance, walletAssociatedBalance, decimals },
} = useAppState(); } = useAppState();
const { token } = useContracts(); const { token } = useContracts();
const { const {
state: approveState, state: approveState,
perform: approve, perform: approve,
dispatch: approveDispatch, dispatch: approveDispatch,
} = useTransaction(() => } = useTransaction(() => {
token.approve( return token.approve(
ethereumConfig.staking_bridge_contract.address, ethereumConfig.staking_bridge_contract.address,
Number.MAX_SAFE_INTEGER.toString() removeDecimal('1000000', decimals).toString()
) );
); });
// Once they have approved deposits then we need to refresh their allowance // Once they have approved deposits then we need to refresh their allowance
React.useEffect(() => { React.useEffect(() => {

View File

@ -8,6 +8,7 @@ import {
} from '../../../components/staking-method-radio'; } from '../../../components/staking-method-radio';
import { TxState } from '../../../hooks/transaction-reducer'; import { TxState } from '../../../hooks/transaction-reducer';
import { useSearchParams } from '../../../hooks/use-search-params'; import { useSearchParams } from '../../../hooks/use-search-params';
import { useRefreshAssociatedBalances } from '../../../hooks/use-refresh-associated-balances';
import { ContractDisassociate } from './contract-disassociate'; import { ContractDisassociate } from './contract-disassociate';
import { DisassociateTransaction } from './disassociate-transaction'; import { DisassociateTransaction } from './disassociate-transaction';
import { useRemoveStake } from './hooks'; import { useRemoveStake } from './hooks';
@ -26,6 +27,7 @@ export const DisassociatePage = ({
const [amount, setAmount] = React.useState<string>(''); const [amount, setAmount] = React.useState<string>('');
const [selectedStakingMethod, setSelectedStakingMethod] = const [selectedStakingMethod, setSelectedStakingMethod] =
React.useState<StakingMethod | null>(params.method || null); React.useState<StakingMethod | null>(params.method || null);
const refreshBalances = useRefreshAssociatedBalances();
// Clear the amount when the staking method changes // Clear the amount when the staking method changes
React.useEffect(() => { React.useEffect(() => {
@ -38,6 +40,12 @@ export const DisassociatePage = ({
perform: txPerform, perform: txPerform,
} = useRemoveStake(address, amount, vegaKey.pub, selectedStakingMethod); } = useRemoveStake(address, amount, vegaKey.pub, selectedStakingMethod);
React.useEffect(() => {
if (txState.txState === TxState.Complete) {
refreshBalances(address, vegaKey.pub);
}
}, [txState, refreshBalances, address, vegaKey.pub]);
if (txState.txState !== TxState.Default) { if (txState.txState !== TxState.Default) {
return ( return (
<DisassociateTransaction <DisassociateTransaction

View File

@ -18,7 +18,14 @@ import type {
import { StakeFailure } from './stake-failure'; import { StakeFailure } from './stake-failure';
import { StakePending } from './stake-pending'; import { StakePending } from './stake-pending';
import { StakeSuccess } from './stake-success'; import { StakeSuccess } from './stake-success';
import { Button, FormGroup, RadioGroup, Radio } from '@vegaprotocol/ui-toolkit'; import {
Button,
Callout,
FormGroup,
Intent,
Radio,
RadioGroup,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import type { import type {
DelegateSubmissionBody, DelegateSubmissionBody,
@ -46,6 +53,7 @@ export const PARTY_DELEGATIONS_QUERY = gql`
enum FormState { enum FormState {
Default, Default,
Requested,
Pending, Pending,
Success, Success,
Failure, Failure,
@ -115,7 +123,7 @@ export const StakingForm = ({
}, [action, availableStakeToAdd, availableStakeToRemove]); }, [action, availableStakeToAdd, availableStakeToRemove]);
async function onSubmit() { async function onSubmit() {
setFormState(FormState.Pending); setFormState(FormState.Requested);
const delegateInput: DelegateSubmissionBody = { const delegateInput: DelegateSubmissionBody = {
pubKey: pubkey, pubKey: pubkey,
propagate: true, propagate: true,
@ -139,6 +147,7 @@ export const StakingForm = ({
try { try {
const command = action === Actions.Add ? delegateInput : undelegateInput; const command = action === Actions.Add ? delegateInput : undelegateInput;
await sendTx(command); await sendTx(command);
setFormState(FormState.Pending);
// await success via poll // await success via poll
} catch (err) { } catch (err) {
@ -184,6 +193,12 @@ export const StakingForm = ({
if (formState === FormState.Failure) { if (formState === FormState.Failure) {
return <StakeFailure nodeName={nodeName} />; return <StakeFailure nodeName={nodeName} />;
} else if (formState === FormState.Requested) {
return (
<Callout title="Confirm transaction in wallet" intent={Intent.Warning}>
<p>{t('stakingConfirm')}</p>
</Callout>
);
} else if (formState === FormState.Pending) { } else if (formState === FormState.Pending) {
return <StakePending action={action} amount={amount} nodeName={nodeName} />; return <StakePending action={action} amount={amount} nodeName={nodeName} />;
} else if (formState === FormState.Success) { } else if (formState === FormState.Success) {

View File

@ -88,7 +88,9 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
if (!nodeInfo) { if (!nodeInfo) {
return ( return (
<span className={'text-red'}>{t('stakingNodeNotFound', { node })}</span> <span className={'text-vega-red'}>
{t('stakingNodeNotFound', { node })}
</span>
); );
} }

View File

@ -40,7 +40,7 @@ export const TrancheProgress = ({
<span className="tranches__progress-title">{t('Redeemed')}</span> <span className="tranches__progress-title">{t('Redeemed')}</span>
<ProgressBar <ProgressBar
width={220} width={220}
color={Colors.green.DEFAULT} color={Colors.vega.green}
percentage={removedPercentage} percentage={removedPercentage}
/> />
<span className="tranches__progress-numbers"> <span className="tranches__progress-numbers">

View File

@ -36,7 +36,7 @@ export const VestingChart = () => {
['pink', Colors.vega.pink], ['pink', Colors.vega.pink],
['green', Colors.vega.green], ['green', Colors.vega.green],
['orange', Colors.orange], ['orange', Colors.orange],
['yellow', Colors.yellow.DEFAULT], ['yellow', Colors.vega.yellow],
].map(([key, color]) => ( ].map(([key, color]) => (
<linearGradient key={key} id={key} x1="0" y1="0" x2="0" y2="1"> <linearGradient key={key} id={key} x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={color} stopOpacity={0.85} /> <stop offset="0%" stopColor={color} stopOpacity={0.85} />
@ -119,7 +119,7 @@ export const VestingChart = () => {
dot={false} dot={false}
type="monotone" type="monotone"
dataKey="publicSale" dataKey="publicSale"
stroke={Colors.yellow.DEFAULT} stroke={Colors.vega.yellow}
fill="url(#yellow)" fill="url(#yellow)"
yAxisId={0} yAxisId={0}
strokeWidth={2} strokeWidth={2}

View File

@ -19,12 +19,14 @@ describe('orders', () => {
const orderTimeInForce = 'timeInForce'; const orderTimeInForce = 'timeInForce';
const orderCreatedAt = 'createdAt'; const orderCreatedAt = 'createdAt';
it('renders orders', () => { beforeEach(() => {
cy.getByTestId('Orders').click(); cy.getByTestId('Orders').click();
cy.getByTestId('tab-orders').contains('Please connect Vega wallet'); cy.getByTestId('tab-orders').contains('Please connect Vega wallet');
connectVegaWallet(); connectVegaWallet();
});
it('renders orders', () => {
cy.getByTestId('tab-orders').should('be.visible'); cy.getByTestId('tab-orders').should('be.visible');
cy.getByTestId('tab-orders') cy.getByTestId('tab-orders')
@ -68,4 +70,17 @@ describe('orders', () => {
cy.wrap($dateTime).invoke('text').should('not.be.empty'); cy.wrap($dateTime).invoke('text').should('not.be.empty');
}); });
}); });
it('orders are sorted by most recent order', () => {
const expectedOrderList = ['TSLA.QM21', 'BTCUSD.MF21', 'AAVEDAI.MF21'];
cy.getByTestId('tab-orders')
.get(`[col-id='${orderSymbol}']`)
.should('have.length.at.least', 3)
.each(($symbol, index) => {
if (index != 0) {
cy.wrap($symbol).should('have.text', expectedOrderList[index - 1]);
}
});
});
}); });

View File

@ -15,50 +15,6 @@ export const generateDealTicketQuery = (
positionDecimalPlaces: 0, positionDecimalPlaces: 0,
state: MarketState.Active, state: MarketState.Active,
tradingMode: MarketTradingMode.Continuous, tradingMode: MarketTradingMode.Continuous,
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
makerFee: '0.0002',
infrastructureFee: '0.0005',
liquidityFee: '0.01',
},
},
priceMonitoringSettings: {
__typename: 'PriceMonitoringSettings',
parameters: {
__typename: 'PriceMonitoringParameters',
triggers: [
{
__typename: 'PriceMonitoringTrigger',
horizonSecs: 43200,
probability: 0.9999999,
auctionExtensionSecs: 600,
},
],
},
updateFrequencySecs: 1,
},
riskFactors: {
__typename: 'RiskFactor',
market:
'54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
short: '0.008571790367285281',
long: '0.008508132993273576',
},
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
},
markPrice: '5749',
indicativeVolume: '0',
bestBidVolume: '5',
bestOfferVolume: '1',
bestStaticBidVolume: '5',
bestStaticOfferVolume: '1',
},
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
@ -74,17 +30,6 @@ export const generateDealTicketQuery = (
}, },
}, },
}, },
riskModel: {
__typename: 'LogNormalRiskModel',
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
__typename: 'LogNormalModelParams',
r: 0.016,
sigma: 0.3,
mu: 0,
},
},
}, },
depth: { depth: {
__typename: 'MarketDepth', __typename: 'MarketDepth',

View File

@ -0,0 +1,100 @@
import type { MarketInfoQuery } from '@vegaprotocol/deal-ticket';
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const generateMarketInfoQuery = (
override?: PartialDeep<MarketInfoQuery>
): MarketInfoQuery => {
const defaultResult: MarketInfoQuery = {
market: {
__typename: 'Market',
id: 'market-0',
name: 'ETHBTC Quarterly (30 Jun 2022)',
decimalPlaces: 2,
positionDecimalPlaces: 0,
state: MarketState.Active,
tradingMode: MarketTradingMode.Continuous,
fees: {
__typename: 'Fees',
factors: {
__typename: 'FeeFactors',
makerFee: '0.0002',
infrastructureFee: '0.0005',
liquidityFee: '0.01',
},
},
priceMonitoringSettings: {
__typename: 'PriceMonitoringSettings',
parameters: {
__typename: 'PriceMonitoringParameters',
triggers: [
{
__typename: 'PriceMonitoringTrigger',
horizonSecs: 43200,
probability: 0.9999999,
auctionExtensionSecs: 600,
},
],
},
updateFrequencySecs: 1,
},
riskFactors: {
__typename: 'RiskFactor',
market:
'54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
short: '0.008571790367285281',
long: '0.008508132993273576',
},
data: {
__typename: 'MarketData',
market: {
__typename: 'Market',
id: '54b78c1b877e106842ae156332ccec740ad98d6bad43143ac6a029501dd7c6e0',
},
markPrice: '5749',
indicativeVolume: '0',
bestBidVolume: '5',
bestOfferVolume: '1',
bestStaticBidVolume: '5',
bestStaticOfferVolume: '1',
},
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
product: {
__typename: 'Future',
quoteName: 'BTC',
settlementAsset: {
__typename: 'Asset',
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
name: 'tBTC TEST',
},
},
},
riskModel: {
__typename: 'LogNormalRiskModel',
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
__typename: 'LogNormalModelParams',
r: 0.016,
sigma: 0.3,
mu: 0,
},
},
},
depth: {
__typename: 'MarketDepth',
lastTrade: {
__typename: 'Trade',
price: '100',
},
},
},
};
return merge(defaultResult, override);
};

View File

@ -12,6 +12,7 @@ export const generateOrderBook = (
const marketDepth: MarketDepth_market = { const marketDepth: MarketDepth_market = {
id: 'b2426f67b085ba8fb429f1b529d49372b2d096c6fb6f509f76c5863abb6d969e', id: 'b2426f67b085ba8fb429f1b529d49372b2d096c6fb6f509f76c5863abb6d969e',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
data: { data: {
staticMidPrice: '826337', staticMidPrice: '826337',
marketTradingMode: MarketTradingMode.Continuous, marketTradingMode: MarketTradingMode.Continuous,

View File

@ -18,6 +18,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d', id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
name: 'AAVEDAI Monthly (30 Jun 2022)', name: 'AAVEDAI Monthly (30 Jun 2022)',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
@ -33,7 +34,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
remaining: '0', remaining: '0',
price: '20000000', price: '20000000',
timeInForce: OrderTimeInForce.GTC, timeInForce: OrderTimeInForce.GTC,
createdAt: new Date().toISOString(), createdAt: new Date(2020, 1, 1).toISOString(),
updatedAt: null, updatedAt: null,
expiresAt: null, expiresAt: null,
rejectionReason: null, rejectionReason: null,
@ -46,6 +47,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376', id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
name: 'Tesla Quarterly (30 Jun 2022)', name: 'Tesla Quarterly (30 Jun 2022)',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
@ -59,13 +61,42 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
status: OrderStatus.Filled, status: OrderStatus.Filled,
side: Side.Buy, side: Side.Buy,
remaining: '0', remaining: '0',
price: '0', price: '100',
timeInForce: OrderTimeInForce.GTC, timeInForce: OrderTimeInForce.GTC,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
updatedAt: null, updatedAt: null,
expiresAt: null, expiresAt: null,
rejectionReason: null, rejectionReason: null,
}, },
{
__typename: 'Order',
id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
market: {
__typename: 'Market',
id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
name: 'BTCUSD Monthly (30 Jun 2022)',
decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
code: 'BTCUSD.MF21',
},
},
},
size: '1',
type: OrderType.Limit,
status: OrderStatus.Filled,
side: Side.Buy,
remaining: '0',
price: '20000',
timeInForce: OrderTimeInForce.GTC,
createdAt: new Date(2022, 5, 10).toISOString(),
updatedAt: null,
expiresAt: null,
rejectionReason: null,
},
]; ];
const defaultResult = { const defaultResult = {

View File

@ -25,6 +25,7 @@ export const generatePositions = (
market: { __typename: 'Market', id: '123' }, market: { __typename: 'Market', id: '123' },
}, },
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: { tradableInstrument: {
instrument: { instrument: {
id: '', id: '',
@ -78,6 +79,7 @@ export const generatePositions = (
}, },
}, },
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
tradableInstrument: { tradableInstrument: {
instrument: { instrument: {
id: '', id: '',

View File

@ -12,6 +12,7 @@ export const generateTrades = (override?: PartialDeep<Trades>): Trades => {
market: { market: {
id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
__typename: 'Market', __typename: 'Market',
}, },
__typename: 'Trade', __typename: 'Trade',
@ -24,6 +25,7 @@ export const generateTrades = (override?: PartialDeep<Trades>): Trades => {
market: { market: {
id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
__typename: 'Market', __typename: 'Market',
}, },
__typename: 'Trade', __typename: 'Trade',
@ -36,6 +38,7 @@ export const generateTrades = (override?: PartialDeep<Trades>): Trades => {
market: { market: {
id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff',
decimalPlaces: 5, decimalPlaces: 5,
positionDecimalPlaces: 0,
__typename: 'Market', __typename: 'Market',
}, },
__typename: 'Trade', __typename: 'Trade',

View File

@ -6,6 +6,7 @@ import { generateCandles } from './mocks/generate-candles';
import { generateChart } from './mocks/generate-chart'; import { generateChart } from './mocks/generate-chart';
import { generateDealTicketQuery } from './mocks/generate-deal-ticket-query'; import { generateDealTicketQuery } from './mocks/generate-deal-ticket-query';
import { generateMarket } from './mocks/generate-market'; import { generateMarket } from './mocks/generate-market';
import { generateMarketInfoQuery } from './mocks/generate-market-info-query';
import { generateOrders } from './mocks/generate-orders'; import { generateOrders } from './mocks/generate-orders';
import { generatePositions } from './mocks/generate-positions'; import { generatePositions } from './mocks/generate-positions';
import { generateTrades } from './mocks/generate-trades'; import { generateTrades } from './mocks/generate-trades';
@ -31,6 +32,11 @@ export const mockTradingPage = (
'DealTicketQuery', 'DealTicketQuery',
generateDealTicketQuery({ market: { state } }) generateDealTicketQuery({ market: { state } })
); );
aliasQuery(
req,
'MarketInfoQuery',
generateMarketInfoQuery({ market: { state } })
);
aliasQuery(req, 'Trades', generateTrades()); aliasQuery(req, 'Trades', generateTrades());
aliasQuery(req, 'Chart', generateChart()); aliasQuery(req, 'Chart', generateChart());
aliasQuery(req, 'Candles', generateCandles()); aliasQuery(req, 'Candles', generateCandles());

View File

@ -1,8 +1,8 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Vega } from '../icons/vega'; import { Vega } from '../icons/vega';
import Link from 'next/link'; import Link from 'next/link';
import { AnchorButton } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import classNames from 'classnames';
export const Navbar = () => { export const Navbar = () => {
return ( return (
@ -33,14 +33,17 @@ const NavLink = ({ name, path, exact, testId = name }: NavLinkProps) => {
const router = useRouter(); const router = useRouter();
const isActive = const isActive =
router.asPath === path || (!exact && router.asPath.startsWith(path)); router.asPath === path || (!exact && router.asPath.startsWith(path));
const linkClasses = classNames(
'px-16 py-6 border-0 self-end',
'uppercase xs:text-ui sm:text-body-large md:text-h5 lg:text-h4',
{
'bg-vega-pink dark:bg-vega-yellow text-white dark:text-black': isActive,
'text-black dark:text-white': !isActive,
}
);
return ( return (
<AnchorButton <Link data-testid={testId} href={path} passHref={true}>
variant={isActive ? 'accent' : 'inline'} <a className={linkClasses}>{name}</a>
className="px-16 py-6 h-[38px] uppercase border-0 self-end xs:text-ui sm:text-body-large md:text-h5 lg:text-h4" </Link>
data-testid={testId}
href={path}
>
{name}
</AnchorButton>
); );
}; };

View File

@ -24,84 +24,80 @@ function AppBody({ Component, pageProps }: AppProps) {
const { push } = useRouter(); const { push } = useRouter();
const store = useGlobalStore(); const store = useGlobalStore();
const { VEGA_NETWORKS } = useEnvironment(); const { VEGA_NETWORKS } = useEnvironment();
const [, toggleTheme] = useThemeSwitcher(); const [theme, toggleTheme] = useThemeSwitcher();
return ( return (
<div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]"> <ThemeContext.Provider value={theme}>
<AppLoader> <div className="h-full dark:bg-black dark:text-white-60 bg-white relative z-0 text-black-60 grid grid-rows-[min-content,1fr]">
<div className="flex items-stretch border-b-[7px] border-vega-yellow"> <AppLoader>
<Navbar /> <div className="flex items-stretch border-b-[7px] border-vega-pink dark:border-vega-yellow">
<div className="flex items-center gap-4 ml-auto mr-8"> <Navbar />
<VegaWalletConnectButton <div className="flex items-center gap-4 ml-auto mr-8">
setConnectDialog={(open) => { <VegaWalletConnectButton
store.setVegaWalletConnectDialog(open); setConnectDialog={(open) => {
}} store.setVegaWalletConnectDialog(open);
setManageDialog={(open) => { }}
store.setVegaWalletManageDialog(open); setManageDialog={(open) => {
}} store.setVegaWalletManageDialog(open);
/> }}
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" /> />
<ThemeSwitcher onToggle={toggleTheme} className="-my-4" />
</div>
</div> </div>
</div> <main data-testid={pageProps.page}>
<main data-testid={pageProps.page}> {/* @ts-ignore conflict between @types/react and nextjs internal types */}
{/* @ts-ignore conflict between @types/react and nextjs internal types */} <Component {...pageProps} />
<Component {...pageProps} /> </main>
</main> <VegaConnectDialog
<VegaConnectDialog connectors={Connectors}
connectors={Connectors} dialogOpen={store.vegaWalletConnectDialog}
dialogOpen={store.vegaWalletConnectDialog} setDialogOpen={(open) => store.setVegaWalletConnectDialog(open)}
setDialogOpen={(open) => store.setVegaWalletConnectDialog(open)} />
/> <VegaManageDialog
<VegaManageDialog dialogOpen={store.vegaWalletManageDialog}
dialogOpen={store.vegaWalletManageDialog} setDialogOpen={(open) => store.setVegaWalletManageDialog(open)}
setDialogOpen={(open) => store.setVegaWalletManageDialog(open)} />
/> <NetworkSwitcherDialog
<NetworkSwitcherDialog dialogOpen={store.vegaNetworkSwitcherDialog}
dialogOpen={store.vegaNetworkSwitcherDialog} setDialogOpen={(open) => store.setVegaNetworkSwitcherDialog(open)}
setDialogOpen={(open) => store.setVegaNetworkSwitcherDialog(open)} onConnect={({ network }) => {
onConnect={({ network }) => { if (VEGA_NETWORKS[network]) {
if (VEGA_NETWORKS[network]) { push(VEGA_NETWORKS[network] ?? '');
push(VEGA_NETWORKS[network] ?? ''); }
} }}
}} />
/> </AppLoader>
</AppLoader> </div>
</div> </ThemeContext.Provider>
); );
} }
function VegaTradingApp(props: AppProps) { function VegaTradingApp(props: AppProps) {
const [theme] = useThemeSwitcher();
return ( return (
<EnvironmentProvider> <EnvironmentProvider>
<ThemeContext.Provider value={theme}> <VegaWalletProvider>
<VegaWalletProvider> <Head>
<Head> <link
<link rel="preload"
rel="preload" href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2" as="font"
as="font" type="font/woff2"
type="font/woff2" crossOrigin="anonymous"
crossOrigin="anonymous" />
/> <title>{t('Welcome to Vega trading!')}</title>
<title>{t('Welcome to Vega trading!')}</title> <link
<link rel="icon"
rel="icon" type="image/x-icon"
type="image/x-icon" href="https://static.vega.xyz/favicon.ico"
href="https://static.vega.xyz/favicon.ico" />
/> <link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" /> {['1', 'true'].includes(process.env['NX_USE_ENV_OVERRIDES'] || '') ? (
{['1', 'true'].includes( /* eslint-disable-next-line @next/next/no-sync-scripts */
process.env['NX_USE_ENV_OVERRIDES'] || '' <script src="./env-config.js" type="text/javascript" />
) ? ( ) : null}
/* eslint-disable-next-line @next/next/no-sync-scripts */ </Head>
<script src="./env-config.js" type="text/javascript" /> <AppBody {...props} />
) : null} </VegaWalletProvider>
</Head>
<AppBody {...props} />
</VegaWalletProvider>
</ThemeContext.Provider>
</EnvironmentProvider> </EnvironmentProvider>
); );
} }

View File

@ -2,7 +2,10 @@ import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { DealTicketContainer } from '@vegaprotocol/deal-ticket'; import {
DealTicketContainer,
MarketInfoContainer,
} from '@vegaprotocol/deal-ticket';
import { OrderListContainer } from '@vegaprotocol/order-list'; import { OrderListContainer } from '@vegaprotocol/order-list';
import { TradesContainer } from '@vegaprotocol/trades'; import { TradesContainer } from '@vegaprotocol/trades';
import { PositionsContainer } from '@vegaprotocol/positions'; import { PositionsContainer } from '@vegaprotocol/positions';
@ -15,8 +18,8 @@ import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { SelectMarketDialog } from '@vegaprotocol/market-list'; import { SelectMarketDialog } from '@vegaprotocol/market-list';
import { import {
ArrowDown, ArrowDown,
GridTab, Tab,
GridTabs, Tabs,
PriceCellChange, PriceCellChange,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import type { CandleClose } from '@vegaprotocol/types'; import type { CandleClose } from '@vegaprotocol/types';
@ -30,6 +33,7 @@ const TradingViews = {
Positions: PositionsContainer, Positions: PositionsContainer,
Accounts: AccountsContainer, Accounts: AccountsContainer,
Trades: TradesContainer, Trades: TradesContainer,
Info: MarketInfoContainer,
}; };
type TradingView = keyof typeof TradingViews; type TradingView = keyof typeof TradingViews;
@ -52,17 +56,17 @@ export const TradeMarketHeader = ({
'font-sans font-normal mb-0 text-dark/80 dark:text-white/80 text-ui-small'; 'font-sans font-normal mb-0 text-dark/80 dark:text-white/80 text-ui-small';
const itemValueClassName = const itemValueClassName =
'capitalize font-sans tracking-tighter text-black dark:text-white text-ui'; 'capitalize font-sans tracking-tighter text-black dark:text-white text-ui';
const headerClassname = classNames( const headerClassName = classNames(
'w-full p-8 bg-white dark:bg-black', 'w-full p-8 bg-white dark:bg-black',
className className
); );
return ( return (
<header className={headerClassname}> <header className={headerClassName}>
<SelectMarketDialog dialogOpen={open} setDialogOpen={setOpen} /> <SelectMarketDialog dialogOpen={open} setDialogOpen={setOpen} />
<div className="flex flex-col md:flex-row gap-20 md:gap-64 ml-auto mr-8"> <div className="flex flex-col md:flex-row gap-20 md:gap-64 ml-auto mr-8">
<button <button
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
className="shrink-0 dark:text-vega-yellow text-black text-h5 flex items-center gap-8 px-4 py-0 h-37 hover:bg-vega-yellow dark:hover:bg-white/20" className="shrink-0 dark:text-vega-yellow text-black text-h5 flex items-center gap-8 px-4 py-0 h-37 hover:bg-black/20 dark:hover:bg-white/20"
> >
<span className="break-words text-left">{market.name}</span> <span className="break-words text-left">{market.name}</span>
<ArrowDown color="yellow" borderX={8} borderTop={12} /> <ArrowDown color="yellow" borderX={8} borderTop={12} />
@ -105,7 +109,7 @@ interface TradeGridProps {
export const TradeGrid = ({ market }: TradeGridProps) => { export const TradeGrid = ({ market }: TradeGridProps) => {
const wrapperClasses = classNames( const wrapperClasses = classNames(
'h-full max-h-full', 'h-full max-h-full',
'grid gap-4 grid-cols-[1fr_375px_460px] grid-rows-[min-content_1fr_200px]', 'grid gap-4 grid-cols-[1fr_375px_460px] grid-rows-[min-content_1fr_300px]',
'bg-black-10 dark:bg-white-10', 'bg-black-10 dark:bg-white-10',
'text-ui' 'text-ui'
); );
@ -115,43 +119,50 @@ export const TradeGrid = ({ market }: TradeGridProps) => {
<div className={wrapperClasses}> <div className={wrapperClasses}>
<TradeMarketHeader <TradeMarketHeader
market={market} market={market}
className="row-start-1 row-end-2 col-start-1 col-end-2" className="row-start-1 row-end-2 col-start-1 col-end-4"
/> />
<TradeGridChild className="row-start-2 row-end-3 col-start-1 col-end-2"> <TradeGridChild className="row-start-2 row-end-3 col-start-1 col-end-2">
<GridTabs> <Tabs>
<GridTab id="candles" name={t('Candles')}> <Tab id="candles" name={t('Candles')}>
<TradingViews.Candles marketId={market.id} /> <TradingViews.Candles marketId={market.id} />
</GridTab> </Tab>
<GridTab id="depth" name={t('Depth')}> <Tab id="depth" name={t('Depth')}>
<TradingViews.Depth marketId={market.id} /> <TradingViews.Depth marketId={market.id} />
</GridTab> </Tab>
</GridTabs> </Tabs>
</TradeGridChild> </TradeGridChild>
<TradeGridChild className="row-start-1 row-end-3"> <TradeGridChild className="row-start-2 row-end-3 col-start-2 col-end-3">
<TradingViews.Ticket marketId={market.id} /> <Tabs>
<Tab id="ticket" name={t('Ticket')}>
<TradingViews.Ticket marketId={market.id} />
</Tab>
<Tab id="info" name={t('Info')}>
<TradingViews.Info marketId={market.id} />
</Tab>
</Tabs>
</TradeGridChild> </TradeGridChild>
<TradeGridChild className="row-start-1 row-end-3"> <TradeGridChild className="row-start-2 row-end-3 col-start-3 col-end-4">
<GridTabs> <Tabs>
<GridTab id="trades" name={t('Trades')}> <Tab id="trades" name={t('Trades')}>
<TradingViews.Trades marketId={market.id} /> <TradingViews.Trades marketId={market.id} />
</GridTab> </Tab>
<GridTab id="orderbook" name={t('Orderbook')}> <Tab id="orderbook" name={t('Orderbook')}>
<TradingViews.Orderbook marketId={market.id} /> <TradingViews.Orderbook marketId={market.id} />
</GridTab> </Tab>
</GridTabs> </Tabs>
</TradeGridChild> </TradeGridChild>
<TradeGridChild className="col-span-3"> <TradeGridChild className="col-span-3">
<GridTabs> <Tabs>
<GridTab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<TradingViews.Orders /> <TradingViews.Orders />
</GridTab> </Tab>
<GridTab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<TradingViews.Positions /> <TradingViews.Positions />
</GridTab> </Tab>
<GridTab id="accounts" name={t('Accounts')}> <Tab id="accounts" name={t('Accounts')}>
<TradingViews.Accounts /> <TradingViews.Accounts />
</GridTab> </Tab>
</GridTabs> </Tabs>
</TradeGridChild> </TradeGridChild>
</div> </div>
</> </>

View File

@ -3,8 +3,7 @@ import { t } from '@vegaprotocol/react-helpers';
import { PositionsContainer } from '@vegaprotocol/positions'; import { PositionsContainer } from '@vegaprotocol/positions';
import { OrderListContainer } from '@vegaprotocol/order-list'; import { OrderListContainer } from '@vegaprotocol/order-list';
import { AccountsContainer } from '@vegaprotocol/accounts'; import { AccountsContainer } from '@vegaprotocol/accounts';
import { AnchorButton, GridTab, GridTabs } from '@vegaprotocol/ui-toolkit'; import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { WithdrawalsContainer } from './withdrawals/withdrawals-container'; import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
const Portfolio = () => { const Portfolio = () => {
@ -20,54 +19,54 @@ const Portfolio = () => {
</h2> </h2>
</aside> </aside>
<section data-testid="portfolio-grid"> <section data-testid="portfolio-grid">
<GridTabs> <Tabs>
<GridTab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<div className={tabClassName}> <div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white"> <h4 className="text-h4 text-black dark:text-white">
{t('Positions')} {t('Positions')}
</h4> </h4>
<PositionsContainer /> <PositionsContainer />
</div> </div>
</GridTab> </Tab>
<GridTab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<div className={tabClassName}> <div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white"> <h4 className="text-h4 text-black dark:text-white">
{t('Orders')} {t('Orders')}
</h4> </h4>
<OrderListContainer /> <OrderListContainer />
</div> </div>
</GridTab> </Tab>
<GridTab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<div className={tabClassName}> <div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white"> <h4 className="text-h4 text-black dark:text-white">
{t('Fills')} {t('Fills')}
</h4> </h4>
</div> </div>
</GridTab> </Tab>
<GridTab id="history" name={t('History')}> <Tab id="history" name={t('History')}>
<div className={tabClassName}> <div className={tabClassName}>
<h4 className="text-h4 text-black dark:text-white"> <h4 className="text-h4 text-black dark:text-white">
{t('History')} {t('History')}
</h4> </h4>
</div> </div>
</GridTab> </Tab>
</GridTabs> </Tabs>
</section> </section>
</main> </main>
<section className="fixed bottom-0 left-0 w-full h-[200px]"> <section className="fixed bottom-0 left-0 w-full h-[200px]">
<GridTabs> <Tabs>
<GridTab id="collateral" name={t('Collateral')}> <Tab id="collateral" name={t('Collateral')}>
<AccountsContainer /> <AccountsContainer />
</GridTab> </Tab>
<GridTab id="deposits" name={t('Deposits')}> <Tab id="deposits" name={t('Deposits')}>
<AnchorButton data-testid="deposit" href="/portfolio/deposit"> <AnchorButton data-testid="deposit" href="/portfolio/deposit">
{t('Deposit')} {t('Deposit')}
</AnchorButton> </AnchorButton>
</GridTab> </Tab>
<GridTab id="withdrawals" name={t('Withdrawals')}> <Tab id="withdrawals" name={t('Withdrawals')}>
<WithdrawalsContainer /> <WithdrawalsContainer />
</GridTab> </Tab>
</GridTabs> </Tabs>
</section> </section>
</div> </div>
</Web3Container> </Web3Container>

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import type { import type {
Accounts, Accounts,
@ -52,17 +53,18 @@ export const getId = (
) => `${data.type}-${data.asset.symbol}-${data.market?.id ?? 'null'}`; ) => `${data.type}-${data.asset.symbol}-${data.market?.id ?? 'null'}`;
const update = ( const update = (
draft: Accounts_party_accounts[], data: Accounts_party_accounts[],
delta: AccountSubscribe_accounts delta: AccountSubscribe_accounts
) => { ) =>
const id = getId(delta); produce(data, (draft) => {
const index = draft.findIndex((a) => getId(a) === id); const id = getId(delta);
if (index !== -1) { const index = draft.findIndex((a) => getId(a) === id);
draft[index] = delta; if (index !== -1) {
} else { draft[index] = delta;
draft.push(delta); } else {
} draft.push(delta);
}; }
});
const getData = (responseData: Accounts): Accounts_party_accounts[] | null => const getData = (responseData: Accounts): Accounts_party_accounts[] | null =>
responseData.party ? responseData.party.accounts : null; responseData.party ? responseData.party.accounts : null;
const getDelta = ( const getDelta = (

View File

@ -17,7 +17,6 @@ import { useContext, useMemo, useState } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { ThemeContext } from '@vegaprotocol/react-helpers'; import { ThemeContext } from '@vegaprotocol/react-helpers';
import { import {
Button,
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem, DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
@ -58,14 +57,14 @@ export const CandlesChartContainer = ({
return new VegaDataSource(client, marketId, keypair?.pub); return new VegaDataSource(client, marketId, keypair?.pub);
}, [client, marketId, keypair]); }, [client, marketId, keypair]);
const dropdownTriggerStyles = 'border-black-60 dark:border-white-60 px-20';
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="p-8 flex flex-row flex-wrap gap-8"> <div className="p-8 flex flex-row flex-wrap gap-8">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild={true}> <DropdownMenuTrigger className={dropdownTriggerStyles}>
<Button appendIconName="caret-down" variant="secondary"> {t('Interval')}
{t('Interval')}
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuRadioGroup <DropdownMenuRadioGroup
@ -90,10 +89,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild={true}> <DropdownMenuTrigger className={dropdownTriggerStyles}>
<Button appendIconName="caret-down" variant="secondary"> <Icon name={chartTypeIcon.get(chartType) as IconName} />
<Icon name={chartTypeIcon.get(chartType) as IconName} />
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuRadioGroup <DropdownMenuRadioGroup
@ -114,10 +111,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild={true}> <DropdownMenuTrigger className={dropdownTriggerStyles}>
<Button appendIconName="caret-down" variant="secondary"> {t('Overlays')}
{t('Overlays')}
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
{Object.values(Overlay).map((overlay) => ( {Object.values(Overlay).map((overlay) => (
@ -145,10 +140,8 @@ export const CandlesChartContainer = ({
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild={true}> <DropdownMenuTrigger className={dropdownTriggerStyles}>
<Button appendIconName="caret-down" variant="secondary"> {t('Studies')}
{t('Studies')}
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
{Object.values(Study).map((study) => ( {Object.values(Study).map((study) => (

View File

@ -9,124 +9,6 @@ import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
// GraphQL query operation: DealTicketQuery // GraphQL query operation: DealTicketQuery
// ==================================================== // ====================================================
export interface DealTicketQuery_market_fees_factors {
__typename: "FeeFactors";
/**
* The factor applied to calculate MakerFees, a non-negative float
*/
makerFee: string;
/**
* The factor applied to calculate InfrastructureFees, a non-negative float
*/
infrastructureFee: string;
/**
* The factor applied to calculate LiquidityFees, a non-negative float
*/
liquidityFee: string;
}
export interface DealTicketQuery_market_fees {
__typename: "Fees";
/**
* The factors used to calculate the different fees
*/
factors: DealTicketQuery_market_fees_factors;
}
export interface DealTicketQuery_market_priceMonitoringSettings_parameters_triggers {
__typename: "PriceMonitoringTrigger";
/**
* Price monitoring projection horizon τ in seconds (> 0).
*/
horizonSecs: number;
/**
* Price monitoring probability level p. (>0 and < 1)
*/
probability: number;
/**
* Price monitoring auction extension duration in seconds should the price
* breach it's theoretical level over the specified horizon at the specified
* probability level (> 0)
*/
auctionExtensionSecs: number;
}
export interface DealTicketQuery_market_priceMonitoringSettings_parameters {
__typename: "PriceMonitoringParameters";
/**
* The list of triggers for this price monitoring
*/
triggers: DealTicketQuery_market_priceMonitoringSettings_parameters_triggers[] | null;
}
export interface DealTicketQuery_market_priceMonitoringSettings {
__typename: "PriceMonitoringSettings";
/**
* Specified a set of PriceMonitoringParameters to be use for price monitoring purposes
*/
parameters: DealTicketQuery_market_priceMonitoringSettings_parameters | null;
/**
* How often (in seconds) the price monitoring bounds should be updated
*/
updateFrequencySecs: number;
}
export interface DealTicketQuery_market_riskFactors {
__typename: "RiskFactor";
/**
* market the risk factor was emitted for
*/
market: string;
/**
* short factor
*/
short: string;
/**
* long factor
*/
long: string;
}
export interface DealTicketQuery_market_data_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface DealTicketQuery_market_data {
__typename: "MarketData";
/**
* market id of the associated mark price
*/
market: DealTicketQuery_market_data_market;
/**
* the mark price (actually an unsigned int)
*/
markPrice: string;
/**
* indicative volume if the auction ended now, 0 if not in auction mode
*/
indicativeVolume: string;
/**
* the aggregated volume being bid at the best bid price.
*/
bestBidVolume: string;
/**
* the aggregated volume being offered at the best offer price.
*/
bestOfferVolume: string;
/**
* the aggregated volume being offered at the best static bid price, excluding pegged orders
*/
bestStaticBidVolume: string;
/**
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
*/
bestStaticOfferVolume: string;
}
export interface DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset { export interface DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset {
__typename: "Asset"; __typename: "Asset";
/** /**
@ -163,70 +45,12 @@ export interface DealTicketQuery_market_tradableInstrument_instrument {
product: DealTicketQuery_market_tradableInstrument_instrument_product; product: DealTicketQuery_market_tradableInstrument_instrument_product;
} }
export interface DealTicketQuery_market_tradableInstrument_riskModel_LogNormalRiskModel_params {
__typename: "LogNormalModelParams";
/**
* r parameter
*/
r: number;
/**
* sigma parameter
*/
sigma: number;
/**
* mu parameter
*/
mu: number;
}
export interface DealTicketQuery_market_tradableInstrument_riskModel_LogNormalRiskModel {
__typename: "LogNormalRiskModel";
/**
* Tau parameter of the risk model
*/
tau: number;
/**
* Lambda parameter of the risk model
*/
riskAversionParameter: number;
/**
* Params for the log normal risk model
*/
params: DealTicketQuery_market_tradableInstrument_riskModel_LogNormalRiskModel_params;
}
export interface DealTicketQuery_market_tradableInstrument_riskModel_SimpleRiskModel_params {
__typename: "SimpleRiskModelParams";
/**
* Risk factor for long
*/
factorLong: number;
/**
* Risk factor for short
*/
factorShort: number;
}
export interface DealTicketQuery_market_tradableInstrument_riskModel_SimpleRiskModel {
__typename: "SimpleRiskModel";
/**
* Params for the simple risk model
*/
params: DealTicketQuery_market_tradableInstrument_riskModel_SimpleRiskModel_params;
}
export type DealTicketQuery_market_tradableInstrument_riskModel = DealTicketQuery_market_tradableInstrument_riskModel_LogNormalRiskModel | DealTicketQuery_market_tradableInstrument_riskModel_SimpleRiskModel;
export interface DealTicketQuery_market_tradableInstrument { export interface DealTicketQuery_market_tradableInstrument {
__typename: "TradableInstrument"; __typename: "TradableInstrument";
/** /**
* An instance of or reference to a fully specified instrument. * An instance of or reference to a fully specified instrument.
*/ */
instrument: DealTicketQuery_market_tradableInstrument_instrument; instrument: DealTicketQuery_market_tradableInstrument_instrument;
/**
* A reference to a risk model that is valid for the instrument
*/
riskModel: DealTicketQuery_market_tradableInstrument_riskModel;
} }
export interface DealTicketQuery_market_depth_lastTrade { export interface DealTicketQuery_market_depth_lastTrade {
@ -286,22 +110,6 @@ export interface DealTicketQuery_market {
* Current mode of execution of the market * Current mode of execution of the market
*/ */
tradingMode: MarketTradingMode; tradingMode: MarketTradingMode;
/**
* Fees related data
*/
fees: DealTicketQuery_market_fees;
/**
* Price monitoring settings for the market
*/
priceMonitoringSettings: DealTicketQuery_market_priceMonitoringSettings;
/**
* risk factors for the market
*/
riskFactors: DealTicketQuery_market_riskFactors | null;
/**
* marketData for the given market
*/
data: DealTicketQuery_market_data | null;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -0,0 +1,324 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: MarketInfoQuery
// ====================================================
export interface MarketInfoQuery_market_fees_factors {
__typename: "FeeFactors";
/**
* The factor applied to calculate MakerFees, a non-negative float
*/
makerFee: string;
/**
* The factor applied to calculate InfrastructureFees, a non-negative float
*/
infrastructureFee: string;
/**
* The factor applied to calculate LiquidityFees, a non-negative float
*/
liquidityFee: string;
}
export interface MarketInfoQuery_market_fees {
__typename: "Fees";
/**
* The factors used to calculate the different fees
*/
factors: MarketInfoQuery_market_fees_factors;
}
export interface MarketInfoQuery_market_priceMonitoringSettings_parameters_triggers {
__typename: "PriceMonitoringTrigger";
/**
* Price monitoring projection horizon τ in seconds (> 0).
*/
horizonSecs: number;
/**
* Price monitoring probability level p. (>0 and < 1)
*/
probability: number;
/**
* Price monitoring auction extension duration in seconds should the price
* breach it's theoretical level over the specified horizon at the specified
* probability level (> 0)
*/
auctionExtensionSecs: number;
}
export interface MarketInfoQuery_market_priceMonitoringSettings_parameters {
__typename: "PriceMonitoringParameters";
/**
* The list of triggers for this price monitoring
*/
triggers: MarketInfoQuery_market_priceMonitoringSettings_parameters_triggers[] | null;
}
export interface MarketInfoQuery_market_priceMonitoringSettings {
__typename: "PriceMonitoringSettings";
/**
* Specified a set of PriceMonitoringParameters to be use for price monitoring purposes
*/
parameters: MarketInfoQuery_market_priceMonitoringSettings_parameters | null;
/**
* How often (in seconds) the price monitoring bounds should be updated
*/
updateFrequencySecs: number;
}
export interface MarketInfoQuery_market_riskFactors {
__typename: "RiskFactor";
/**
* market the risk factor was emitted for
*/
market: string;
/**
* short factor
*/
short: string;
/**
* long factor
*/
long: string;
}
export interface MarketInfoQuery_market_data_market {
__typename: "Market";
/**
* Market ID
*/
id: string;
}
export interface MarketInfoQuery_market_data {
__typename: "MarketData";
/**
* market id of the associated mark price
*/
market: MarketInfoQuery_market_data_market;
/**
* the mark price (actually an unsigned int)
*/
markPrice: string;
/**
* indicative volume if the auction ended now, 0 if not in auction mode
*/
indicativeVolume: string;
/**
* the aggregated volume being bid at the best bid price.
*/
bestBidVolume: string;
/**
* the aggregated volume being offered at the best offer price.
*/
bestOfferVolume: string;
/**
* the aggregated volume being offered at the best static bid price, excluding pegged orders
*/
bestStaticBidVolume: string;
/**
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
*/
bestStaticOfferVolume: string;
}
export interface MarketInfoQuery_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 MarketInfoQuery_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: MarketInfoQuery_market_tradableInstrument_instrument_product_settlementAsset;
}
export interface MarketInfoQuery_market_tradableInstrument_instrument {
__typename: "Instrument";
/**
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
*/
product: MarketInfoQuery_market_tradableInstrument_instrument_product;
}
export interface MarketInfoQuery_market_tradableInstrument_riskModel_LogNormalRiskModel_params {
__typename: "LogNormalModelParams";
/**
* r parameter
*/
r: number;
/**
* sigma parameter
*/
sigma: number;
/**
* mu parameter
*/
mu: number;
}
export interface MarketInfoQuery_market_tradableInstrument_riskModel_LogNormalRiskModel {
__typename: "LogNormalRiskModel";
/**
* Tau parameter of the risk model
*/
tau: number;
/**
* Lambda parameter of the risk model
*/
riskAversionParameter: number;
/**
* Params for the log normal risk model
*/
params: MarketInfoQuery_market_tradableInstrument_riskModel_LogNormalRiskModel_params;
}
export interface MarketInfoQuery_market_tradableInstrument_riskModel_SimpleRiskModel_params {
__typename: "SimpleRiskModelParams";
/**
* Risk factor for long
*/
factorLong: number;
/**
* Risk factor for short
*/
factorShort: number;
}
export interface MarketInfoQuery_market_tradableInstrument_riskModel_SimpleRiskModel {
__typename: "SimpleRiskModel";
/**
* Params for the simple risk model
*/
params: MarketInfoQuery_market_tradableInstrument_riskModel_SimpleRiskModel_params;
}
export type MarketInfoQuery_market_tradableInstrument_riskModel = MarketInfoQuery_market_tradableInstrument_riskModel_LogNormalRiskModel | MarketInfoQuery_market_tradableInstrument_riskModel_SimpleRiskModel;
export interface MarketInfoQuery_market_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
*/
instrument: MarketInfoQuery_market_tradableInstrument_instrument;
/**
* A reference to a risk model that is valid for the instrument
*/
riskModel: MarketInfoQuery_market_tradableInstrument_riskModel;
}
export interface MarketInfoQuery_market_depth_lastTrade {
__typename: "Trade";
/**
* The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
*/
price: string;
}
export interface MarketInfoQuery_market_depth {
__typename: "MarketDepth";
/**
* Last trade for the given market (if available)
*/
lastTrade: MarketInfoQuery_market_depth_lastTrade | null;
}
export interface MarketInfoQuery_market {
__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;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/**
* Current state of the market
*/
state: MarketState;
/**
* Current mode of execution of the market
*/
tradingMode: MarketTradingMode;
/**
* Fees related data
*/
fees: MarketInfoQuery_market_fees;
/**
* Price monitoring settings for the market
*/
priceMonitoringSettings: MarketInfoQuery_market_priceMonitoringSettings;
/**
* risk factors for the market
*/
riskFactors: MarketInfoQuery_market_riskFactors | null;
/**
* marketData for the given market
*/
data: MarketInfoQuery_market_data | null;
/**
* An instance of or reference to a tradable instrument.
*/
tradableInstrument: MarketInfoQuery_market_tradableInstrument;
/**
* Current depth on the order book for this market
*/
depth: MarketInfoQuery_market_depth;
}
export interface MarketInfoQuery {
/**
* An instrument that is trading on the VEGA network
*/
market: MarketInfoQuery_market | null;
}
export interface MarketInfoQueryVariables {
marketId: string;
}

View File

@ -1,108 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
// ====================================================
// GraphQL subscription operation: OrderEvent
// ====================================================
export interface OrderEvent_busEvents_event_TimeUpdate {
__typename: "TimeUpdate" | "MarketEvent" | "TransferResponses" | "PositionResolution" | "Trade" | "Account" | "Party" | "MarginLevels" | "Proposal" | "Vote" | "MarketData" | "NodeSignature" | "LossSocialization" | "SettlePosition" | "Market" | "Asset" | "MarketTick" | "SettleDistressed" | "AuctionEvent" | "RiskFactor" | "Deposit" | "Withdrawal" | "OracleSpec" | "LiquidityProvision";
}
export interface OrderEvent_busEvents_event_Order_market {
__typename: "Market";
/**
* 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;
}
export interface OrderEvent_busEvents_event_Order {
__typename: "Order";
/**
* Type the order type (defaults to PARTY)
*/
type: OrderType | null;
/**
* Hash of the order data
*/
id: string;
/**
* The status of an order, for example 'Active'
*/
status: OrderStatus;
/**
* Reason for the order to be rejected
*/
rejectionReason: OrderRejectionReason | null;
/**
* RFC3339Nano formatted date and time for when the order was created (timestamp)
*/
createdAt: string;
/**
* Total number of contracts that may be bought or sold (immutable) (uint64)
*/
size: string;
/**
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
*/
price: string;
/**
* The market the order is trading on (probably stored internally as a hash of the market details)
*/
market: OrderEvent_busEvents_event_Order_market | null;
}
export type OrderEvent_busEvents_event = OrderEvent_busEvents_event_TimeUpdate | OrderEvent_busEvents_event_Order;
export interface OrderEvent_busEvents {
__typename: "BusEvent";
/**
* the id for this event
*/
eventId: string;
/**
* the block hash
*/
block: string;
/**
* the type of event we're dealing with
*/
type: BusEventType;
/**
* the payload - the wrapped event
*/
event: OrderEvent_busEvents_event;
}
export interface OrderEvent {
/**
* Subscribe to event data from the event bus
*/
busEvents: OrderEvent_busEvents[] | null;
}
export interface OrderEventVariables {
partyId: string;
}

View File

@ -1,2 +1,2 @@
export * from './DealTicketQuery'; export * from './DealTicketQuery';
export * from './OrderEvent'; export * from './MarketInfoQuery';

View File

@ -12,22 +12,17 @@ export interface DealTicketAmountProps {
price?: string; price?: string;
} }
const getAmountComponent = (type: OrderType) => { export const DealTicketAmount = ({
switch (type) { orderType,
...props
}: DealTicketAmountProps) => {
switch (orderType) {
case OrderType.Market: case OrderType.Market:
return DealTicketMarketAmount; return <DealTicketMarketAmount {...props} />;
case OrderType.Limit: case OrderType.Limit:
return DealTicketLimitAmount; return <DealTicketLimitAmount {...props} />;
default: { default: {
throw new Error('Invalid ticket type'); throw new Error('Invalid ticket type');
} }
} }
}; };
export const DealTicketAmount = ({
orderType,
...props
}: DealTicketAmountProps) => {
const AmountComponent = getAmountComponent(orderType);
return <AmountComponent {...props} />;
};

View File

@ -1,13 +1,7 @@
import { gql, useQuery } from '@apollo/client'; import { gql, useQuery } from '@apollo/client';
import { import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
AsyncRenderer,
GridTab,
GridTabs,
Splash,
} from '@vegaprotocol/ui-toolkit';
import { DealTicketManager } from './deal-ticket-manager'; import { DealTicketManager } from './deal-ticket-manager';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { Info } from './info-market';
import type { DealTicketQuery_market, DealTicketQuery } from './__generated__'; import type { DealTicketQuery_market, DealTicketQuery } from './__generated__';
const DEAL_TICKET_QUERY = gql` const DEAL_TICKET_QUERY = gql`
@ -19,40 +13,6 @@ const DEAL_TICKET_QUERY = gql`
positionDecimalPlaces positionDecimalPlaces
state state
tradingMode tradingMode
fees {
factors {
makerFee
infrastructureFee
liquidityFee
}
}
priceMonitoringSettings {
parameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
updateFrequencySecs
}
riskFactors {
market
short
long
}
data {
market {
id
}
markPrice
indicativeVolume
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
indicativeVolume
}
tradableInstrument { tradableInstrument {
instrument { instrument {
product { product {
@ -66,23 +26,6 @@ const DEAL_TICKET_QUERY = gql`
} }
} }
} }
riskModel {
... on LogNormalRiskModel {
tau
riskAversionParameter
params {
r
sigma
mu
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
} }
depth { depth {
lastTrade { lastTrade {
@ -111,45 +54,18 @@ export const DealTicketContainer = ({
}); });
return ( return (
<GridTabs> <AsyncRenderer<DealTicketQuery> data={data} loading={loading} error={error}>
<GridTab id="ticket" name={t('Ticket')}> {data && data.market ? (
<AsyncRenderer<DealTicketQuery> children ? (
data={data} children(data)
loading={loading} ) : (
error={error} <DealTicketManager market={data.market} />
> )
{data && data.market ? ( ) : (
children ? ( <Splash>
children(data) <p>{t('Could not load market')}</p>
) : ( </Splash>
<DealTicketManager market={data.market} /> )}
) </AsyncRenderer>
) : (
<Splash>
<p>{t('Could not load market')}</p>
</Splash>
)}
</AsyncRenderer>
</GridTab>
<GridTab id="info" name={t('Info')}>
<AsyncRenderer<DealTicketQuery>
data={data}
loading={loading}
error={error}
>
{data && data.market ? (
children ? (
children(data)
) : (
<Info market={data.market} />
)
) : (
<Splash>
<p>{t('Could not load market')}</p>
</Splash>
)}
</AsyncRenderer>
</GridTab>
</GridTabs>
); );
}; };

View File

@ -37,6 +37,10 @@ export const DealTicketManager = ({
return Intent.Danger; return Intent.Danger;
} }
if (status === VegaTxStatus.Requested) {
return Intent.Warning;
}
if (status === VegaTxStatus.Error) { if (status === VegaTxStatus.Error) {
return Intent.Danger; return Intent.Danger;
} }
@ -47,6 +51,8 @@ export const DealTicketManager = ({
useEffect(() => { useEffect(() => {
if (transaction.status !== VegaTxStatus.Default) { if (transaction.status !== VegaTxStatus.Default) {
setOrderDialogOpen(true); setOrderDialogOpen(true);
} else {
setOrderDialogOpen(false);
} }
}, [transaction.status]); }, [transaction.status]);

View File

@ -6,13 +6,14 @@ import {
import { addDecimal } from '@vegaprotocol/react-helpers'; import { addDecimal } from '@vegaprotocol/react-helpers';
import { fireEvent, render, screen, act } from '@testing-library/react'; import { fireEvent, render, screen, act } from '@testing-library/react';
import { DealTicket } from './deal-ticket'; import { DealTicket } from './deal-ticket';
import type { DealTicketQuery_market } from '../__generated__/DealTicketQuery'; import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
import type { Order } from '../utils/get-default-order'; import type { Order } from '../utils/get-default-order';
import { MarketState, MarketTradingMode } from '@vegaprotocol/types'; import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
const market: DealTicketQuery_market = { const market: DealTicketQuery_market = {
__typename: 'Market', __typename: 'Market',
id: 'market-id', id: 'market-id',
name: 'market-name',
decimalPlaces: 2, decimalPlaces: 2,
positionDecimalPlaces: 1, positionDecimalPlaces: 1,
tradingMode: MarketTradingMode.Continuous, tradingMode: MarketTradingMode.Continuous,
@ -24,6 +25,12 @@ const market: DealTicketQuery_market = {
product: { product: {
__typename: 'Future', __typename: 'Future',
quoteName: 'quote-name', quoteName: 'quote-name',
settlementAsset: {
__typename: 'Asset',
id: 'asset-id',
name: 'asset-name',
symbol: 'asset-symbol',
},
}, },
}, },
}, },

View File

@ -77,7 +77,7 @@ export const DealTicket = ({
/> />
<DealTicketAmount <DealTicketAmount
orderType={orderType} orderType={orderType}
step={0.02} step={step}
register={register} register={register}
price={ price={
market.depth.lastTrade market.depth.lastTrade
@ -109,7 +109,7 @@ export const DealTicket = ({
)} )}
<Button <Button
className="w-full mb-8" className="w-full mb-8"
variant="primary" variant="trade"
type="submit" type="submit"
disabled={isDisabled} disabled={isDisabled}
data-testid="place-order" data-testid="place-order"

View File

@ -8,18 +8,130 @@ import {
import { import {
KeyValueTable, KeyValueTable,
KeyValueTableRow, KeyValueTableRow,
AsyncRenderer,
Splash,
Accordion, Accordion,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import startCase from 'lodash/startCase'; import startCase from 'lodash/startCase';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery'; import type {
MarketInfoQuery,
MarketInfoQuery_market,
} from './__generated__/MarketInfoQuery';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { gql, useQuery } from '@apollo/client';
const MARKET_INFO_QUERY = gql`
query MarketInfoQuery($marketId: ID!) {
market(id: $marketId) {
id
name
decimalPlaces
positionDecimalPlaces
state
tradingMode
fees {
factors {
makerFee
infrastructureFee
liquidityFee
}
}
priceMonitoringSettings {
parameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
updateFrequencySecs
}
riskFactors {
market
short
long
}
data {
market {
id
}
markPrice
indicativeVolume
bestBidVolume
bestOfferVolume
bestStaticBidVolume
bestStaticOfferVolume
indicativeVolume
}
tradableInstrument {
instrument {
product {
... on Future {
quoteName
settlementAsset {
id
symbol
name
}
}
}
}
riskModel {
... on LogNormalRiskModel {
tau
riskAversionParameter
params {
r
sigma
mu
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
}
depth {
lastTrade {
price
}
}
}
}
`;
export interface InfoProps { export interface InfoProps {
market: DealTicketQuery_market; market: MarketInfoQuery_market;
} }
export interface MarketInfoContainerProps {
marketId: string;
}
export const MarketInfoContainer = ({ marketId }: MarketInfoContainerProps) => {
const { data, loading, error } = useQuery(MARKET_INFO_QUERY, {
variables: { marketId },
});
return (
<AsyncRenderer<MarketInfoQuery> data={data} loading={loading} error={error}>
{data && data.market ? (
<div className={'overflow-auto h-full'}>
<Info market={data.market} />
</div>
) : (
<Splash>
<p>{t('Could not load market')}</p>
</Splash>
)}
</AsyncRenderer>
);
};
export const Info = ({ market }: InfoProps) => { export const Info = ({ market }: InfoProps) => {
const headerClassName = const headerClassName =
'text-h5 font-bold uppercase text-black dark:text-white'; 'text-h5 font-bold uppercase text-black dark:text-white';

View File

@ -1,9 +1,13 @@
import { Icon, Loader } from '@vegaprotocol/ui-toolkit'; import { Icon, Loader } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type { OrderEvent_busEvents_event_Order } from './__generated__/OrderEvent'; import {
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers'; addDecimal,
addDecimalsFormatNumber,
t,
} from '@vegaprotocol/react-helpers';
import type { VegaTxState } from '@vegaprotocol/wallet'; import type { VegaTxState } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '@vegaprotocol/wallet'; import { VegaTxStatus } from '@vegaprotocol/wallet';
import type { OrderEvent_busEvents_event_Order } from '../hooks/__generated__/OrderEvent';
interface OrderDialogProps { interface OrderDialogProps {
transaction: VegaTxState; transaction: VegaTxState;
@ -14,9 +18,22 @@ export const OrderDialog = ({
transaction, transaction,
finalizedOrder, finalizedOrder,
}: OrderDialogProps) => { }: OrderDialogProps) => {
// TODO: When wallets support confirming transactions return UI for 'awaiting confirmation' step
// Rejected by wallet // Rejected by wallet
if (transaction.status === VegaTxStatus.Requested) {
return (
<OrderDialogWrapper
title="Confirm transaction in wallet"
icon={<Icon name="hand-up" size={20} />}
>
<p>
Please open your wallet application and confirm or reject the
transaction
</p>
</OrderDialogWrapper>
);
}
// Transaction error
if (transaction.status === VegaTxStatus.Error) { if (transaction.status === VegaTxStatus.Error) {
return ( return (
<OrderDialogWrapper <OrderDialogWrapper
@ -72,7 +89,14 @@ export const OrderDialog = ({
<p>{t(`Market: ${finalizedOrder.market.name}`)}</p> <p>{t(`Market: ${finalizedOrder.market.name}`)}</p>
)} )}
<p>{t(`Type: ${finalizedOrder.type}`)}</p> <p>{t(`Type: ${finalizedOrder.type}`)}</p>
<p>{t(`Amount: ${finalizedOrder.size}`)}</p> <p>
{t(
`Amount: ${addDecimal(
finalizedOrder.size,
finalizedOrder.market?.positionDecimalPlaces || 0
)}`
)}
</p>
{finalizedOrder.type === 'Limit' && finalizedOrder.market && ( {finalizedOrder.type === 'Limit' && finalizedOrder.market && (
<p> <p>
{t( {t(

View File

@ -36,6 +36,12 @@ export interface OrderEvent_busEvents_event_Order_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
} }
export interface OrderEvent_busEvents_event_Order { export interface OrderEvent_busEvents_event_Order {

View File

@ -4,12 +4,12 @@ import type { Order } from '../utils/get-default-order';
import { OrderType, useVegaWallet } from '@vegaprotocol/wallet'; import { OrderType, useVegaWallet } from '@vegaprotocol/wallet';
import { determineId, removeDecimal } from '@vegaprotocol/react-helpers'; import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
import { useVegaTransaction } from '@vegaprotocol/wallet'; import { useVegaTransaction } from '@vegaprotocol/wallet';
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
import type { import type {
OrderEvent, OrderEvent,
OrderEventVariables, OrderEventVariables,
OrderEvent_busEvents_event_Order, OrderEvent_busEvents_event_Order,
} from '../components/__generated__/OrderEvent'; } from './__generated__/OrderEvent';
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
const ORDER_EVENT_SUB = gql` const ORDER_EVENT_SUB = gql`
subscription OrderEvent($partyId: ID!) { subscription OrderEvent($partyId: ID!) {
@ -29,6 +29,7 @@ const ORDER_EVENT_SUB = gql`
market { market {
name name
decimalPlaces decimalPlaces
positionDecimalPlaces
} }
} }
} }

View File

@ -37,7 +37,6 @@ beforeEach(() => {
submitDeposit: jest.fn(), submitDeposit: jest.fn(),
requestFaucet: jest.fn(), requestFaucet: jest.fn(),
limits: { limits: {
min: new BigNumber(0),
max: new BigNumber(20), max: new BigNumber(20),
}, },
allowance: new BigNumber(30), allowance: new BigNumber(30),
@ -199,6 +198,6 @@ it('Deposit', async () => {
// @ts-ignore contract address definitely defined // @ts-ignore contract address definitely defined
assetSource: asset.source.contractAddress, assetSource: asset.source.contractAddress,
amount: '1500', amount: '1500',
vegaPublicKey: `0x${vegaKey}`, vegaPublicKey: vegaKey,
}); });
}); });

View File

@ -89,7 +89,7 @@ export const DepositForm = ({
submitDeposit({ submitDeposit({
assetSource: selectedAsset.source.contractAddress, assetSource: selectedAsset.source.contractAddress,
amount: removeDecimal(fields.amount, selectedAsset.decimals), amount: removeDecimal(fields.amount, selectedAsset.decimals),
vegaPublicKey: `0x${fields.to}`, vegaPublicKey: fields.to,
}); });
}; };

View File

@ -132,6 +132,12 @@ export interface MarketDepth_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* marketData for the given market * marketData for the given market
*/ */

View File

@ -55,6 +55,12 @@ export interface MarketDepthSubscription_marketDepthUpdate_market {
* Market ID * Market ID
*/ */
id: string; id: string;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* marketData for the given market * marketData for the given market
*/ */

View File

@ -1,5 +1,4 @@
import { DepthChart } from 'pennant'; import { DepthChart } from 'pennant';
import { produce } from 'immer';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { import {
@ -92,28 +91,31 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
if (!dataRef.current) { if (!dataRef.current) {
return false; return false;
} }
dataRef.current = produce(dataRef.current, (draft) => { dataRef.current = {
if (delta.buy) { ...dataRef.current,
draft.data.buy = updateLevels( midPrice: delta.market.data?.staticMidPrice
draft.data.buy,
delta.buy,
decimalPlacesRef.current
);
}
if (delta.sell) {
draft.data.sell = updateLevels(
draft.data.sell,
delta.sell,
decimalPlacesRef.current
);
}
draft.midPrice = delta.market.data?.staticMidPrice
? formatMidPrice( ? formatMidPrice(
delta.market.data?.staticMidPrice, delta.market.data?.staticMidPrice,
decimalPlacesRef.current decimalPlacesRef.current
) )
: undefined; : undefined,
}); data: {
buy: delta.buy
? updateLevels(
dataRef.current.data.buy,
delta.buy,
decimalPlacesRef.current
)
: dataRef.current.data.buy,
sell: delta.sell
? updateLevels(
dataRef.current.data.sell,
delta.sell,
decimalPlacesRef.current
)
: dataRef.current.data.sell,
},
};
setDepthDataThrottledRef.current(dataRef.current); setDepthDataThrottledRef.current(dataRef.current);
return true; return true;
}, },

View File

@ -16,6 +16,7 @@ const MARKET_DEPTH_QUERY = gql`
market(id: $marketId) { market(id: $marketId) {
id id
decimalPlaces decimalPlaces
positionDecimalPlaces
data { data {
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
@ -52,6 +53,7 @@ export const MARKET_DEPTH_SUBSCRIPTION_QUERY = gql`
marketDepthUpdate(marketId: $marketId) { marketDepthUpdate(marketId: $marketId) {
market { market {
id id
positionDecimalPlaces
data { data {
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
@ -84,27 +86,31 @@ const sequenceNumbers: Record<string, number> = {};
const update: Update< const update: Update<
MarketDepth_market, MarketDepth_market,
MarketDepthSubscription_marketDepthUpdate MarketDepthSubscription_marketDepthUpdate
> = (draft, delta, reload) => { > = (data, delta, reload) => {
if (delta.market.id !== draft.id) { if (delta.market.id !== data.id) {
return; return data;
} }
const sequenceNumber = Number(delta.sequenceNumber); const sequenceNumber = Number(delta.sequenceNumber);
if (sequenceNumber <= sequenceNumbers[delta.market.id]) { if (sequenceNumber <= sequenceNumbers[delta.market.id]) {
return; return data;
} }
/*
if (sequenceNumber - 1 !== sequenceNumbers[delta.market.id]) { if (sequenceNumber - 1 !== sequenceNumbers[delta.market.id]) {
sequenceNumbers[delta.market.id] = 0; sequenceNumbers[delta.market.id] = 0;
reload(); reload();
return; return;
} }
*/
sequenceNumbers[delta.market.id] = sequenceNumber; sequenceNumbers[delta.market.id] = sequenceNumber;
Object.assign(draft.data, delta.market.data); const updatedData = { ...data };
data.data = delta.market.data;
if (delta.buy) { if (delta.buy) {
draft.depth.buy = updateLevels(draft.depth.buy ?? [], delta.buy); updatedData.depth.buy = updateLevels(data.depth.buy ?? [], delta.buy);
} }
if (delta.sell) { if (delta.sell) {
draft.depth.sell = updateLevels(draft.depth.sell ?? [], delta.sell); updatedData.depth.sell = updateLevels(data.depth.sell ?? [], delta.sell);
} }
return updatedData;
}; };
const getData = (responseData: MarketDepth) => { const getData = (responseData: MarketDepth) => {

View File

@ -55,10 +55,8 @@ describe('compactRows', () => {
'1097': 3, '1097': 3,
'1098': 2, '1098': 2,
'1099': 1, '1099': 1,
'1100': 0,
}); });
expect(orderbookRows[orderbookRows.length - 1].bidByLevel).toEqual({ expect(orderbookRows[orderbookRows.length - 1].bidByLevel).toEqual({
'901': 0,
'902': 1, '902': 1,
'903': 2, '903': 2,
'904': 3, '904': 3,
@ -81,7 +79,7 @@ describe('compactRows', () => {
}); });
describe('updateLevels', () => { describe('updateLevels', () => {
const levels: MarketDepth_market_depth_sell[] = new Array(10) let levels: MarketDepth_market_depth_sell[] = new Array(10)
.fill(null) .fill(null)
.map((n, i) => ({ .map((n, i) => ({
__typename: 'PriceLevel', __typename: 'PriceLevel',
@ -96,9 +94,9 @@ describe('updateLevels', () => {
volume: '0', volume: '0',
numberOfOrders: '0', numberOfOrders: '0',
}; };
updateLevels(levels, [removeFirstRow]); levels = updateLevels(levels, [removeFirstRow]);
expect(levels[0].price).toEqual('20'); expect(levels[0].price).toEqual('20');
updateLevels(levels, [removeFirstRow]); levels = updateLevels(levels, [removeFirstRow]);
expect(levels[0].price).toEqual('20'); expect(levels[0].price).toEqual('20');
expect(updateLevels([], [removeFirstRow])).toEqual([]); expect(updateLevels([], [removeFirstRow])).toEqual([]);
const addFirstRow: MarketDepthSubscription_marketDepthUpdate_sell = { const addFirstRow: MarketDepthSubscription_marketDepthUpdate_sell = {
@ -107,7 +105,7 @@ describe('updateLevels', () => {
volume: '10', volume: '10',
numberOfOrders: '10', numberOfOrders: '10',
}; };
updateLevels(levels, [addFirstRow]); levels = updateLevels(levels, [addFirstRow]);
expect(levels[0].price).toEqual('10'); expect(levels[0].price).toEqual('10');
const addBeforeLastRow: MarketDepthSubscription_marketDepthUpdate_sell = { const addBeforeLastRow: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel', __typename: 'PriceLevel',
@ -115,7 +113,7 @@ describe('updateLevels', () => {
volume: '95', volume: '95',
numberOfOrders: '95', numberOfOrders: '95',
}; };
updateLevels(levels, [addBeforeLastRow]); levels = updateLevels(levels, [addBeforeLastRow]);
expect(levels[levels.length - 2].price).toEqual('95'); expect(levels[levels.length - 2].price).toEqual('95');
const addAtTheEnd: MarketDepthSubscription_marketDepthUpdate_sell = { const addAtTheEnd: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel', __typename: 'PriceLevel',
@ -123,7 +121,7 @@ describe('updateLevels', () => {
volume: '115', volume: '115',
numberOfOrders: '115', numberOfOrders: '115',
}; };
updateLevels(levels, [addAtTheEnd]); levels = updateLevels(levels, [addAtTheEnd]);
expect(levels[levels.length - 1].price).toEqual('115'); expect(levels[levels.length - 1].price).toEqual('115');
const updateLastRow: MarketDepthSubscription_marketDepthUpdate_sell = { const updateLastRow: MarketDepthSubscription_marketDepthUpdate_sell = {
__typename: 'PriceLevel', __typename: 'PriceLevel',
@ -131,7 +129,7 @@ describe('updateLevels', () => {
volume: '116', volume: '116',
numberOfOrders: '115', numberOfOrders: '115',
}; };
updateLevels(levels, [updateLastRow]); levels = updateLevels(levels, [updateLastRow]);
expect(levels[levels.length - 1]).toEqual(updateLastRow); expect(levels[levels.length - 1]).toEqual(updateLastRow);
expect(updateLevels([], [updateLastRow])).toEqual([updateLastRow]); expect(updateLevels([], [updateLastRow])).toEqual([updateLastRow]);
}); });

View File

@ -1,4 +1,3 @@
import produce from 'immer';
import groupBy from 'lodash/groupBy'; import groupBy from 'lodash/groupBy';
import { VolumeType } from '@vegaprotocol/react-helpers'; import { VolumeType } from '@vegaprotocol/react-helpers';
import { MarketTradingMode } from '@vegaprotocol/types'; import { MarketTradingMode } from '@vegaprotocol/types';
@ -31,6 +30,8 @@ export interface OrderbookRowData {
cumulativeVol: CumulativeVol; cumulativeVol: CumulativeVol;
} }
type PartialOrderbookRowData = Pick<OrderbookRowData, 'price' | 'ask' | 'bid'>;
export type OrderbookData = Partial< export type OrderbookData = Partial<
Omit<MarketDepth_market_data, '__typename' | 'market'> Omit<MarketDepth_market_data, '__typename' | 'market'>
> & { rows: OrderbookRowData[] | null }; > & { rows: OrderbookRowData[] | null };
@ -75,21 +76,31 @@ const updateRelativeData = (data: OrderbookRowData[]) => {
}); });
}; };
export const createPartialRow = (
price: string,
volume = 0,
dataType?: VolumeType
): PartialOrderbookRowData => ({
price,
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
});
export const extendRow = (row: PartialOrderbookRowData): OrderbookRowData =>
Object.assign(row, {
cumulativeVol: {
ask: 0,
bid: 0,
},
askByLevel: row.ask ? { [row.price]: row.ask } : {},
bidByLevel: row.bid ? { [row.price]: row.bid } : {},
});
export const createRow = ( export const createRow = (
price: string, price: string,
volume = 0, volume = 0,
dataType?: VolumeType dataType?: VolumeType
): OrderbookRowData => ({ ): OrderbookRowData => extendRow(createPartialRow(price, volume, dataType));
price,
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
cumulativeVol: {
ask: dataType === VolumeType.ask ? volume : 0,
bid: dataType === VolumeType.bid ? volume : 0,
},
askByLevel: dataType === VolumeType.ask ? { [price]: volume } : {},
bidByLevel: dataType === VolumeType.bid ? { [price]: volume } : {},
});
const mapRawData = const mapRawData =
(dataType: VolumeType.ask | VolumeType.bid) => (dataType: VolumeType.ask | VolumeType.bid) =>
@ -99,8 +110,8 @@ const mapRawData =
| MarketDepthSubscription_marketDepthUpdate_sell | MarketDepthSubscription_marketDepthUpdate_sell
| MarketDepth_market_depth_buy | MarketDepth_market_depth_buy
| MarketDepthSubscription_marketDepthUpdate_buy | MarketDepthSubscription_marketDepthUpdate_buy
): OrderbookRowData => ): PartialOrderbookRowData =>
createRow(data.price, Number(data.volume), dataType); createPartialRow(data.price, Number(data.volume), dataType);
/** /**
* @summary merges sell amd buy data, orders by price desc, group by price level, counts cumulative and relative values * @summary merges sell amd buy data, orders by price desc, group by price level, counts cumulative and relative values
@ -121,37 +132,38 @@ export const compactRows = (
resolution: number resolution: number
) => { ) => {
// map raw sell data to OrderbookData // map raw sell data to OrderbookData
const askOrderbookData = [...(sell ?? [])].map<OrderbookRowData>( const askOrderbookData = [...(sell ?? [])].map<PartialOrderbookRowData>(
mapRawData(VolumeType.ask) mapRawData(VolumeType.ask)
); );
// map raw buy data to OrderbookData // map raw buy data to OrderbookData
const bidOrderbookData = [...(buy ?? [])].map<OrderbookRowData>( const bidOrderbookData = [...(buy ?? [])].map<PartialOrderbookRowData>(
mapRawData(VolumeType.bid) mapRawData(VolumeType.bid)
); );
// group by price level // group by price level
const groupedByLevel = groupBy<OrderbookRowData>( const groupedByLevel = groupBy<PartialOrderbookRowData>(
[...askOrderbookData, ...bidOrderbookData], [...askOrderbookData, ...bidOrderbookData],
(row) => getPriceLevel(row.price, resolution) (row) => getPriceLevel(row.price, resolution)
); );
const orderbookData: OrderbookRowData[] = [];
// create single OrderbookData from grouped OrderbookData[], sum volumes and atore volume by level Object.keys(groupedByLevel).forEach((price) => {
const orderbookData = Object.keys(groupedByLevel).reduce<OrderbookRowData[]>( const row = extendRow(
(rows, price) => groupedByLevel[price].pop() as PartialOrderbookRowData
rows.concat( );
groupedByLevel[price].reduce<OrderbookRowData>( row.price = price;
(a, c) => ({ let subRow: PartialOrderbookRowData | undefined;
...a, // eslint-disable-next-line no-cond-assign
ask: a.ask + c.ask, while ((subRow = groupedByLevel[price].pop())) {
askByLevel: Object.assign(a.askByLevel, c.askByLevel), row.ask += subRow.ask;
bid: (a.bid ?? 0) + (c.bid ?? 0), row.bid += subRow.bid;
bidByLevel: Object.assign(a.bidByLevel, c.bidByLevel), if (subRow.ask) {
}), row.askByLevel[subRow.price] = subRow.ask;
createRow(price) }
) if (subRow.bid) {
), row.bidByLevel[subRow.price] = subRow.bid;
[] }
); }
orderbookData.push(row);
});
// order by price, it's safe to cast to number price diff should not exceed Number.MAX_SAFE_INTEGER // order by price, it's safe to cast to number price diff should not exceed Number.MAX_SAFE_INTEGER
orderbookData.sort((a, b) => Number(BigInt(b.price) - BigInt(a.price))); orderbookData.sort((a, b) => Number(BigInt(b.price) - BigInt(a.price)));
// count cumulative volumes // count cumulative volumes
@ -163,11 +175,9 @@ export const compactRows = (
(i !== 0 ? orderbookData[i - 1].cumulativeVol.bid : 0); (i !== 0 ? orderbookData[i - 1].cumulativeVol.bid : 0);
} }
for (let i = maxIndex; i >= 0; i--) { for (let i = maxIndex; i >= 0; i--) {
if (!orderbookData[i].cumulativeVol.ask) { orderbookData[i].cumulativeVol.ask =
orderbookData[i].cumulativeVol.ask = orderbookData[i].ask +
orderbookData[i].ask + (i !== maxIndex ? orderbookData[i + 1].cumulativeVol.ask : 0);
(i !== maxIndex ? orderbookData[i + 1].cumulativeVol.ask : 0);
}
} }
} }
// count relative volumes // count relative volumes
@ -186,13 +196,13 @@ export const compactRows = (
*/ */
const partiallyUpdateCompactedRows = ( const partiallyUpdateCompactedRows = (
dataType: VolumeType, dataType: VolumeType,
draft: OrderbookRowData[], data: OrderbookRowData[],
delta: delta:
| MarketDepthSubscription_marketDepthUpdate_sell | MarketDepthSubscription_marketDepthUpdate_sell
| MarketDepthSubscription_marketDepthUpdate_buy, | MarketDepthSubscription_marketDepthUpdate_buy,
resolution: number, resolution: number,
modifiedIndex: number modifiedIndex: number
) => { ): [number, OrderbookRowData[]] => {
const { price } = delta; const { price } = delta;
const volume = Number(delta.volume); const volume = Number(delta.volume);
const priceLevel = getPriceLevel(price, resolution); const priceLevel = getPriceLevel(price, resolution);
@ -201,28 +211,36 @@ const partiallyUpdateCompactedRows = (
const oppositeVolKey = isAskDataType ? 'bid' : 'ask'; const oppositeVolKey = isAskDataType ? 'bid' : 'ask';
const volByLevelKey = isAskDataType ? 'askByLevel' : 'bidByLevel'; const volByLevelKey = isAskDataType ? 'askByLevel' : 'bidByLevel';
const resolveModifiedIndex = isAskDataType ? Math.max : Math.min; const resolveModifiedIndex = isAskDataType ? Math.max : Math.min;
let index = draft.findIndex((data) => data.price === priceLevel); let index = data.findIndex((row) => row.price === priceLevel);
if (index !== -1) { if (index !== -1) {
modifiedIndex = resolveModifiedIndex(modifiedIndex, index); modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
draft[index][volKey] = data[index] = {
draft[index][volKey] - (draft[index][volByLevelKey][price] || 0) + volume; ...data[index],
draft[index][volByLevelKey][price] = volume; [volKey]:
data[index][volKey] - (data[index][volByLevelKey][price] || 0) + volume,
[volByLevelKey]: {
...data[index][volByLevelKey],
[price]: volume,
},
};
return [modifiedIndex, [...data]];
} else { } else {
const newData: OrderbookRowData = createRow(priceLevel, volume, dataType); const newData: OrderbookRowData = createRow(priceLevel, volume, dataType);
index = draft.findIndex((data) => BigInt(data.price) < BigInt(priceLevel)); index = data.findIndex((row) => BigInt(row.price) < BigInt(priceLevel));
if (index !== -1) { if (index !== -1) {
draft.splice(index, 0, newData);
newData.cumulativeVol[oppositeVolKey] = newData.cumulativeVol[oppositeVolKey] =
draft[index + (isAskDataType ? -1 : 1)]?.cumulativeVol[ data[index + (isAskDataType ? 0 : 1)]?.cumulativeVol[oppositeVolKey] ??
oppositeVolKey 0;
] ?? 0;
modifiedIndex = resolveModifiedIndex(modifiedIndex, index); modifiedIndex = resolveModifiedIndex(modifiedIndex, index);
return [
modifiedIndex,
[...data.slice(0, index), newData, ...data.slice(index)],
];
} else { } else {
draft.push(newData); modifiedIndex = data.length - 1;
modifiedIndex = draft.length - 1; return [modifiedIndex, [...data, newData]];
} }
} }
return modifiedIndex;
}; };
/** /**
@ -239,59 +257,66 @@ export const updateCompactedRows = (
sell: MarketDepthSubscription_marketDepthUpdate_sell[] | null, sell: MarketDepthSubscription_marketDepthUpdate_sell[] | null,
buy: MarketDepthSubscription_marketDepthUpdate_buy[] | null, buy: MarketDepthSubscription_marketDepthUpdate_buy[] | null,
resolution: number resolution: number
) => ) => {
produce(rows, (draft) => { let sellModifiedIndex = -1;
let sellModifiedIndex = -1; let data = [...rows];
sell?.forEach((delta) => { sell?.forEach((delta) => {
sellModifiedIndex = partiallyUpdateCompactedRows( [sellModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.ask, VolumeType.ask,
draft, data,
delta, delta,
resolution, resolution,
sellModifiedIndex sellModifiedIndex
); );
});
let buyModifiedIndex = draft.length;
buy?.forEach((delta) => {
buyModifiedIndex = partiallyUpdateCompactedRows(
VolumeType.bid,
draft,
delta,
resolution,
buyModifiedIndex
);
});
// update cummulative ask only below hihgest modified price level
if (sellModifiedIndex !== -1) {
for (let i = Math.min(sellModifiedIndex, draft.length - 2); i >= 0; i--) {
draft[i].cumulativeVol.ask =
draft[i + 1].cumulativeVol.ask + draft[i].ask;
}
}
// update cummulative bid only above lowest modified price level
if (buyModifiedIndex !== draft.length) {
for (
let i = Math.max(buyModifiedIndex, 1), l = draft.length;
i < l;
i++
) {
draft[i].cumulativeVol.bid =
draft[i - 1].cumulativeVol.bid + draft[i].bid;
}
}
let index = 0;
// remove levels that do not have any volume
while (index < draft.length) {
if (!draft[index].ask && !draft[index].bid) {
draft.splice(index, 1);
} else {
index += 1;
}
}
// count relative volumes
updateRelativeData(draft);
}); });
let buyModifiedIndex = data.length;
buy?.forEach((delta) => {
[buyModifiedIndex, data] = partiallyUpdateCompactedRows(
VolumeType.bid,
data,
delta,
resolution,
buyModifiedIndex
);
});
// update cummulative ask only below hihgest modified price level
if (sellModifiedIndex !== -1) {
for (let i = Math.min(sellModifiedIndex, data.length - 2); i >= 0; i--) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
ask: data[i + 1].cumulativeVol.ask + data[i].ask,
},
};
}
}
// update cummulative bid only above lowest modified price level
if (buyModifiedIndex !== data.length) {
for (let i = Math.max(buyModifiedIndex, 1), l = data.length; i < l; i++) {
data[i] = {
...data[i],
cumulativeVol: {
...data[i].cumulativeVol,
bid: data[i - 1].cumulativeVol.bid + data[i].bid,
},
};
}
}
let index = 0;
// remove levels that do not have any volume
while (index < data.length) {
if (!data[index].ask && !data[index].bid) {
data.splice(index, 1);
} else {
index += 1;
}
}
// count relative volumes
updateRelativeData(data);
return data;
};
export const mapMarketData = ( export const mapMarketData = (
data: data:
@ -319,23 +344,24 @@ export const mapMarketData = (
* @returns * @returns
*/ */
export const updateLevels = ( export const updateLevels = (
levels: (MarketDepth_market_depth_buy | MarketDepth_market_depth_sell)[], draft: (MarketDepth_market_depth_buy | MarketDepth_market_depth_sell)[],
updates: ( updates: (
| MarketDepthSubscription_marketDepthUpdate_buy | MarketDepthSubscription_marketDepthUpdate_buy
| MarketDepthSubscription_marketDepthUpdate_sell | MarketDepthSubscription_marketDepthUpdate_sell
)[] )[]
) => { ) => {
const levels = [...draft];
updates.forEach((update) => { updates.forEach((update) => {
let index = levels.findIndex((level) => level.price === update.price); let index = levels.findIndex((level) => level.price === update.price);
if (index !== -1) { if (index !== -1) {
if (update.volume === '0') { if (update.volume === '0') {
levels.splice(index, 1); levels.splice(index, 1);
} else { } else {
Object.assign(levels[index], update); levels[index] = update;
} }
} else if (update.volume !== '0') { } else if (update.volume !== '0') {
index = levels.findIndex( index = levels.findIndex(
(level) => Number(level.price) > Number(update.price) (level) => BigInt(level.price) > BigInt(update.price)
); );
if (index !== -1) { if (index !== -1) {
levels.splice(index, 0, update); levels.splice(index, 0, update);

View File

@ -1,5 +1,4 @@
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import produce from 'immer';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Orderbook } from './orderbook'; import { Orderbook } from './orderbook';
import { useDataProvider } from '@vegaprotocol/react-helpers'; import { useDataProvider } from '@vegaprotocol/react-helpers';
@ -25,27 +24,52 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
rows: null, rows: null,
}); });
const dataRef = useRef<OrderbookData>({ rows: null }); const dataRef = useRef<OrderbookData>({ rows: null });
const setOrderbookDataThrottled = useRef(throttle(setOrderbookData, 1000)); const deltaRef = useRef<MarketDepthSubscription_marketDepthUpdate>();
const updateOrderbookData = useRef(
throttle(() => {
if (!deltaRef.current) {
return;
}
dataRef.current = {
...deltaRef.current.market.data,
...mapMarketData(deltaRef.current.market.data, resolutionRef.current),
rows: updateCompactedRows(
dataRef.current.rows ?? [],
deltaRef.current.sell,
deltaRef.current.buy,
resolutionRef.current
),
};
deltaRef.current = undefined;
setOrderbookData(dataRef.current);
}, 1000)
);
const update = useCallback( const update = useCallback(
(delta: MarketDepthSubscription_marketDepthUpdate) => { (delta: MarketDepthSubscription_marketDepthUpdate) => {
if (!dataRef.current.rows) { if (!dataRef.current.rows) {
return false; return false;
} }
dataRef.current = produce(dataRef.current, (draft) => { if (deltaRef.current) {
Object.assign(draft, delta.market.data); deltaRef.current.market = delta.market;
draft.rows = updateCompactedRows( if (delta.sell) {
draft.rows ?? [], if (deltaRef.current.sell) {
delta.sell, deltaRef.current.sell.push(...delta.sell);
delta.buy, } else {
resolutionRef.current deltaRef.current.sell = delta.sell;
); }
Object.assign( }
draft, if (delta.buy) {
mapMarketData(delta.market.data, resolutionRef.current) if (deltaRef.current.buy) {
); deltaRef.current.buy.push(...delta.buy);
}); } else {
setOrderbookDataThrottled.current(dataRef.current); deltaRef.current.buy = delta.buy;
}
}
} else {
deltaRef.current = delta;
}
updateOrderbookData.current();
return true; return true;
}, },
// using resolutionRef.current to avoid using resolution as a dependency - it will cause data provider restart on resolution change // using resolutionRef.current to avoid using resolution as a dependency - it will cause data provider restart on resolution change
@ -82,6 +106,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
<Orderbook <Orderbook
{...orderbookData} {...orderbookData}
decimalPlaces={data?.decimalPlaces ?? 0} decimalPlaces={data?.decimalPlaces ?? 0}
positionDecimalPlaces={data?.positionDecimalPlaces ?? 0}
resolution={resolution} resolution={resolution}
onResolutionChange={(resolution: number) => setResolution(resolution)} onResolutionChange={(resolution: number) => setResolution(resolution)}
/> />

View File

@ -5,6 +5,7 @@ import {
CumulativeVol, CumulativeVol,
addDecimalsFormatNumber, addDecimalsFormatNumber,
VolumeType, VolumeType,
addDecimal,
} from '@vegaprotocol/react-helpers'; } from '@vegaprotocol/react-helpers';
interface OrderbookRowProps { interface OrderbookRowProps {
@ -15,6 +16,7 @@ interface OrderbookRowProps {
cumulativeRelativeAsk?: number; cumulativeRelativeAsk?: number;
cumulativeRelativeBid?: number; cumulativeRelativeBid?: number;
decimalPlaces: number; decimalPlaces: number;
positionDecimalPlaces: number;
indicativeVolume?: string; indicativeVolume?: string;
price: string; price: string;
relativeAsk?: number; relativeAsk?: number;
@ -30,6 +32,7 @@ export const OrderbookRow = React.memo(
cumulativeRelativeAsk, cumulativeRelativeAsk,
cumulativeRelativeBid, cumulativeRelativeBid,
decimalPlaces, decimalPlaces,
positionDecimalPlaces,
indicativeVolume, indicativeVolume,
price, price,
relativeAsk, relativeAsk,
@ -40,6 +43,7 @@ export const OrderbookRow = React.memo(
<Vol <Vol
testId={`bid-vol-${price}`} testId={`bid-vol-${price}`}
value={bid} value={bid}
valueFormatted={addDecimal(bid, positionDecimalPlaces)}
relativeValue={relativeBid} relativeValue={relativeBid}
type={VolumeType.bid} type={VolumeType.bid}
/> />
@ -51,6 +55,7 @@ export const OrderbookRow = React.memo(
<Vol <Vol
testId={`ask-vol-${price}`} testId={`ask-vol-${price}`}
value={ask} value={ask}
valueFormatted={addDecimal(ask, positionDecimalPlaces)}
relativeValue={relativeAsk} relativeValue={relativeAsk}
type={VolumeType.ask} type={VolumeType.ask}
/> />

View File

@ -22,6 +22,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -35,6 +36,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -44,6 +46,7 @@ describe('Orderbook', () => {
result.rerender( result.rerender(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData({ {...generateMockData({
...params, ...params,
numberOfSellRows: params.numberOfSellRows - 1, numberOfSellRows: params.numberOfSellRows - 1,
@ -60,6 +63,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -69,6 +73,7 @@ describe('Orderbook', () => {
result.rerender( result.rerender(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData({ {...generateMockData({
...params, ...params,
bestStaticBidPrice: params.bestStaticBidPrice + 1, bestStaticBidPrice: params.bestStaticBidPrice + 1,
@ -86,6 +91,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -98,6 +104,7 @@ describe('Orderbook', () => {
result.rerender( result.rerender(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData({ {...generateMockData({
...params, ...params,
numberOfSellRows: params.numberOfSellRows - 1, numberOfSellRows: params.numberOfSellRows - 1,
@ -114,6 +121,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -134,6 +142,7 @@ describe('Orderbook', () => {
const result = render( const result = render(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData(params)} {...generateMockData(params)}
onResolutionChange={onResolutionChange} onResolutionChange={onResolutionChange}
/> />
@ -153,6 +162,7 @@ describe('Orderbook', () => {
result.rerender( result.rerender(
<Orderbook <Orderbook
decimalPlaces={decimalPlaces} decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
{...generateMockData({ {...generateMockData({
...params, ...params,
resolution: 10, resolution: 10,

View File

@ -19,6 +19,7 @@ import { Icon, Splash } from '@vegaprotocol/ui-toolkit';
import type { OrderbookData, OrderbookRowData } from './orderbook-data'; import type { OrderbookData, OrderbookRowData } from './orderbook-data';
interface OrderbookProps extends OrderbookData { interface OrderbookProps extends OrderbookData {
decimalPlaces: number; decimalPlaces: number;
positionDecimalPlaces: number;
resolution: number; resolution: number;
onResolutionChange: (resolution: number) => void; onResolutionChange: (resolution: number) => void;
} }
@ -100,6 +101,7 @@ export const Orderbook = ({
indicativeVolume, indicativeVolume,
indicativePrice, indicativePrice,
decimalPlaces, decimalPlaces,
positionDecimalPlaces,
resolution, resolution,
onResolutionChange, onResolutionChange,
}: OrderbookProps) => { }: OrderbookProps) => {
@ -174,7 +176,10 @@ export const Orderbook = ({
// adjust to current rows position // adjust to current rows position
scrollTop += scrollTop +=
(scrollTopRef.current % rowHeight) - (scrollTop % rowHeight); (scrollTopRef.current % rowHeight) - (scrollTop % rowHeight);
const priceCenterScrollOffset = Math.max(0, Math.min(scrollTop)); const priceCenterScrollOffset = Math.max(
0,
Math.min(scrollTop, numberOfRows * rowHeight - viewportHeight)
);
if (scrollTopRef.current !== priceCenterScrollOffset) { if (scrollTopRef.current !== priceCenterScrollOffset) {
updateScrollOffset(priceCenterScrollOffset); updateScrollOffset(priceCenterScrollOffset);
scrollTopRef.current = priceCenterScrollOffset; scrollTopRef.current = priceCenterScrollOffset;
@ -182,7 +187,13 @@ export const Orderbook = ({
} }
} }
}, },
[maxPriceLevel, resolution, viewportHeight, updateScrollOffset] [
maxPriceLevel,
resolution,
viewportHeight,
numberOfRows,
updateScrollOffset,
]
); );
useEffect(() => { useEffect(() => {
@ -197,23 +208,36 @@ export const Orderbook = ({
return; return;
} }
priceInCenter.current = undefined; priceInCenter.current = undefined;
setLockOnMidPrice(true); let midPrice = getPriceLevel(
scrollToPrice( BigInt(bestStaticOfferPrice) +
getPriceLevel( (BigInt(bestStaticBidPrice) - BigInt(bestStaticOfferPrice)) / BigInt(2),
BigInt(bestStaticOfferPrice) + resolution
(BigInt(bestStaticBidPrice) - BigInt(bestStaticOfferPrice)) /
BigInt(2),
resolution
)
); );
}, [bestStaticOfferPrice, bestStaticBidPrice, scrollToPrice, resolution]); if (BigInt(midPrice) > BigInt(maxPriceLevel)) {
midPrice = maxPriceLevel;
} else {
const minPriceLevel =
BigInt(maxPriceLevel) - BigInt(Math.floor(numberOfRows * resolution));
if (BigInt(midPrice) < minPriceLevel) {
midPrice = minPriceLevel.toString();
}
}
scrollToPrice(midPrice);
setLockOnMidPrice(true);
}, [
bestStaticOfferPrice,
bestStaticBidPrice,
scrollToPrice,
resolution,
maxPriceLevel,
numberOfRows,
]);
// adjust scroll position to keep selected price in center // adjust scroll position to keep selected price in center
useLayoutEffect(() => { useLayoutEffect(() => {
if (resolutionRef.current !== resolution) { if (resolutionRef.current !== resolution) {
priceInCenter.current = undefined; priceInCenter.current = undefined;
resolutionRef.current = resolution; resolutionRef.current = resolution;
setLockOnMidPrice(true);
} }
if (priceInCenter.current) { if (priceInCenter.current) {
scrollToPrice(priceInCenter.current); scrollToPrice(priceInCenter.current);
@ -236,21 +260,19 @@ export const Orderbook = ({
return () => window.removeEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize);
}, []); }, []);
const renderedRows = useMemo(() => { let offset = Math.max(0, Math.round(scrollOffset / rowHeight));
let offset = Math.max(0, Math.round(scrollOffset / rowHeight)); const prependingBufferSize = Math.min(bufferSize, offset);
const prependingBufferSize = Math.min(bufferSize, offset); offset -= prependingBufferSize;
offset -= prependingBufferSize; const viewportSize = Math.round(viewportHeight / rowHeight);
const viewportSize = Math.round(viewportHeight / rowHeight); const limit = Math.min(
const limit = Math.min( prependingBufferSize + viewportSize + bufferSize,
prependingBufferSize + viewportSize + bufferSize, numberOfRows - offset
numberOfRows - offset );
); const renderedRows = {
return { offset,
offset, limit,
limit, data: getRowsToRender(rows, resolution, offset, limit),
data: getRowsToRender(rows, resolution, offset, limit), };
};
}, [rows, scrollOffset, resolution, viewportHeight, numberOfRows]);
const paddingTop = renderedRows.offset * rowHeight; const paddingTop = renderedRows.offset * rowHeight;
const paddingBottom = const paddingBottom =
@ -298,6 +320,7 @@ export const Orderbook = ({
<OrderbookRow <OrderbookRow
price={(BigInt(data.price) / BigInt(resolution)).toString()} price={(BigInt(data.price) / BigInt(resolution)).toString()}
decimalPlaces={decimalPlaces - Math.log10(resolution)} decimalPlaces={decimalPlaces - Math.log10(resolution)}
positionDecimalPlaces={positionDecimalPlaces}
bid={data.bid} bid={data.bid}
relativeBid={data.relativeBid} relativeBid={data.relativeBid}
cumulativeBid={data.cumulativeVol.bid} cumulativeBid={data.cumulativeVol.bid}

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import type { import type {
Markets, Markets,
@ -90,13 +91,14 @@ const MARKET_DATA_SUB = gql`
} }
`; `;
const update = (draft: Markets_markets[], delta: MarketDataSub_marketData) => { const update = (data: Markets_markets[], delta: MarketDataSub_marketData) =>
const index = draft.findIndex((m) => m.id === delta.market.id); produce(data, (draft) => {
if (index !== -1) { const index = draft.findIndex((m) => m.id === delta.market.id);
draft[index].data = delta; if (index !== -1) {
} draft[index].data = delta;
// @TODO - else push new market to draft }
}; // @TODO - else push new market to draft
});
const getData = (responseData: Markets): Markets_markets[] | null => const getData = (responseData: Markets): Markets_markets[] | null =>
responseData.markets; responseData.markets;
const getDelta = (subscriptionData: MarketDataSub): MarketDataSub_marketData => const getDelta = (subscriptionData: MarketDataSub): MarketDataSub_marketData =>

View File

@ -52,6 +52,12 @@ export interface OrderFields_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -52,6 +52,12 @@ export interface OrderSub_orders_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -52,6 +52,12 @@ export interface Orders_party_orders_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -25,6 +25,7 @@ const marketOrder: Orders_party_orders = {
id: 'market-id', id: 'market-id',
name: 'market-name', name: 'market-name',
decimalPlaces: 2, decimalPlaces: 2,
positionDecimalPlaces: 0,
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
@ -54,6 +55,7 @@ const limitOrder: Orders_party_orders = {
id: 'market-id', id: 'market-id',
name: 'market-name', name: 'market-name',
decimalPlaces: 2, decimalPlaces: 2,
positionDecimalPlaces: 2,
tradableInstrument: { tradableInstrument: {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
@ -62,11 +64,11 @@ const limitOrder: Orders_party_orders = {
}, },
}, },
}, },
size: '10', size: '1000',
type: OrderType.Limit, type: OrderType.Limit,
status: OrderStatus.Active, status: OrderStatus.Active,
side: Side.Sell, side: Side.Sell,
remaining: '5', remaining: '500',
price: '12345', price: '12345',
timeInForce: OrderTimeInForce.GTT, timeInForce: OrderTimeInForce.GTT,
createdAt: new Date('2022-3-3').toISOString(), createdAt: new Date('2022-3-3').toISOString(),
@ -122,10 +124,10 @@ it('Correct formatting applied for GTT limit order', async () => {
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
limitOrder.market?.tradableInstrument.instrument.code, limitOrder.market?.tradableInstrument.instrument.code,
'-10', '-10.00',
limitOrder.type, limitOrder.type,
limitOrder.status, limitOrder.status,
'5', '5.00',
formatNumber(limitOrder.price, limitOrder.market?.decimalPlaces ?? 0), formatNumber(limitOrder.price, limitOrder.market?.decimalPlaces ?? 0),
`${limitOrder.timeInForce}: ${getDateTimeFormat().format( `${limitOrder.timeInForce}: ${getDateTimeFormat().format(
new Date(limitOrder.expiresAt ?? '') new Date(limitOrder.expiresAt ?? '')

View File

@ -1,11 +1,17 @@
import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types'; import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types';
import type { Orders_party_orders } from './__generated__/Orders'; import type { Orders_party_orders } from './__generated__/Orders';
import { formatNumber, getDateTimeFormat } from '@vegaprotocol/react-helpers'; import {
addDecimal,
formatNumber,
getDateTimeFormat,
t,
} from '@vegaprotocol/react-helpers';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit'; import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import type { ValueFormatterParams } from 'ag-grid-community'; import type { ValueFormatterParams } from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react'; import { AgGridColumn } from 'ag-grid-react';
import { forwardRef } from 'react'; import { forwardRef } from 'react';
import BigNumber from 'bignumber.js';
interface OrderListProps { interface OrderListProps {
data: Orders_party_orders[] | null; data: Orders_party_orders[] | null;
@ -23,16 +29,18 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
getRowId={({ data }) => data.id} getRowId={({ data }) => data.id}
> >
<AgGridColumn <AgGridColumn
headerName="Market" headerName={t('Market')}
field="market.tradableInstrument.instrument.code" field="market.tradableInstrument.instrument.code"
/> />
<AgGridColumn <AgGridColumn
headerName="Amount" headerName={t('Amount')}
field="size" field="size"
cellClass="font-mono" cellClass="font-mono"
valueFormatter={({ value, data }: ValueFormatterParams) => { valueFormatter={({ value, data }: ValueFormatterParams) => {
const prefix = data.side === Side.Buy ? '+' : '-'; const prefix = data.side === Side.Buy ? '+' : '-';
return prefix + value; return (
prefix + addDecimal(value, data.market.positionDecimalPlaces)
);
}} }}
/> />
<AgGridColumn field="type" /> <AgGridColumn field="type" />
@ -47,11 +55,18 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
}} }}
/> />
<AgGridColumn <AgGridColumn
headerName="Filled" headerName={t('Filled')}
field="remaining" field="remaining"
cellClass="font-mono" cellClass="font-mono"
valueFormatter={({ data }: ValueFormatterParams) => { valueFormatter={({ data }: ValueFormatterParams) => {
return `${Number(data.size) - Number(data.remaining)}/${data.size}`; const dps = data.market.positionDecimalPlaces;
const size = new BigNumber(data.size);
const remaining = new BigNumber(data.remaining);
const fills = size.minus(remaining);
return `${addDecimal(fills.toString(), dps)}/${addDecimal(
size.toString(),
dps
)}`;
}} }}
/> />
<AgGridColumn <AgGridColumn

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import { makeDataProvider } from '@vegaprotocol/react-helpers'; import { makeDataProvider } from '@vegaprotocol/react-helpers';
import type { OrderFields } from './__generated__/OrderFields'; import type { OrderFields } from './__generated__/OrderFields';
@ -13,6 +14,7 @@ const ORDER_FRAGMENT = gql`
id id
name name
decimalPlaces decimalPlaces
positionDecimalPlaces
tradableInstrument { tradableInstrument {
instrument { instrument {
code code
@ -77,19 +79,20 @@ export const prepareIncomingOrders = (delta: OrderFields[]) => {
return incoming; return incoming;
}; };
const update = (draft: OrderFields[], delta: OrderFields[]) => { const update = (data: OrderFields[], delta: OrderFields[]) =>
const incoming = prepareIncomingOrders(delta); produce(data, (draft) => {
const incoming = prepareIncomingOrders(delta);
// Add or update incoming orders // Add or update incoming orders
incoming.forEach((order) => { incoming.forEach((order) => {
const index = draft.findIndex((o) => o.id === order.id); const index = draft.findIndex((o) => o.id === order.id);
if (index === -1) { if (index === -1) {
draft.unshift(order); draft.unshift(order);
} else { } else {
draft[index] = order; draft[index] = order;
} }
});
}); });
};
const getData = (responseData: Orders): Orders_party_orders[] | null => const getData = (responseData: Orders): Orders_party_orders[] | null =>
responseData?.party?.orders || null; responseData?.party?.orders || null;

View File

@ -136,6 +136,12 @@ export interface PositionDetails_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -136,6 +136,12 @@ export interface PositionSubscribe_positions_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -136,6 +136,12 @@ export interface Positions_party_positions_market {
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p) * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/ */
decimalPlaces: number; decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
/** /**
* An instance of or reference to a tradable instrument. * An instance of or reference to a tradable instrument.
*/ */

View File

@ -1,3 +1,4 @@
import produce from 'immer';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import type { import type {
Positions, Positions,
@ -27,6 +28,7 @@ const POSITIONS_FRAGMENT = gql`
} }
} }
decimalPlaces decimalPlaces
positionDecimalPlaces
tradableInstrument { tradableInstrument {
instrument { instrument {
id id
@ -74,16 +76,17 @@ export const POSITIONS_SUB = gql`
`; `;
const update = ( const update = (
draft: Positions_party_positions[], data: Positions_party_positions[],
delta: PositionSubscribe_positions delta: PositionSubscribe_positions
) => { ) =>
const index = draft.findIndex((m) => m.market.id === delta.market.id); produce(data, (draft) => {
if (index !== -1) { const index = draft.findIndex((m) => m.market.id === delta.market.id);
draft[index] = delta; if (index !== -1) {
} else { draft[index] = delta;
draft.push(delta); } else {
} draft.push(delta);
}; }
});
const getData = (responseData: Positions): Positions_party_positions[] | null => const getData = (responseData: Positions): Positions_party_positions[] | null =>
responseData.party ? responseData.party.positions : null; responseData.party ? responseData.party.positions : null;
const getDelta = ( const getDelta = (

View File

@ -5,7 +5,7 @@ import { MarketTradingMode } from '@vegaprotocol/types';
const singleRow: Positions_party_positions = { const singleRow: Positions_party_positions = {
realisedPNL: '520000000', realisedPNL: '520000000',
openVolume: '100', openVolume: '10000',
unrealisedPNL: '895000', unrealisedPNL: '895000',
averageEntryPrice: '1129935', averageEntryPrice: '1129935',
market: { market: {
@ -17,6 +17,7 @@ const singleRow: Positions_party_positions = {
__typename: 'MarketData', __typename: 'MarketData',
market: { __typename: 'Market', id: '123' }, market: { __typename: 'Market', id: '123' },
}, },
positionDecimalPlaces: 2,
decimalPlaces: 5, decimalPlaces: 5,
tradableInstrument: { tradableInstrument: {
instrument: { instrument: {
@ -90,7 +91,7 @@ it('Correct formatting applied', async () => {
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
singleRow.market.tradableInstrument.instrument.code, singleRow.market.tradableInstrument.instrument.code,
'+100', '+100.00',
'11.29935', '11.29935',
'11.38885', '11.38885',
'+5,200.000', '+5,200.000',

View File

@ -88,8 +88,11 @@ export const PositionsTable = forwardRef<AgGridReact, PositionsTableProps>(
<AgGridColumn <AgGridColumn
headerName={t('Amount')} headerName={t('Amount')}
field="openVolume" field="openVolume"
valueFormatter={({ value }: PositionsTableValueFormatterParams) => valueFormatter={({
volumePrefix(value) value,
data,
}: PositionsTableValueFormatterParams) =>
volumePrefix(addDecimal(value, data.market.positionDecimalPlaces))
} }
/> />
<AgGridColumn <AgGridColumn

View File

@ -1,19 +1,6 @@
import once from 'lodash/once'; import once from 'lodash/once';
import { getUserLocale } from './utils'; import { getUserLocale } from './utils';
/**
* Returns a number prefixed with either a '-' or a '+'. The open volume field
* already comes with a '-' if negative so we only need to actually prefix if
* its a positive value
*/
export function volumePrefix(value: string): string {
if (value === '0' || value.startsWith('-')) {
return value;
}
return '+' + value;
}
export const getTimeFormat = once( export const getTimeFormat = once(
() => () =>
new Intl.DateTimeFormat(getUserLocale(), { new Intl.DateTimeFormat(getUserLocale(), {

Some files were not shown because too many files have changed in this diff Show More