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

This commit is contained in:
madalinaraicu 2022-06-13 12:32:44 +01:00
commit f51681cb86
369 changed files with 8076 additions and 5323 deletions

View File

@ -28,3 +28,9 @@ Feature: Blocks Page
And jump to first block
Then previous button is disabled
And I am on the second block when I click next
Scenario: Infinite scroll shows at least 300 new blocks
Given I am on the homepage
When I navigate to the blocks page
And I scroll down to the last block on the page
Then I can expect to see at least 100 blocks if i scroll 7 times

View File

@ -14,10 +14,10 @@ export default class BlocksPage extends BasePage {
nextBlockBtn = 'next-block';
jumpToBlockInput = 'block-input';
jumpToBlockSubmit = 'go-submit';
infiniteScrollWrapper = 'infinite-scroll-wrapper';
private waitForBlocksResponse() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
}
validateBlocksPageDisplayed() {
@ -37,8 +37,6 @@ export default class BlocksPage extends BasePage {
validateBlockValidatorPage() {
cy.getByTestId(this.minedByValidator).should('not.be.empty');
cy.getByTestId(this.blockTime).should('not.be.empty');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.transactionsRow}] > td`).length) {
cy.get(`[data-testid=${this.transactionsRow}] > td`).each(($cell) => {
@ -85,6 +83,50 @@ export default class BlocksPage extends BasePage {
});
}
navigateToLastBlockOnPage() {
this.waitForBlocksResponse();
cy.getByTestId(this.infiniteScrollWrapper).children().scrollTo('bottom');
}
navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks: number,
scrollAttempts: number
) {
cy.intercept('*blockchain?maxHeight*').as('blockchain_load');
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($initialLastBlockHeight) => {
for (let index = 0; index < scrollAttempts; index++) {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.children()
.invoke('css', 'height')
.then((scrollTarget) => {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.scrollTo(0, scrollTarget.toString(), { easing: 'linear' })
.wait('@blockchain_load');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50); // Need this as although network response has arrived it takes a few millisecs for the css height to expand
});
}
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($lastBlockHeight) => {
const totalBlocksLoadedSinceScrollBegan =
parseInt($initialLastBlockHeight) - parseInt($lastBlockHeight);
expect(totalBlocksLoadedSinceScrollBegan).to.be.at.least(
expectedBlocks
);
});
});
}
clickPreviousBlock() {
cy.getByTestId(this.previousBlockBtn).click();
}

View File

@ -34,3 +34,17 @@ Then('previous button is disabled', () => {
Then('I am on the second block when I click next', () => {
blocksPage.navigateToNextBlock();
});
Then('I scroll down to the last block on the page', () => {
blocksPage.navigateToLastBlockOnPage();
});
Then(
'I can expect to see at least {int} blocks if i scroll {int} times',
(expectedBlocks, scrollAttempts) => {
blocksPage.navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks,
scrollAttempts
);
}
);

View File

@ -18,12 +18,13 @@ NX_URL=$URL
NX_DEPLOY_URL=$DEPLOY_URL
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-explorer-api"
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
NX_CHAIN_EXPLORER_URL = 'https://explorer.vega.trading/.netlify/functions/chain-explorer-api'
NX_TENDERMINT_URL = 'https://lb.testnet.vega.xyz/tm'
NX_TENDERMINT_WEBSOCKET_URL = 'wss://lb.testnet.vega.xyz/tm/websocket'
NX_VEGA_URL = 'https://lb.testnet.vega.xyz/query'
NX_VEGA_ENV = 'TESTNET'
NX_VEGA_REST = 'https://lb.testnet.vega.xyz/datanode/rest'
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
CYPRESS_VEGA_TENDERMINT_URL='https://lb.testnet.vega.xyz/tm'
# App flags

View File

@ -3,6 +3,7 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "http://localhost:26617"
NX_TENDERMINT_WEBSOCKET_URL = "wss://localhost:26617/websocket"
NX_VEGA_URL = "http://localhost:3028/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'LOCAL'
NX_VEGA_REST = 'http://localhost:3029'

View File

@ -3,5 +3,6 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "https://n04.d.vega.xyz/tm"
NX_TENDERMINT_WEBSOCKET_URL = "wss://n04.d.vega.xyz/tm/websocket"
NX_VEGA_URL = "https://n04.d.vega.xyz/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'DEVNET'
NX_VEGA_REST = 'https://n04.d.vega.xyz/datanode/rest'

View File

@ -3,5 +3,6 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "https://mainnet-observer-proxy01.ops.vega.xyz/"
NX_TENDERMINT_WEBSOCKET_URL = "wss://mainnet-observer-proxy01.ops.vega.xyz/websocket"
NX_VEGA_URL = "https://api.token.vega.xyz/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'MAINNET'
NX_VEGA_REST = 'https://api.token.vega.xyz/'

View File

@ -3,5 +3,6 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "https://n03.s.vega.xyz/tm"
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.s.vega.xyz/tm/websocket"
NX_VEGA_URL = "https://n03.s.vega.xyz/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'STAGNET'
NX_VEGA_REST = 'https://n03.s.vega.xyz/datanode/rest'

View File

@ -3,5 +3,6 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "https://n03.stagnet2.vega.xyz/tm"
NX_TENDERMINT_WEBSOCKET_URL = "wss://n03.stagnet2.vega.xyz/tm/websocket"
NX_VEGA_URL = "https://n03.stagnet2.vega.xyz/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'STAGNET2'
NX_VEGA_REST = 'https://n01.stagnet2.vega.xyz/datanode/rest'

View File

@ -3,5 +3,6 @@ NX_CHAIN_EXPLORER_URL = "https://explorer.vega.trading/.netlify/functions/chain-
NX_TENDERMINT_URL = "https://lb.testnet.vega.xyz/tm"
NX_TENDERMINT_WEBSOCKET_URL = "wss://lb.testnet.vega.xyz/tm/websocket"
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
NX_VEGA_NETWORKS = '{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV = 'TESTNET'
NX_VEGA_REST = 'https://lb.testnet.vega.xyz/datanode/rest'

View File

@ -1,11 +1,8 @@
import { useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { ApolloProvider } from '@apollo/client';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import {
useThemeSwitcher,
EnvironmentProvider,
} from '@vegaprotocol/react-helpers';
import { ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
import { createClient } from './lib/apollo-client';
import { Nav } from './components/nav';
import { Header } from './components/header';

View File

@ -6,6 +6,7 @@ const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom
module.exports = {
content: [
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
'libs/ui-toolkit/src/utils/shared.ts',
...createGlobPatternsForDependencies(__dirname),
],
darkMode: 'class',

View File

@ -9,7 +9,7 @@ import {
VegaManageDialog,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
import { Connectors } from './lib/vega-connectors';

View File

@ -0,0 +1,59 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: PartyBalanceQuery
// ====================================================
export interface PartyBalanceQuery_party_accounts_asset {
__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;
/**
* The precision of the asset
*/
decimals: number;
}
export interface PartyBalanceQuery_party_accounts {
__typename: "Account";
/**
* Balance as string - current account balance (approx. as balances can be updated several times per second)
*/
balance: string;
/**
* Asset, the 'currency'
*/
asset: PartyBalanceQuery_party_accounts_asset;
}
export interface PartyBalanceQuery_party {
__typename: "Party";
/**
* Collateral accounts relating to a party
*/
accounts: PartyBalanceQuery_party_accounts[] | null;
}
export interface PartyBalanceQuery {
/**
* An entity that is trading on the VEGA network
*/
party: PartyBalanceQuery_party | null;
}
export interface PartyBalanceQueryVariables {
partyId: string;
}

View File

@ -0,0 +1,55 @@
import * as React from 'react';
import type { DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset } from '@vegaprotocol/deal-ticket';
import type { PartyBalanceQuery_party_accounts } from './__generated__/PartyBalanceQuery';
import { useSettlementAccount } from '../../hooks/use-settlement-account';
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
interface DealTicketBalanceProps {
settlementAsset: DealTicketQuery_market_tradableInstrument_instrument_product_settlementAsset;
accounts: PartyBalanceQuery_party_accounts[];
isWalletConnected: boolean;
}
export const DealTicketBalance = ({
settlementAsset,
accounts,
isWalletConnected,
}: DealTicketBalanceProps) => {
const settlementAssetId = settlementAsset?.id;
const settlementAssetSymbol = settlementAsset?.symbol;
const settlementAccount = useSettlementAccount(settlementAssetId, accounts);
const formatedNumber =
settlementAccount?.balance &&
settlementAccount.asset.decimals &&
addDecimalsFormatNumber(
settlementAccount.balance,
settlementAccount.asset.decimals
);
const balance = settlementAccount ? (
<p>
{t(
`${formatedNumber} ${settlementAccount.asset.symbol} available to trade`
)}
</p>
) : (
<p>{t(`You have no ${settlementAssetSymbol} available to trade`)}</p>
);
const connectWallet = (
<p>
{t(
"Please connect your Vega wallet to see your balance for this market's settlement asset"
)}
</p>
);
return (
<div className="text-right">
<div className="border border-current p-16 inline-block">
<small className="text-text">{t('Balance')}</small>
{isWalletConnected ? balance : connectWallet}
</div>
</div>
);
};

View File

@ -4,15 +4,58 @@ import {
DealTicketContainer as Container,
} from '@vegaprotocol/deal-ticket';
import { DealTicketSteps } from './deal-ticket-steps';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { gql, useQuery } from '@apollo/client';
import { DealTicketBalance } from './deal-ticket-balance';
import * as React from 'react';
import type { PartyBalanceQuery } from './__generated__/PartyBalanceQuery';
const tempEmptyText = <p>Please select a market from the markets page</p>;
const PARTY_BALANCE_QUERY = gql`
query PartyBalanceQuery($partyId: ID!) {
party(id: $partyId) {
accounts {
balance
asset {
id
symbol
name
decimals
}
}
}
}
`;
export const DealTicketContainer = () => {
const { marketId } = useParams<{ marketId: string }>();
const { keypair } = useVegaWallet();
const { data: partyData, loading } = useQuery<PartyBalanceQuery>(
PARTY_BALANCE_QUERY,
{
variables: { partyId: keypair?.pub },
skip: !keypair?.pub,
}
);
return marketId ? (
<Container marketId={marketId}>
{(data) => (
<DealTicketManager market={data.market}>
{loading ? (
'Loading...'
) : (
<DealTicketBalance
settlementAsset={
data.market.tradableInstrument.instrument.product
?.settlementAsset
}
accounts={partyData?.party?.accounts || []}
isWalletConnected={!!keypair?.pub}
/>
)}
<DealTicketSteps market={data.market} />
</DealTicketManager>
)}

View File

@ -1,15 +1,15 @@
import { TailwindIntents } from '@vegaprotocol/ui-toolkit';
import { Intent } from '@vegaprotocol/ui-toolkit';
import { MarketState } from '@vegaprotocol/types';
export const MARKET_STATUS: Record<MarketState | '', TailwindIntents> = {
[MarketState.Active]: TailwindIntents.Success,
[MarketState.Cancelled]: TailwindIntents.Highlight,
[MarketState.Closed]: TailwindIntents.Help,
[MarketState.Pending]: TailwindIntents.Warning,
[MarketState.Proposed]: TailwindIntents.Prompt,
[MarketState.Rejected]: TailwindIntents.Danger,
[MarketState.Settled]: TailwindIntents.Highlight,
[MarketState.Suspended]: TailwindIntents.Warning,
[MarketState.TradingTerminated]: TailwindIntents.Danger,
'': TailwindIntents.Highlight,
export const MARKET_STATUS: Record<MarketState | '', Intent> = {
[MarketState.Active]: Intent.Success,
[MarketState.Cancelled]: Intent.Primary,
[MarketState.Closed]: Intent.None,
[MarketState.Pending]: Intent.Warning,
[MarketState.Proposed]: Intent.Warning,
[MarketState.Rejected]: Intent.Danger,
[MarketState.Settled]: Intent.Primary,
[MarketState.Suspended]: Intent.Warning,
[MarketState.TradingTerminated]: Intent.Danger,
'': Intent.Primary,
};

View File

@ -43,7 +43,7 @@ const getColor = (change: number | string) => {
if (parseFloat(change as string) < 0) {
return theme.colors.vega.pink;
}
return theme.colors.intent.highlight;
return theme.colors.black[10];
};
const SimpleMarketPercentChangeWrapper = (props: Props) => {

View File

@ -0,0 +1,61 @@
import { renderHook } from '@testing-library/react-hooks';
import { useSettlementAccount } from './use-settlement-account';
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
describe('useSettlementAccount Hook', () => {
it('should filter accounts by settlementAssetId', () => {
const accounts: PartyBalanceQuery_party_accounts[] = [
{
__typename: 'Account',
balance: '2000000000000000000000',
asset: {
__typename: 'Asset',
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
name: 'tBTC TEST',
decimals: 5,
},
},
{
__typename: 'Account',
balance: '1000000000',
asset: {
__typename: 'Asset',
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
symbol: 'tDAI',
name: 'tDAI TEST',
decimals: 5,
},
},
{
__typename: 'Account',
balance: '5000000000000000000',
asset: {
__typename: 'Asset',
id: 'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55',
symbol: 'VEGA',
name: 'Vega (testnet)',
decimals: 18,
},
},
];
const settlementAssetId =
'6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61';
const { result } = renderHook(() =>
useSettlementAccount(settlementAssetId, accounts)
);
expect(result.current?.balance).toBe(accounts[1].balance);
expect(result.current?.asset).toEqual(accounts[1].asset);
});
it('should return null if no accounts', () => {
const accounts: PartyBalanceQuery_party_accounts[] = [];
const settlementAssetId =
'6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61';
const { result } = renderHook(() =>
useSettlementAccount(settlementAssetId, accounts)
);
expect(result.current).toBe(undefined);
});
});

View File

@ -0,0 +1,12 @@
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
import { useMemo } from 'react';
export const useSettlementAccount = (
settlementAssetId: string,
accounts: PartyBalanceQuery_party_accounts[]
): PartyBalanceQuery_party_accounts | null => {
const callback = () =>
accounts.find((account) => account.asset.id === settlementAssetId);
const account = useMemo(callback, [accounts, settlementAssetId]);
return account as PartyBalanceQuery_party_accounts;
};

View File

@ -6,6 +6,7 @@ const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom
module.exports = {
content: [
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
'libs/ui-toolkit/src/utils/shared.ts',
...createGlobPatternsForDependencies(__dirname),
],
darkMode: 'class',

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "47.187921740233385",
"locked_amount": "45.544174911212587",
"deposits": [
{
"amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "34.71173579401319",
"locked_amount": "33.06798896499239",
"deposits": [
{
"amount": "100",

View File

@ -67,12 +67,112 @@
"tranche_id": 2,
"tranche_start": "2021-10-12T00:53:20.000Z",
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "0",
"total_removed": "0",
"locked_amount": "0",
"deposits": [],
"withdrawals": [],
"users": []
"total_added": "1010.000000000000000001",
"total_removed": "668.3761174488",
"locked_amount": "333.9866885464231390003306798896499239",
"deposits": [
{
"amount": "1000",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tx": "0x6c22362848c237c4a5c7134e88c179d6ba90cb79498e0b7a33e4c76089448ddd"
},
{
"amount": "10.000000000000000001",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tx": "0xcab88d53d9fe946b7089ce0b2e252ed6a589bc8a1900a733fdd6bd3a6fc6f514"
}
],
"withdrawals": [
{
"amount": "661.48985286",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tx": "0xdabed7e1d1f354e9102f3a678db8fa80cad17596be9b73f9b517fb0417a56954"
},
{
"amount": "6.6172349061",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tx": "0x5ff4b9826a54f8540e246519c5d3bd4a8ef1adc4fbfc324cf864beef8abd1a97"
},
{
"amount": "0.26864536",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tx": "0x7f8b379c5718352726b63438a4352e0d5657d7f0f08bf103c6fe942226e6d69d"
},
{
"amount": "0.0003652968",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tx": "0xf65916a587ded089d316b8c9b7ca2d6300f5814beb190860c1bb76dc41e63148"
},
{
"amount": "0.0000190259",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tx": "0xcbc22a50267244f1cbb61c1018270f7e9d0052815141c7c1158393036e93be5b"
}
],
"users": [
{
"address": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"deposits": [
{
"amount": "1000",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tranche_id": 2,
"tx": "0x6c22362848c237c4a5c7134e88c179d6ba90cb79498e0b7a33e4c76089448ddd"
}
],
"withdrawals": [
{
"amount": "661.48985286",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tranche_id": 2,
"tx": "0xdabed7e1d1f354e9102f3a678db8fa80cad17596be9b73f9b517fb0417a56954"
},
{
"amount": "0.26864536",
"user": "0x0d18ACaa868f87BB4666F918326141cAEAe893Fa",
"tranche_id": 2,
"tx": "0x7f8b379c5718352726b63438a4352e0d5657d7f0f08bf103c6fe942226e6d69d"
}
],
"total_tokens": "1000",
"withdrawn_tokens": "661.75849822",
"remaining_tokens": "338.24150178"
},
{
"address": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"deposits": [
{
"amount": "10.000000000000000001",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tranche_id": 2,
"tx": "0xcab88d53d9fe946b7089ce0b2e252ed6a589bc8a1900a733fdd6bd3a6fc6f514"
}
],
"withdrawals": [
{
"amount": "6.6172349061",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tranche_id": 2,
"tx": "0x5ff4b9826a54f8540e246519c5d3bd4a8ef1adc4fbfc324cf864beef8abd1a97"
},
{
"amount": "0.0003652968",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tranche_id": 2,
"tx": "0xf65916a587ded089d316b8c9b7ca2d6300f5814beb190860c1bb76dc41e63148"
},
{
"amount": "0.0000190259",
"user": "0x970Bf6C66E55f90a7D455354954Af5CaBA11318C",
"tranche_id": 2,
"tx": "0xcbc22a50267244f1cbb61c1018270f7e9d0052815141c7c1158393036e93be5b"
}
],
"total_tokens": "10.000000000000000001",
"withdrawn_tokens": "6.6176192288",
"remaining_tokens": "3.382380771200000001"
}
]
},
{
"tranche_id": 3,

View File

@ -3,7 +3,7 @@ import { DATA_SOURCES } from './config';
import { Header } from './components/header';
import { StatsManager } from '@vegaprotocol/network-stats';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
const envName = DATA_SOURCES.envName;

View File

@ -6,6 +6,7 @@ const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom
module.exports = {
content: [
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
'libs/ui-toolkit/src/utils/shared.ts',
...createGlobPatternsForDependencies(__dirname),
],
darkMode: 'class',

View File

@ -6,7 +6,7 @@ describe('token', () => {
it('should always have a header title based on environment', () => {
cy.get('[data-testid="header-title"]', { timeout: 8000 }).should(
'have.text',
`${fairgroundSet ? 'Fairground token' : '$VEGA TOKEN'}`
`${fairgroundSet ? 'Fairground token' : 'VEGA TOKEN'}`
);
});
});

View File

@ -25,6 +25,8 @@ NX_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
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"}'
#Test configuration variables
CYPRESS_FAIRGROUND = false

View File

@ -1,6 +1,7 @@
# App configuration variables
NX_VEGA_ENV = "DEVNET"
NX_VEGA_URL = "https://n04.d.vega.xyz/query"
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_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"

View File

@ -1,6 +1,7 @@
# App configuration variables
NX_VEGA_ENV = "MAINNET"
NX_VEGA_URL = "https://api.token.vega.xyz/query"
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_ETHEREUM_CHAIN_ID = 1
NX_ETHEREUM_PROVIDER_URL = "https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://etherscan.io"
NX_ETHERSCAN_URL = "https://etherscan.io"

View File

@ -1,6 +1,7 @@
# App configuration variables
NX_VEGA_ENV = "STAGNET"
NX_VEGA_URL = "https://n03.s.vega.xyz/query"
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_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"

View File

@ -1,6 +1,7 @@
# App configuration variables
NX_VEGA_ENV = "STAGNET2"
NX_VEGA_URL = "https://n03.stagnet2.vega.xyz/query"
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_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"

View File

@ -1,6 +1,7 @@
# App configuration variables
NX_VEGA_ENV = "TESTNET"
NX_VEGA_URL = "https://lb.testnet.vega.xyz/query"
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_ETHEREUM_CHAIN_ID = 3
NX_ETHEREUM_PROVIDER_URL = "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"
NX_ETHERSCAN_URL = "https://ropsten.etherscan.io"

View File

@ -1,4 +1,5 @@
import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
import { useWeb3React } from '@web3-react/core';
@ -43,11 +44,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
vesting.totalStaked(),
token.decimals(),
]);
const totalSupply = toBigNum(supply, decimals);
const totalWallet = toBigNum(totalAssociatedWallet, decimals);
const totalVesting = toBigNum(totalAssociatedVesting, decimals);
appDispatch({
type: AppStateActionType.SET_TOKEN,
decimals,
totalSupply: supply,
totalAssociated: totalAssociatedWallet.plus(totalAssociatedVesting),
totalSupply,
totalAssociated: totalWallet.plus(totalVesting),
});
setBalancesLoaded(true);
} catch (err) {

View File

@ -1,14 +1,12 @@
import './i18n';
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { BrowserRouter as Router } from 'react-router-dom';
import { AppLoader } from './app-loader';
import { AppBanner } from './components/app-banner';
import { AppFooter } from './components/app-footer';
import { BalanceManager } from './components/balance-manager';
import { EthWallet } from './components/eth-wallet';
import { GraphQlProvider } from './components/graphql-provider';
import { TemplateSidebar } from './components/page-templates/template-sidebar';
import { TransactionModal } from './components/transactions-modal';
import { VegaWallet } from './components/vega-wallet';
@ -20,12 +18,13 @@ import { Web3Provider } from '@vegaprotocol/web3';
import { Connectors } from './lib/web3-connectors';
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import { EnvironmentProvider } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider } from '@vegaprotocol/network-switcher';
import { client } from './lib/apollo-client';
function App() {
const sideBar = React.useMemo(() => [<EthWallet />, <VegaWallet />], []);
return (
<GraphQlProvider>
<ApolloProvider client={client}>
<Router>
<EnvironmentProvider>
<AppStateProvider>
@ -55,7 +54,7 @@ function App() {
</AppStateProvider>
</EnvironmentProvider>
</Router>
</GraphQlProvider>
</ApolloProvider>
);
}

View File

@ -1,15 +1,15 @@
import { useTranslation } from 'react-i18next';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import { useAddAssetSupported } from '../../hooks/use-add-asset-to-wallet';
import vegaVesting from '../../images/vega_vesting.png';
import { AddTokenButtonLink } from '../add-token-button/add-token-button';
import { Callout } from '@vegaprotocol/ui-toolkit';
export const AddLockedTokenAddress = () => {
const { ADDRESSES } = useEnvironment();
const { t } = useTranslation();
const addSupported = useAddAssetSupported();
const { ADDRESSES } = useEnvironment();
return (
<Callout
title={t(

View File

@ -12,7 +12,7 @@ export const AppBanner = () => {
return (
<div className="bg-white p-8 text-black" role="alert">
<p>
<span className="inline-block relative top-[1px] text-intent-danger text-ui mr-[5px]">
<span className="inline-block relative top-[1px] text-danger text-ui mr-[5px]">
<Error />
</span>
{bannerMessage}

View File

@ -1,3 +1,4 @@
import { Link } from '@vegaprotocol/ui-toolkit';
import { Trans } from 'react-i18next';
import { Links } from '../../config';
@ -13,8 +14,12 @@ export const AppFooter = () => {
i18nKey="footerLinksText"
components={{
/* eslint-disable */
feedbackLink: <a href={Links.FEEDBACK} />,
githubLink: <a href={Links.GITHUB} />,
feedbackLink: (
<Link className="text-white underline" href={Links.FEEDBACK} />
),
githubLink: (
<Link className="text-white underline" href={Links.GITHUB} />
),
/* eslint-enable */
}}
/>

View File

@ -1,8 +1,9 @@
import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import {
AppStateActionType,
useAppState,
@ -10,17 +11,19 @@ import {
import { useContracts } from '../../contexts/contracts/contracts-context';
import { useGetAssociationBreakdown } from '../../hooks/use-get-association-breakdown';
import { useGetUserTrancheBalances } from '../../hooks/use-get-user-tranche-balances';
import { BigNumber } from '../../lib/bignumber';
interface BalanceManagerProps {
children: React.ReactElement;
}
export const BalanceManager = ({ children }: BalanceManagerProps) => {
const { ADDRESSES } = useEnvironment();
const contracts = useContracts();
const { account } = useWeb3React();
const { appDispatch } = useAppState();
const {
appState: { decimals },
appDispatch,
} = useAppState();
const { config } = useEthereumConfig();
const getUserTrancheBalances = useGetUserTrancheBalances(
account || '',
@ -35,17 +38,26 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
// update balances on connect to Ethereum
React.useEffect(() => {
const updateBalances = async () => {
if (!account) return;
if (!account || !config) return;
try {
const [balance, walletBalance, lien, allowance] = await Promise.all([
contracts.vesting.getUserBalanceAllTranches(account),
const [b, w, stats, a] = await Promise.all([
contracts.vesting.userTotalAllTranches(account),
contracts.token.balanceOf(account),
contracts.vesting.getLien(account),
contracts.token.allowance(account, ADDRESSES.stakingBridge),
contracts.vesting.userStats(account),
contracts.token.allowance(
account,
config.staking_bridge_contract.address
),
]);
const balance = toBigNum(b, decimals);
const walletBalance = toBigNum(w, decimals);
const lien = toBigNum(stats.lien, decimals);
const allowance = toBigNum(a, decimals);
appDispatch({
type: AppStateActionType.UPDATE_ACCOUNT_BALANCES,
balance: new BigNumber(balance),
balance,
walletBalance,
lien,
allowance,
@ -57,11 +69,12 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
updateBalances();
}, [
decimals,
appDispatch,
contracts?.token,
contracts?.vesting,
account,
ADDRESSES.stakingBridge,
config,
]);
// This use effect hook is very expensive and is kept separate to prevent expensive reloading of data.

View File

@ -9,7 +9,7 @@ interface BulletHeaderProps {
export const BulletHeader = ({ tag, children, style }: BulletHeaderProps) => {
return React.createElement(
tag,
{ className: 'mt-24 pt-8 pb-20 uppercase', style },
{ className: 'mt-24 pt-8 pb-20 uppercase text-white', style },
<>
<span className="inline-block w-[12px] h-[12px] mr-12 bg-white" />
{children}

View File

@ -1,39 +0,0 @@
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Ethereum } from '../icons';
import { Button } from '@vegaprotocol/ui-toolkit';
interface EthWalletContainerProps {
children: (address: string) => React.ReactElement;
}
export const EthWalletContainer = ({ children }: EthWalletContainerProps) => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { account } = useWeb3React();
if (!account) {
return (
<Button
data-testid="connect-to-eth-btn"
onClick={() =>
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
>
<div>{t('connectEthWallet')}</div>
<Ethereum />
</Button>
);
}
return children(account);
};

View File

@ -1 +0,0 @@
export * from './eth-wallet-container';

View File

@ -22,7 +22,7 @@ import {
WalletCardHeader,
WalletCardRow,
} from '../wallet-card';
import { Button, Loader } from '@vegaprotocol/ui-toolkit';
import { Loader } from '@vegaprotocol/ui-toolkit';
import { theme } from '@vegaprotocol/tailwindcss-config';
const Colors = theme.colors;
@ -163,15 +163,15 @@ const ConnectedKey = () => {
/>
)}
<WalletCardActions>
<Link style={{ flex: 1 }} to={`${Routes.STAKING}/associate`}>
<Button variant="primary" className="w-full">
<Link className="flex-1" to={`${Routes.STAKING}/associate`}>
<span className="flex items-center justify-center w-full text-center px-28 border h-28">
{t('associate')}
</Button>
</span>
</Link>
<Link style={{ flex: 1 }} to={`${Routes.STAKING}/disassociate`}>
<Button variant="primary" className="w-full">
<Link className="flex-1" to={`${Routes.STAKING}/disassociate`}>
<span className="flex items-center justify-center w-full px-28 border h-28">
{t('disassociate')}
</Button>
</span>
</Link>
</WalletCardActions>
</>
@ -187,14 +187,14 @@ export const EthWallet = () => {
return (
<WalletCard>
<WalletCardHeader>
<h1 className="text-h3 px-8 uppercase">{t('ethereumKey')}</h1>
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
{account && (
<div className="font-mono px-4 text-right">
<div>{truncateMiddle(account)}</div>
<div className="px-4 text-right">
<div className="font-mono">{truncateMiddle(account)}</div>
{pendingTxs && (
<div>
<Button
className="flex gap-2 justify-between p-4, bg-black text-white flex-nowrap whitespace-nowrap"
<button
className="flex items-center gap-4 p-4 border whitespace-nowrap"
data-testid="pending-transactions-btn"
onClick={() =>
appDispatch({
@ -203,9 +203,9 @@ export const EthWallet = () => {
})
}
>
<Loader size="small" />
<Loader size="small" forceTheme="light" />
{t('pendingTransactions')}
</Button>
</button>
</div>
)}
</div>
@ -215,8 +215,8 @@ export const EthWallet = () => {
{account ? (
<ConnectedKey />
) : (
<Button
className="fill button-secondary--inverted"
<button
className="w-full px-28 border h-28"
onClick={() =>
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
@ -226,7 +226,7 @@ export const EthWallet = () => {
data-test-id="connect-to-eth-wallet-button"
>
{t('connectEthWalletToAssociate')}
</Button>
</button>
)}
{account && (
<WalletCardActions>

View File

@ -1,12 +0,0 @@
import { ApolloProvider } from '@apollo/client';
import React from 'react';
import { client } from '../../lib/apollo-client';
export const GraphQlProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};

View File

@ -1 +0,0 @@
export * from './graphql-provider';

View File

@ -7,7 +7,7 @@ export const Heading = ({ title }: HeadingProps) => {
return (
<header className="my-0 mx-auto">
<h1 className="font-alpha font-normal text-h3 uppercase m-0 mb-4">
<h1 className="font-alpha calt text-h3 text-white uppercase mb-4">
{title}
</h1>
</header>

View File

@ -1 +0,0 @@
export * from './loader';

View File

@ -1,35 +0,0 @@
import React from 'react';
interface LoaderProps {
invert?: boolean;
}
export const Loader = ({ invert = false }: LoaderProps) => {
const [, forceRender] = React.useState(false);
React.useEffect(() => {
const interval = setInterval(() => {
forceRender((x) => !x);
}, 100);
return () => clearInterval(interval);
}, []);
return (
<span className="flex flex-row flex-wrap w-[15px] h-[15px]">
{new Array(9).fill(null).map((_, i) => {
return (
<span
key={i}
style={{
opacity: Math.random() > 0.5 ? 1 : 0,
}}
className={`block w-5 h-5 opacity-0 ${
invert ? 'bg-black' : 'bg-white'
}`}
/>
);
})}
</span>
);
};

View File

@ -15,7 +15,7 @@ const ProgressContents = ({
}) => (
<div
className={`flex justify-between py-2 font-mono ${
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4 text-black-60'
light ? 'gap-0 px-0 text-black' : 'gap-y-0 gap-x-4 px-4'
}`}
>
{children}
@ -72,7 +72,7 @@ export const LockedProgress = ({
unlocked,
leftLabel,
rightLabel,
leftColor = Colors.pink,
leftColor = Colors.vega.pink,
rightColor = Colors.green.DEFAULT,
light = false,
}: LockedProgressProps) => {

View File

@ -5,14 +5,13 @@ import debounce from 'lodash/debounce';
import React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { useTranslation } from 'react-i18next';
import { Link, NavLink } from 'react-router-dom';
import { NavLink } from 'react-router-dom';
import { Flags } from '../../config';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import vegaWhite from '../../images/vega_white.png';
import { Routes } from '../../routes/router-config';
import { EthWallet } from '../eth-wallet';
import { VegaWallet } from '../vega-wallet';
@ -36,14 +35,14 @@ export const Nav = () => {
return (
<div
className={`p-8 ${
className={`p-16 ${
inverted
? 'bg-clouds bg-no-repeat bg-cover bg-vega-yellow'
: 'border-b-white border-b-1'
}`}
>
{isDesktop && <NavHeader fairground={inverted} />}
<div className="flex justify-between items-center mx-auto gap-12 lg:justify-start lg:ml-8">
<div className="flex justify-between items-center mx-auto gap-12 lg:justify-start">
{!isDesktop && <NavHeader fairground={inverted} />}
<div className="flex gap-12 lg:flex-auto">
{isDesktop ? (
@ -61,76 +60,10 @@ const NavHeader = ({ fairground }: { fairground: boolean }) => {
const { t } = useTranslation();
return (
<div className="h-[30px] inline-flex items-center ml-8 lg:h-40 uppercase">
<Link to="/">
{fairground ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="33"
height="20"
viewBox="0 0 200 116"
fill="none"
data-testid="fairground-icon"
>
<g clip-path="url(#clip0)">
<path
d="M70.5918 32.8569L70.5918 22.7932L60.5254 22.7932L60.5254 32.8569L70.5918 32.8569Z"
fill="black"
></path>
<path
d="M80.6641 83.2006L80.6641 73.1377L70.5977 73.1377L70.5977 83.2006L80.6641 83.2006Z"
fill="black"
></path>
<path
d="M70.5918 93.2409L70.5918 83.1772L60.5254 83.1772L60.5254 93.2409L70.5918 93.2409Z"
fill="black"
></path>
<path
d="M100.797 93.2636L100.797 73.1377L90.7305 73.1377L90.7305 93.2636L100.797 93.2636Z"
fill="black"
></path>
<path
d="M90.7285 103.33L90.7285 93.2671L80.662 93.2671L80.662 103.33L90.7285 103.33Z"
fill="black"
></path>
<path
d="M90.7285 22.8026L90.7285 12.74L80.662 12.74L80.662 22.8026L90.7285 22.8026Z"
fill="black"
></path>
<path
d="M110.869 12.6108L110.869 2.54785L100.803 2.54785L100.803 12.6108L110.869 12.6108Z"
fill="black"
></path>
<path
d="M120.934 103.326L120.934 73.1377L110.867 73.1377L110.867 103.326L120.934 103.326Z"
fill="black"
></path>
<path
d="M110.869 113.384L110.869 103.321L100.803 103.321L100.803 113.384L110.869 113.384Z"
fill="black"
></path>
<path
d="M161.328 52.9855L161.328 42.9226L151.262 42.9226L151.262 52.9855L161.328 52.9855Z"
fill="black"
></path>
<path
d="M20.133 83.187L30.3354 83.187L30.3354 73.124L40.4017 73.124L40.4017 63.0613L50.4681 63.0613L50.4681 73.124L60.5345 73.124L60.5345 63.0613L70.6008 63.0613L80.6672 63.0613L131.135 63.0613L131.135 113.376L161.334 113.376L161.334 103.313L171.4 103.313L171.4 93.25L181.467 93.25L181.467 83.187L191.533 83.187L191.533 63.0613L181.467 63.0613L181.467 73.1241L171.4 73.1241L171.4 83.187L161.334 83.187L161.334 73.1241L171.4 73.1241L171.4 63.0613L161.334 63.0613L151.268 63.0613L141.201 63.0613L141.201 52.9983L141.201 32.8726L161.334 32.8726L171.4 32.8726L171.4 63.0613L181.467 63.0613L181.467 52.9983L191.533 52.9983L191.533 32.8726L181.467 32.8726L181.467 22.8096L171.4 22.8096L171.4 12.7467L161.334 12.7467L161.334 2.54785L141.201 2.54785L131.135 2.54785L131.135 52.9983L120.933 52.9983L120.933 12.7467L110.866 12.7467L110.866 52.9983L100.8 52.9983L100.8 22.8096L90.7336 22.8096L90.7336 52.9983L80.6672 52.9983L80.6672 32.8726L70.6008 32.8726L70.6008 52.9983L60.5345 52.9983L60.5345 42.9354L50.4681 42.9354L50.4681 52.9983L40.4017 52.9983L40.4017 42.9354L30.3354 42.9354L30.3354 32.8726L20.133 32.8726L20.133 22.8096L0.000308081 22.8096L0.000307201 42.9354L10.0666 42.9354L10.0666 52.9983L20.133 52.9983L20.133 63.0613L10.0666 63.0613L10.0666 73.124L0.000305881 73.124L0.000305002 93.25L20.133 93.25L20.133 83.187Z"
fill="black"
></path>
</g>
<defs>
<clipPath id="clip0">
<rect width="200" height="116" fill="none"></rect>
</clipPath>
</defs>
</svg>
) : (
<img alt="Vega" src={vegaWhite} className="w-[30px] lg:w-40" />
)}
</Link>
<div className="h-[30px] inline-flex items-center lg:h-40 uppercase">
<h1
data-testid="header-title"
className={`text-[28px] lg:text-h3 pl-8 ${
className={`text-h3 font-alpha uppercase calt mb-2 ${
fairground ? 'text-black' : 'text-white'
}`}
>

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]">
<Nav />
<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-cover border-l border-white">
<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">
{sidebar.map((Component, i) => (
<section className="mb-20 last:mb-0" key={i}>
{Component}

View File

@ -1 +0,0 @@
export * from './stateful-button';

View File

@ -1,14 +0,0 @@
import type { ButtonHTMLAttributes } from 'react';
import { Button } from '@vegaprotocol/ui-toolkit';
export const StatefulButton = (
props: ButtonHTMLAttributes<HTMLButtonElement>
) => {
const classProp = props.className || '';
return (
<Button
{...props}
className={`flex justify-center items-center gap-12 disabled:cursor-default ${classProp}`}
/>
);
};

View File

@ -4,6 +4,7 @@ import {
Input,
Intent,
FormGroup,
Lozenge,
} from '@vegaprotocol/ui-toolkit';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -25,7 +26,6 @@ export const AmountInput = ({
amount,
setAmount,
maximum,
// TODO: render currency in input when https://github.com/vegaprotocol/frontend-monorepo/issues/273
currency,
}: {
amount: string;
@ -36,25 +36,31 @@ export const AmountInput = ({
const { t } = useTranslation();
return (
<div className="flex">
<Input
data-testid="token-amount-input"
className="flex-1"
name={inputName}
id={inputName}
onChange={(e) => setAmount(e.target.value)}
value={amount}
autoComplete="off"
type="number"
max={maximum.toString()}
min={0}
step="any"
/>
<div className="flex-1">
<Input
data-testid="token-amount-input"
name={inputName}
id={inputName}
onChange={(e) => setAmount(e.target.value)}
value={amount}
autoComplete="off"
type="number"
max={maximum.toString()}
min={0}
step="any"
appendElement={
<Lozenge className="text-[10px] relative top-[-2px]">
{currency}
</Lozenge>
}
/>
</div>
{maximum && (
<Button
variant="inline-link"
onClick={() => setAmount(maximum.toString())}
data-testid="token-amount-use-maximum"
className="flex flex-col justify-center text-ui p-8 h-28 my-0 mx-8"
className="flex flex-col justify-center p-8 h-28 my-0 mx-8"
>
{t('Use maximum')}
</Button>
@ -69,7 +75,6 @@ export const TokenInput = ({
perform,
submitText,
currency,
approveText,
allowance,
approve,
@ -84,7 +89,6 @@ export const TokenInput = ({
perform: () => void;
submitText: string;
currency: string;
requireApproval?: boolean;
maximum?: BigNumber;
minimum?: BigNumber;
@ -128,6 +132,7 @@ export const TokenInput = ({
new BigNumber(amount).isLessThan(minimum)
);
}, [amount, isApproved, maximum, requireApproval, minimum]);
let approveContent = null;
if (showApproveButton) {
@ -158,11 +163,9 @@ export const TokenInput = ({
}
} else if (requireApproval) {
approveContent = (
<Callout
iconName="tick"
intent={Intent.Success}
title={`${currency} are approved for staking`}
/>
<Callout iconName="tick" intent={Intent.Success}>
<p>{`${currency} are approved for staking`}</p>
</Callout>
);
}

View File

@ -1 +0,0 @@
export * from './transaction-button';

View File

@ -1,159 +0,0 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import type { TransactionState } from '../../hooks/transaction-reducer';
import { TxState } from '../../hooks/transaction-reducer';
import { truncateMiddle } from '../../lib/truncate-middle';
import { Button, Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Error, HandUp, Tick } from '../icons';
import { Loader } from '../loader';
import { StatefulButton } from '../stateful-button';
interface TransactionButtonProps {
text: string;
transactionState: TransactionState;
/** txHash of the transaction if already complete */
forceTxHash: string | null;
forceTxState?: TxState;
disabled?: boolean;
start: () => void;
reset: () => void;
}
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<div className="mb-20">{children}</div>
);
const Text = ({ children }: { children: React.ReactNode }) => (
<p className="flex justify-center items-center gap-10 m-0 py-12 px-32 leading-[1.15]">
{children}
</p>
);
export const TransactionButton = ({
text,
transactionState,
forceTxHash,
forceTxState,
disabled = false,
start,
reset,
}: TransactionButtonProps) => {
const { t } = useTranslation();
const { txState, txData } = transactionState;
const txHash = forceTxHash || txData.hash;
const state = forceTxState || txState;
if (state === TxState.Complete) {
return (
<Wrapper>
<Text>
<span className="text-vega-green">
<Tick />
</span>
<span>{t('txButtonComplete')}</span>
</Text>
<TransactionButtonFooter txHash={txHash} />
</Wrapper>
);
}
// User as started transaction and we are awaiting confirmation from the users wallet
if (state === TxState.Requested) {
return (
<Wrapper>
<StatefulButton disabled={true}>
<HandUp />
<span>{t('txButtonActionRequired')}</span>
</StatefulButton>
<TransactionButtonFooter
message={t('transactionHashPrompt')}
txHash={txHash}
/>
</Wrapper>
);
}
if (state === TxState.Pending) {
return (
<Wrapper>
<StatefulButton disabled={true}>
<Loader />
<span>{t('txButtonAwaiting')}</span>
</StatefulButton>
<TransactionButtonFooter txHash={txHash} />
</Wrapper>
);
}
if (state === TxState.Error) {
return (
<Wrapper>
<Text>
<span className="text-intent-danger">
<Error />
</span>
<span>{t('txButtonFailure')}</span>
<Button onClick={reset} variant="inline-link">
{t('Try again')}
</Button>
</Text>
<TransactionButtonFooter txHash={txHash} />
</Wrapper>
);
}
// Idle
return (
<Wrapper>
<StatefulButton onClick={start} disabled={disabled}>
{text}
</StatefulButton>
<TransactionButtonFooter txHash={txHash} />
</Wrapper>
);
};
interface TransactionButtonFooterProps {
txHash: string | null;
message?: string;
}
export const TransactionButtonFooter = ({
txHash,
message,
}: TransactionButtonFooterProps) => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
if (message) {
return (
<div className="mt-4 mb-0 mx-0">
<p className="m-0 py-4 pl-8 border-l border-[3px] border-intent-warning text-ui">
<span className="relative top-2 mr-4 text-intent-warning">
<Error />
</span>
{message}
</p>
</div>
);
}
if (txHash) {
return (
<div className="transaction-button__footer">
<p className="flex justify-between items-start m-0 text-ui">
<span>{t('transaction')}</span>
<Link
href={`${ETHERSCAN_URL}/tx/${txHash}`}
title={t('View on Etherscan')}
>
{truncateMiddle(txHash)}
</Link>
</p>
</div>
);
}
return null;
};

View File

@ -1,7 +1,7 @@
import { Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import type { ReactElement } from 'react';
export const TransactionComplete = ({
@ -23,13 +23,18 @@ export const TransactionComplete = ({
intent={Intent.Success}
title={heading || t('Complete')}
>
{body && <p data-testid="transaction-complete-body">{body}</p>}
<p>
{body && (
<p className="mb-8" data-testid="transaction-complete-body">
{body}
</p>
)}
<p className="mb-8">
<Link
title={t('View transaction on Etherscan')}
target="_blank"
href={`${ETHERSCAN_URL}/tx/${hash}`}
>
{hash}
{t('View transaction on Etherscan')}
</Link>
</p>
{footer && <p data-testid="transaction-complete-footer">{footer}</p>}

View File

@ -2,7 +2,7 @@ import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
export interface TransactionErrorProps {
error: Error | null;
@ -20,12 +20,15 @@ export const TransactionError = ({
return (
<Callout iconName="error" intent={Intent.Danger}>
<p>{error ? error.message : t('Something went wrong')}</p>
<p className="mb-8">
{error ? error.message : t('Something went wrong')}
</p>
{hash ? (
<p>
<p className="mb-8">
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${hash}`}
target="_blank"
>
{hash}
</Link>

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Callout } from '@vegaprotocol/ui-toolkit';
import { Callout, Loader } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
export const TransactionPending = ({
hash,
@ -37,11 +37,16 @@ export const TransactionPending = ({
return defaultTitle;
}, [heading, remainingConfirmations, t]);
return (
<Callout iconName="refresh" title={title}>
{body && <p data-testid="transaction-pending-body">{body}</p>}
<p>
<Callout icon={<Loader size="small" />} title={title}>
{body && (
<p className="mb-8" data-testid="transaction-pending-body">
{body}
</p>
)}
<p className="mb-8">
<Link
title={t('View transaction on Etherscan')}
target="_blank"
href={`${ETHERSCAN_URL}/tx/${hash}`}
>
{hash}

View File

@ -6,8 +6,8 @@ export const TransactionRequested = () => {
return (
<Callout
iconName="hand-up"
intent={Intent.Prompt}
title={t('Awaiting action in Ethereum wallet (e.g. metamask)')}
intent={Intent.Warning}
title={t('Awaiting action in Ethereum wallet (e.g. MetaMask)')}
/>
);
};

View File

@ -1,6 +1,5 @@
import type { TxData } from '@vegaprotocol/smart-contracts';
import { Dialog, Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -8,9 +7,10 @@ import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { useContracts } from '../../contexts/contracts/contracts-context';
import { truncateMiddle } from '../../lib/truncate-middle';
import { Tick } from '../icons';
import type { TxData } from '../../stores/transactions';
import { useTransactionStore } from '../../stores/transactions';
const TransactionModalTh = ({ children }: { children: React.ReactNode }) => (
<th className="border-b border-black-25 text-black-60 text-left font-normal">
@ -31,7 +31,7 @@ const TransactionModalStatus = ({
export const TransactionModal = () => {
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const { transactions } = useContracts();
const { transactions } = useTransactionStore();
const { appState, appDispatch } = useAppState();
const renderStatus = (txObj: TxData) => {
@ -84,6 +84,7 @@ export const TransactionModal = () => {
<TransactionModalTd>
<Link
title={t('View transaction on Etherscan')}
target="_blank"
href={`${ETHERSCAN_URL}/tx/${transaction.tx.hash}`}
>
{truncateMiddle(transaction.tx.hash)}

View File

@ -1,31 +1,22 @@
import { Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Links } from '../../config';
export const DownloadWalletPrompt = () => {
const { t } = useTranslation();
return (
<>
<div className="mt-8">
<h3>{t('getWallet')}</h3>
<p style={{ margin: 0 }}>
<a
className={'text-deemphasise'}
href={Links.WALLET_GUIDE}
target="_blank"
rel="noreferrer"
>
<p>
<Link className="text-deemphasise" href={Links.WALLET_GUIDE}>
{t('readGuide')}
</a>
</Link>
</p>
<p style={{ margin: 0 }}>
<a
className={'text-deemphasise'}
href={Links.WALLET_RELEASES}
target="_blank"
rel="noreferrer"
>
<p>
<Link className="text-deemphasise" href={Links.WALLET_RELEASES}>
{t('downloadWallet')}
</a>
</Link>
</p>
</>
</div>
);
};

View File

@ -17,7 +17,7 @@ import type {
DelegationsVariables,
} from './__generated__/Delegations';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
const DELEGATIONS_QUERY = gql`
query Delegations($partyId: ID!) {

View File

@ -41,10 +41,8 @@ export const VegaWallet = () => {
<WalletCard dark={true}>
<WalletCardHeader dark={true}>
<div>
<h1 className="text-h3 px-8 uppercase">{t('vegaWallet')}</h1>
<span className="mx-8 text-h6">
{keypair && `(${keypair.name})`}
</span>
<h1 className="text-h3 uppercase">{t('vegaWallet')}</h1>
<span className="text-h6">{keypair && `(${keypair.name})`}</span>
</div>
{keypair && (
<span className="font-mono px-8">
@ -72,6 +70,7 @@ const VegaWalletNotConnected = () => {
})
}
variant="secondary"
className="w-full"
data-testid="connect-vega"
>
{t('connectVegaWalletToUseAssociated')}
@ -182,10 +181,14 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
))}
<WalletCardActions>
<Link style={{ flex: 1 }} to={Routes.GOVERNANCE}>
<Button className="w-full">{t('governance')}</Button>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
{t('governance')}
</span>
</Link>
<Link style={{ flex: 1 }} to={Routes.STAKING}>
<Button className="w-full">{t('staking')}</Button>
<span className="flex items-center justify-center w-full px-28 border h-28 bg-white text-black">
{t('staking')}
</span>
</Link>
</WalletCardActions>
<VegaWalletAssetList accounts={accounts} />

View File

@ -1,3 +1,4 @@
import classNames from 'classnames';
import React from 'react';
import { Link } from 'react-router-dom';
@ -27,15 +28,13 @@ interface WalletCardProps {
dark?: boolean;
}
export const WalletCard = ({ dark, children }: WalletCardProps) => (
<div
className={`text-ui border border-white ${
dark ? 'bg-black text-white' : 'bg-white text-black'
}`}
>
{children}
</div>
);
export const WalletCard = ({ dark, children }: WalletCardProps) => {
const className = classNames('text-ui border border-white', 'p-8', {
'bg-black text-white': dark,
'bg-white text-black': !dark,
});
return <div className={className}>{children}</div>;
};
interface WalletCardHeaderProps {
children: React.ReactNode;
@ -44,7 +43,7 @@ interface WalletCardHeaderProps {
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
return (
<div className="flex justify-between items-center py-8">{children}</div>
<div className="flex gap-4 justify-between items-center">{children}</div>
);
};
@ -53,7 +52,7 @@ interface WalletCardContentProps {
}
export const WalletCardContent = ({ children }: WalletCardContentProps) => {
return <div className="my-4 mx-8">{children}</div>;
return <div className="mt-8">{children}</div>;
};
export const WalletCardRow = ({

View File

@ -8,4 +8,7 @@ export const Flags = {
MOCK: TRUTHY.includes(process.env['NX_MOCKED'] as string),
FAIRGROUND: TRUTHY.includes(process.env['NX_FAIRGROUND'] as string),
NETWORK_LIMITS: TRUTHY.includes(process.env['NX_NETWORK_LIMITS'] as string),
USE_NEW_BRIDGE_CONTRACT: TRUTHY.includes(
process.env['NX_IS_NEW_BRIDGE_CONTRACT'] as string
),
};

View File

@ -1,4 +1,4 @@
import { Networks } from '@vegaprotocol/smart-contracts';
import { Networks } from '@vegaprotocol/react-helpers';
interface VegaNode {
url: string;

View File

@ -1,20 +1,16 @@
import type {
TxData,
VegaClaim,
VegaErc20Bridge,
VegaStaking,
ERC20Token,
VegaVesting,
Claim,
Token,
TokenVesting,
StakingBridge,
} from '@vegaprotocol/smart-contracts';
import React from 'react';
export interface ContractsContextShape {
token: ERC20Token;
staking: VegaStaking;
vesting: VegaVesting;
claim: VegaClaim;
erc20Bridge: VegaErc20Bridge;
transactions: TxData[];
token: Token;
staking: StakingBridge;
vesting: TokenVesting;
claim: Claim;
}
export const ContractsContext = React.createContext<

View File

@ -1,33 +1,29 @@
import type { TxData } from '@vegaprotocol/smart-contracts';
import {
VegaClaim,
VegaErc20Bridge,
VegaStaking,
ERC20Token,
VegaVesting,
Token,
TokenVesting,
Claim,
StakingBridge,
} from '@vegaprotocol/smart-contracts';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core';
import uniqBy from 'lodash/uniqBy';
import React from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { SplashLoader } from '../../components/splash-loader';
import type { ContractsContextShape } from './contracts-context';
import { ContractsContext } from './contracts-context';
import { defaultProvider } from '../../lib/web3-connectors';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useEnvironment } from '@vegaprotocol/network-switcher';
/**
* Provides Vega Ethereum contract instances to its children.
*/
export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
const { ADDRESSES, VEGA_ENV } = useEnvironment();
const { provider: activeProvider, account } = useWeb3React();
const [txs, setTxs] = React.useState<TxData[]>([]);
const [contracts, setContracts] = React.useState<Pick<
ContractsContextShape,
'token' | 'staking' | 'vesting' | 'claim' | 'erc20Bridge'
> | null>(null);
const { config } = useEthereumConfig();
const { VEGA_ENV, ADDRESSES } = useEnvironment();
const [contracts, setContracts] =
React.useState<ContractsContextShape | null>(null);
// Create instances of contract classes. If we have an account use a signer for the
// contracts so that we can sign transactions, otherwise use the provider for just
@ -45,49 +41,21 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
signer = provider.getSigner();
}
if (provider) {
if (provider && config) {
setContracts({
token: new ERC20Token(
ADDRESSES.vegaTokenAddress,
// @ts-ignore Cant accept JsonRpcProvider provider
provider,
signer
token: new Token(ADDRESSES.vegaTokenAddress, signer || provider),
staking: new StakingBridge(
config.staking_bridge_contract.address,
signer || provider
),
// @ts-ignore Cant accept JsonRpcProvider provider
staking: new VegaStaking(VEGA_ENV, provider, signer),
// @ts-ignore Cant accept JsonRpcProvider provider
vesting: new VegaVesting(VEGA_ENV, provider, signer),
// @ts-ignore Cant accept JsonRpcProvider provider
claim: new VegaClaim(VEGA_ENV, provider, signer),
erc20Bridge: new VegaErc20Bridge(
VEGA_ENV,
// @ts-ignore Cant accept JsonRpcProvider provider
provider,
signer
vesting: new TokenVesting(
config.token_vesting_contract.address,
signer || provider
),
claim: new Claim(ADDRESSES.claimAddress, signer || provider),
});
}
}, [activeProvider, account, ADDRESSES.vegaTokenAddress, VEGA_ENV]);
React.useEffect(() => {
if (!contracts) return;
const mergeTxs = (existing: TxData[], incoming: TxData[]) => {
return uniqBy([...incoming, ...existing], 'tx.hash');
};
contracts.staking.listen((txs) => {
setTxs((curr) => mergeTxs(curr, txs));
});
contracts.vesting.listen((txs) => {
setTxs((curr) => mergeTxs(curr, txs));
});
}, [contracts]);
React.useEffect(() => {
setTxs([]);
}, [account]);
}, [activeProvider, account, config, ADDRESSES, VEGA_ENV]);
if (!contracts) {
return (
@ -98,7 +66,7 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
}
return (
<ContractsContext.Provider value={{ ...contracts, transactions: txs }}>
<ContractsContext.Provider value={contracts}>
{children}
</ContractsContext.Provider>
);

View File

@ -1,9 +1,9 @@
import React from 'react';
import * as Sentry from '@sentry/react';
import { Networks } from '@vegaprotocol/react-helpers';
import { useWeb3React } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { Networks } from '@vegaprotocol/smart-contracts';
import { useEnvironment } from '@vegaprotocol/network-switcher';
export const useAddAssetSupported = () => {
const { connector } = useWeb3React();

View File

@ -31,9 +31,9 @@ export function useAnimateValue(
) {
elRef.current?.animate(
[
{ backgroundColor: Colors.red.vega, color: Colors.white.DEFAULT },
{ backgroundColor: Colors.vega.red, color: Colors.white.DEFAULT },
{
backgroundColor: Colors.red.vega,
backgroundColor: Colors.vega.red,
color: Colors.white.DEFAULT,
offset: 0.8,
},
@ -55,11 +55,11 @@ export function useAnimateValue(
elRef.current?.animate(
[
{
backgroundColor: Colors.green.vega,
backgroundColor: Colors.vega.green,
color: Colors.white.DEFAULT,
},
{
backgroundColor: Colors.green.vega,
backgroundColor: Colors.vega.green,
color: Colors.white.DEFAULT,
offset: 0.8,
},

View File

@ -1,38 +0,0 @@
import * as React from 'react';
export function useCopyToClipboard() {
const [copied, setCopied] = React.useState(false);
// Once copied flip a boolean so we can display
// a message to the user such as 'Copied!' which will
// revert after 800 milliseconds
React.useEffect(() => {
let timeout: ReturnType<typeof setTimeout>;
if (copied) {
timeout = setTimeout(() => {
setCopied(false);
}, 800);
}
return () => clearTimeout(timeout);
}, [copied]);
// Create an input we can copy text from and render it
// off screen
function handler(text: string) {
const input = document.createElement('input');
input.style.position = 'fixed';
input.style.left = '100vw';
input.style.opacity = '0';
input.value = text;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
setCopied(true);
}
return {
copy: handler,
copied,
};
}

View File

@ -1,36 +0,0 @@
import * as Sentry from '@sentry/react';
import React from 'react';
import { NetworkParams } from '../config';
import { useNetworkParam } from './use-network-param';
export const useEthereumConfig = () => {
const { data: ethereumConfigJSON, loading } = useNetworkParam([
NetworkParams.ETHEREUM_CONFIG,
]);
const ethereumConfig = React.useMemo(() => {
if (!ethereumConfigJSON && !loading) {
Sentry.captureMessage(
`No ETH config found for network param ${NetworkParams.ETHEREUM_CONFIG}`
);
return null;
} else if (!ethereumConfigJSON) {
return null;
}
try {
const [configJson] = ethereumConfigJSON;
return JSON.parse(configJson);
} catch {
Sentry.captureMessage('Ethereum config JSON is invalid');
return null;
}
}, [ethereumConfigJSON, loading]);
if (!ethereumConfig) {
return null;
}
return {
confirmations: ethereumConfig.confirmations,
};
};

View File

@ -1,24 +1,33 @@
import React from 'react';
import type { ethers } from 'ethers';
import * as Sentry from '@sentry/react';
import type { VegaStaking, VegaVesting } from '@vegaprotocol/smart-contracts';
import { addDecimal } from '@vegaprotocol/react-helpers';
import type {
StakingBridge,
TokenVesting,
} from '@vegaprotocol/smart-contracts';
import {
AppStateActionType,
useAppState,
} from '../contexts/app-state/app-state-context';
import BigNumber from 'bignumber.js';
export function useGetAssociationBreakdown(
ethAddress: string,
staking: VegaStaking,
vesting: VegaVesting
staking: StakingBridge,
vesting: TokenVesting
): () => Promise<void> {
const { appDispatch } = useAppState();
const {
appState: { decimals },
appDispatch,
} = useAppState();
const getAssociationBreakdown = React.useCallback(async () => {
try {
const [stakingAssociations, vestingAssociations] = await Promise.all([
staking.userTotalStakedByVegaKey(ethAddress),
vesting.userTotalStakedByVegaKey(ethAddress),
userTotalStakedByVegaKey(staking, ethAddress, decimals),
userTotalStakedByVegaKey(vesting, ethAddress, decimals),
]);
appDispatch({
@ -31,7 +40,59 @@ export function useGetAssociationBreakdown(
} catch (err) {
Sentry.captureException(err);
}
}, [ethAddress, staking, vesting, appDispatch]);
}, [ethAddress, staking, vesting, decimals, appDispatch]);
return getAssociationBreakdown;
}
async function userTotalStakedByVegaKey(
contract: StakingBridge | TokenVesting,
ethereumAccount: string,
decimals: number
): Promise<{ [vegaKey: string]: BigNumber }> {
const addFilter = contract.contract.filters.Stake_Deposited(ethereumAccount);
const removeFilter = contract.contract.filters.Stake_Removed(ethereumAccount);
const addEvents = await contract.contract.queryFilter(addFilter);
const removeEvents = await contract.contract.queryFilter(removeFilter);
const res = combineStakeEventsByVegaKey(
[...addEvents, ...removeEvents],
decimals
);
return res;
}
function combineStakeEventsByVegaKey(
events: ethers.Event[],
decimals: number
): { [vegaKey: string]: BigNumber } {
const res = events.reduce((obj, e) => {
const vegaKey = e.args?.vega_public_key;
const amount = parseEventAmount(e, decimals);
const isDeposit = e.event === 'Stake_Deposited';
const isRemove = e.event === 'Stake_Removed';
if (!isDeposit && !isRemove) return obj;
if (Object.prototype.hasOwnProperty.call(obj, vegaKey)) {
if (isDeposit) {
obj[vegaKey] = obj[vegaKey].plus(amount);
} else {
obj[vegaKey] = obj[vegaKey].minus(amount);
}
} else {
if (isDeposit) {
obj[vegaKey] = amount;
} else {
obj[vegaKey] = new BigNumber(0);
}
}
return obj;
}, {} as { [vegaKey: string]: BigNumber });
return res;
}
function parseEventAmount(e: ethers.Event, decimals: number) {
const rawAmount = new BigNumber(e.args?.amount.toString() || 0);
return new BigNumber(addDecimal(rawAmount.toString(), decimals));
}

View File

@ -1,6 +1,6 @@
import React from 'react';
import * as Sentry from '@sentry/react';
import type { VegaVesting } from '@vegaprotocol/smart-contracts';
import type { TokenVesting } from '@vegaprotocol/smart-contracts';
import {
AppStateActionType,
@ -8,12 +8,16 @@ import {
} from '../contexts/app-state/app-state-context';
import { BigNumber } from '../lib/bignumber';
import { useTranches } from './use-tranches';
import { toBigNum } from '@vegaprotocol/react-helpers';
export const useGetUserTrancheBalances = (
address: string,
vesting: VegaVesting
vesting: TokenVesting
) => {
const { appDispatch } = useAppState();
const {
appState: { decimals },
appDispatch,
} = useAppState();
const { tranches } = useTranches();
return React.useCallback(async () => {
appDispatch({
@ -32,10 +36,14 @@ export const useGetUserTrancheBalances = (
);
const trancheIds = [0, ...userTranches.map((t) => t.tranche_id)];
const promises = trancheIds.map(async (tId) => {
const [total, vested] = await Promise.all([
vesting.userTrancheTotalBalance(address, tId),
vesting.userTrancheVestedBalance(address, tId),
const [t, v] = await Promise.all([
vesting.getTrancheBalance(address, tId),
vesting.getVestedForTranche(address, tId),
]);
const total = toBigNum(t, decimals);
const vested = toBigNum(v, decimals);
return {
id: tId,
locked: tId === 0 ? total : total.minus(vested),
@ -56,5 +64,5 @@ export const useGetUserTrancheBalances = (
error: e as Error,
});
}
}, [address, appDispatch, tranches, vesting]);
}, [address, decimals, appDispatch, tranches, vesting]);
};

View File

@ -1,9 +1,8 @@
import React from 'react';
import { useContracts } from '../contexts/contracts/contracts-context';
import { useTransactionStore } from '../stores/transactions';
export const usePendingTransactions = () => {
const { transactions } = useContracts();
const { transactions } = useTransactionStore();
return React.useMemo(() => {
return transactions.some((tx) => tx.pending);

View File

@ -14,8 +14,8 @@ export function useRefreshAssociatedBalances() {
async (ethAddress: string, vegaKey: string) => {
const [walletAssociatedBalance, vestingAssociatedBalance] =
await Promise.all([
staking.stakeBalance(ethAddress, vegaKey),
vesting.stakeBalance(ethAddress, vegaKey),
staking.stakeBalance(ethAddress, `0x${vegaKey}`),
vesting.stakeBalance(ethAddress, `0x${vegaKey}`),
]);
appDispatch({

View File

@ -1,8 +1,9 @@
import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEthereumConfig } from '@vegaprotocol/web3';
import React from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import {
AppStateActionType,
useAppState,
@ -10,29 +11,35 @@ import {
import { useContracts } from '../contexts/contracts/contracts-context';
export const useRefreshBalances = (address: string) => {
const { ADDRESSES } = useEnvironment();
const { appDispatch } = useAppState();
const {
appState: { decimals },
appDispatch,
} = useAppState();
const { keypair } = useVegaWallet();
const { token, staking, vesting } = useContracts();
const { config } = useEthereumConfig();
return React.useCallback(async () => {
if (!config) return;
try {
const [
balance,
walletBalance,
lien,
allowance,
walletAssociatedBalance,
vestingAssociatedBalance,
] = await Promise.all([
vesting.getUserBalanceAllTranches(address),
token.balanceOf(address),
vesting.getLien(address),
token.allowance(address, ADDRESSES.stakingBridge),
// Refresh connected vega key balances as well if we are connected to a vega key
keypair?.pub ? staking.stakeBalance(address, keypair.pub) : null,
keypair?.pub ? vesting.stakeBalance(address, keypair.pub) : null,
]);
const [b, w, stats, a, walletStakeBalance, vestingStakeBalance] =
await Promise.all([
vesting.userTotalAllTranches(address),
token.balanceOf(address),
vesting.userStats(address),
token.allowance(address, config.staking_bridge_contract.address),
// Refresh connected vega key balances as well if we are connected to a vega key
keypair?.pub ? staking.stakeBalance(address, keypair.pub) : null,
keypair?.pub ? vesting.stakeBalance(address, keypair.pub) : null,
]);
const balance = toBigNum(b, decimals);
const walletBalance = toBigNum(w, decimals);
const lien = toBigNum(stats.lien, decimals);
const allowance = toBigNum(a, decimals);
const walletAssociatedBalance = toBigNum(walletStakeBalance, decimals);
const vestingAssociatedBalance = toBigNum(vestingStakeBalance, decimals);
appDispatch({
type: AppStateActionType.REFRESH_BALANCES,
balance,
@ -47,11 +54,12 @@ export const useRefreshBalances = (address: string) => {
}
}, [
address,
decimals,
appDispatch,
keypair?.pub,
staking,
token,
vesting,
ADDRESSES.stakingBridge,
config,
]);
};

View File

@ -1,7 +1,8 @@
import type { Networks } from '@vegaprotocol/react-helpers';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { Networks, Tranche } from '@vegaprotocol/smart-contracts';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import React, { useEffect } from 'react';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import { BigNumber } from '../lib/bignumber';

View File

@ -2,19 +2,20 @@ import React from 'react';
import * as Sentry from '@sentry/react';
import { useTranslation } from 'react-i18next';
import type { ethers } from 'ethers';
import { isUnexpectedError, isUserRejection } from '../lib/web3-utils';
import {
initialState,
TransactionActionType,
transactionReducer,
} from './transaction-reducer';
import { useTransactionStore } from '../stores/transactions';
export const useTransaction = (
performTransaction: () => Promise<ethers.ContractTransaction>,
requiredConfirmations = 1
) => {
const { t } = useTranslation();
const store = useTransactionStore();
const [state, dispatch] = React.useReducer(transactionReducer, {
...initialState,
requiredConfirmations,
@ -62,10 +63,12 @@ export const useTransaction = (
try {
const tx = await performTransaction();
store.add({ tx, receipt: null, pending: true, requiredConfirmations });
dispatch({
type: TransactionActionType.TX_SUBMITTED,
txHash: tx.hash,
});
Sentry.addBreadcrumb({
type: 'Transaction',
level: Sentry.Severity.Log,
@ -83,6 +86,7 @@ export const useTransaction = (
for (let i = 1; i <= requiredConfirmations; i++) {
receipt = await tx.wait(i);
store.update({ tx, receipt, pending: true, requiredConfirmations });
dispatch({
type: TransactionActionType.TX_CONFIRMATION,
confirmations: receipt.confirmations,
@ -93,11 +97,13 @@ export const useTransaction = (
throw new Error('No receipt after confirmations are met');
}
store.update({ tx, receipt, pending: false, requiredConfirmations });
dispatch({
type: TransactionActionType.TX_COMPLETE,
receipt,
confirmations: receipt.confirmations,
});
Sentry.addBreadcrumb({
type: 'Transaction',
level: Sentry.Severity.Log,
@ -114,7 +120,7 @@ export const useTransaction = (
} catch (err) {
handleError(err as Error);
}
}, [performTransaction, requiredConfirmations, handleError]);
}, [performTransaction, requiredConfirmations, handleError, store]);
const reset = () => {
dispatch({ type: TransactionActionType.TX_RESET });

View File

@ -1,9 +1,9 @@
{
"Home": "Home",
"fairgroundTitle": "Fairground token",
"pageTitleHome": "$VEGA Tokens",
"pageTitleHome": "The $VEGA token",
"pageTitleClaim": "Claim $VEGA tokens",
"pageTitleAssociate": "Associate $VEGA tokens with $VEGA Key",
"pageTitleAssociate": "Associate $VEGA tokens with Vega Key",
"pageTitleRedemption": "Vesting",
"pageTitleLiquidity": "Incentivised Liquidity Programme",
"pageTitleRedemptionTranche": "Redeem from Tranche",
@ -79,6 +79,7 @@
"Incomplete": "Incomplete",
"Complete": "Complete",
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
"View transaction on Etherscan": "View transaction on Etherscan",
"Transaction in progress": "Transaction in progress",
"Unknown error": "Unknown error",
"Awaiting action in Ethereum wallet (e.g. MetaMask)": "Awaiting action in Ethereum wallet (e.g. MetaMask)",
@ -126,7 +127,6 @@
"noVestingTokens": "You do not have any vesting $VEGA tokens. Switch to another Ethereum address to check what can be redeemed, or view <tranchesLink>all tranches</tranchesLink>",
"ethereumKey": "Ethereum key",
"checkingForProvider": "Checking for provider",
"Awaiting action in wallet...": "Awaiting action in Ethereum wallet (e.g. MetaMask)",
"In wallet": "In wallet",
"Not staked": "Not staked",
"viewKeys": "View keys",
@ -149,7 +149,6 @@
"Stake VEGA tokens": "Stake $VEGA tokens",
"Tranche breakdown": "Tranche breakdown",
"Across all tranches": "Across all tranches",
"theVegaToken": "The {{symbol}} token",
"Token Vesting": "Vesting",
"Rewards": "Rewards",
"Governance": "Governance",
@ -238,7 +237,7 @@
"VEGA Tokens": "$VEGA Tokens",
"SLP Tokens": "SLP Tokens",
"Connected Vega key": "Connected Vega key",
"What Vega wallet/key is going to control your stake?": "What Vega Wallet/key is going to control your stake?",
"What Vega key is going to control your stake?": "What Vega key is going to control your stake?",
"Where would you like to stake from?": "Where would you like to associate from?",
"associateNoVega": "Your connected Ethereum address does not have any $VEGA",
"associateInfo1": "To participate in governance or to nominate a Validator you'll need to associate $VEGA tokens with a Vega wallet/key.",
@ -322,7 +321,7 @@
"Associate VEGA tokens": "Associate $VEGA tokens",
"VEGA token holders can vote on proposed changes to the network and create proposals.": "$VEGA token holders can vote on proposed changes to the network and create proposals.",
"VEGA token holders can nominate a validator node and receive staking rewards.": "$VEGA token holders can nominate a validator node and receive staking rewards.",
"USE YOUR VEGA TOKENS": "USE YOUR $VEGA TOKENS",
"Use your Vega tokens": "Use your Vega tokens",
"Check your vesting VEGA tokens": "Check your vesting $VEGA tokens",
"Tokens from this Tranche have been redeemed": "Tokens from this Tranche have been redeemed",
"You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.": "You have redeemed {{redeemedAmount}} $VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.",
@ -450,12 +449,12 @@
"transaction": "Transaction",
"associated": "Associated",
"notAssociated": "Not Associated",
"title": "$VEGA TOKEN",
"title": "VEGA TOKEN",
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas.": "Connect to the Ethereum wallet that holds your $VEGA tokens to see what can be redeemed from vesting tranches. To redeem tokens you will need some ETH to pay gas fees.\n",
"Staked on Vega validator": "Staked $VEGA",
"You can associate tokens while they are held in the vesting contract, when they unlock you will need to disassociate them before they can be redeemed.": "You can associate tokens while they are held in the vesting contract, when they unlock you will need to disassociate them before they can be redeemed.",
"Nominate Stake to Validator Node": "Select a validator to nominate",
"Vega key {{vegaKey}} can now participate in governance and Nominate a validator with it's stake.": "Vega key {{vegaKey}} can now participate in governance and nominate a validator with your associated $VEGA.",
"successfullAssociationMessage": "Vega key {{vegaKey}} can now participate in governance and nominate a validator with your associated $VEGA.",
"stakingStep2Text": "Your tokens need to be associated with a Vega Wallet so that you can control your stake",
"stakingStep3": "Step 3. Select the validator you'd like to nominate",
"stakeAddSuccessMessage": "You can cancel your nomination at any time",

View File

@ -1,17 +0,0 @@
/**
* From:
* https://github.com/ChainSafe/web3.js/blob/436e77a8eaa061fbaa183a9f73ca590c2e1d7697/packages/web3-utils/src/index.js
*/
export const asciiToHex = (str: string) => {
if (!str) return '0x00';
let hex = '';
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
const n = code.toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return '0x' + hex;
};

View File

@ -6,6 +6,7 @@ export function addDecimal(value: BigNumber, decimals: number): string {
.decimalPlaces(decimals)
.toString();
}
export function removeDecimal(value: BigNumber, decimals: number): string {
return value.times(Math.pow(10, decimals)).toFixed(0);
}

View File

@ -6,7 +6,7 @@ import { AddLockedTokenAddress } from '../../components/add-locked-token';
export const CodeUsed = () => {
const { t } = useTranslation();
return (
<Callout intent={Intent.Prompt} iconName="error" title={t('codeUsed')}>
<Callout intent={Intent.Warning} iconName="error" title={t('codeUsed')}>
<p>{t('codeUsedText')}</p>
<AddLockedTokenAddress />
</Callout>

View File

@ -1,5 +1,5 @@
import { Callout, Intent, Link, Button } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import { Trans, useTranslation } from 'react-i18next';
import { Link as RouteLink } from 'react-router-dom';
@ -38,6 +38,7 @@ export const Complete = ({
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${commitTxHash}`}
target="_blank"
>
{commitTxHash}
</Link>
@ -49,6 +50,7 @@ export const Complete = ({
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${claimTxHash}`}
target="_blank"
>
{claimTxHash}
</Link>

View File

@ -1,8 +1,14 @@
import { useContracts } from '../../contexts/contracts/contracts-context';
import { useTransaction } from '../../hooks/use-transaction';
import type { IClaimTokenParams } from '@vegaprotocol/smart-contracts';
import { removeDecimal } from '@vegaprotocol/react-helpers';
import { useAppState } from '../../contexts/app-state/app-state-context';
export const useClaim = (claimData: IClaimTokenParams, address: string) => {
const {
appState: { decimals },
} = useAppState();
const claimArgs = {
...claimData,
...claimData.signature,
@ -10,6 +16,7 @@ export const useClaim = (claimData: IClaimTokenParams, address: string) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
country: claimData.country!,
account: address,
amount: removeDecimal(claimData.claim.amount.toString(), decimals),
};
const { claim } = useContracts();
return useTransaction(() => claim.claim(claimArgs));

View File

@ -1,22 +1,63 @@
import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers';
import { Link, Splash } from '@vegaprotocol/ui-toolkit';
import type { EthereumConfig } from '@vegaprotocol/web3';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import { Heading } from '../../components/heading';
import { SplashLoader } from '../../components/splash-loader';
const Contracts = () => {
const { config } = useEthereumConfig();
const { ADDRESSES, ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
if (!config) {
return (
<Splash>
<SplashLoader />
</Splash>
);
}
return (
<section>
<Heading title={'Contracts'} />
<hr />
{[
'collateral_bridge_contract',
'multisig_control_contract',
'staking_bridge_contract',
'token_vesting_contract',
].map((key) => {
const contract = config[key as keyof EthereumConfig] as {
address: string;
};
return (
<div
key={key}
style={{ display: 'flex', justifyContent: 'space-between' }}
>
<div>{key}:</div>
<Link
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${contract.address}`}
>
{config.collateral_bridge_contract.address}
</Link>
</div>
);
})}
{Object.entries(ADDRESSES).map(([key, value]) => (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div
key={key}
style={{ display: 'flex', justifyContent: 'space-between' }}
>
<div>{key}:</div>
<Link
title={t('View address on Etherscan')}
href={`${ETHERSCAN_URL}/address/${value}`}
>
asdfasd
{value}
</Link>
</div>

View File

@ -14,7 +14,7 @@ export const CurrentProposalState = ({
state === ProposalState.Failed ||
state === ProposalState.Rejected
) {
className = 'text-intent-danger';
className = 'text-danger';
} else if (
state === ProposalState.Enacted ||
state === ProposalState.Passed

View File

@ -11,7 +11,7 @@ const StatusPass = ({ children }: { children: React.ReactNode }) => (
);
const StatusFail = ({ children }: { children: React.ReactNode }) => (
<span className="text-intent-danger">{children}</span>
<span className="text-danger">{children}</span>
);
export const CurrentProposalStatus = ({

View File

@ -10,7 +10,7 @@ export const ProposalTermsJson = ({
const { t } = useTranslation();
return (
<section>
<h2>{t('proposalTerms')}</h2>
<h2 className="text-h4 text-white mb-8">{t('proposalTerms')}</h2>
<SyntaxHighlighter data={terms} />
</section>
);

View File

@ -37,6 +37,7 @@ export const ProposalVotesTable = ({ proposal }: ProposalVotesTableProps) => {
data-testid="proposal-votes-table"
muted={true}
numerical={true}
headingLevel={4}
>
<KeyValueTableRow>
{t('willPass')}

View File

@ -20,58 +20,67 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
return <p>{t('noProposals')}</p>;
}
const renderRow = (proposal: Proposals_proposals) => {
if (!proposal || !proposal.id) return null;
return (
<li className="last:mb-0 mb-24" key={proposal.id}>
<Link to={proposal.id} className="underline">
<header>{getProposalName(proposal)}</header>
</Link>
<KeyValueTable muted={true}>
<KeyValueTableRow>
{t('state')}
<span data-testid="governance-proposal-state">
<CurrentProposalState proposal={proposal} />
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{isFuture(new Date(proposal.terms.closingDatetime))
? t('closesOn')
: t('closedOn')}
<span data-testid="governance-proposal-closingDate">
{format(
new Date(proposal.terms.closingDatetime),
DATE_FORMAT_DETAILED
)}
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{isFuture(new Date(proposal.terms.enactmentDatetime))
? t('proposedEnactment')
: t('enactedOn')}
<span data-testid="governance-proposal-enactmentDate">
{format(
new Date(proposal.terms.enactmentDatetime),
DATE_FORMAT_DETAILED
)}
</span>
</KeyValueTableRow>
</KeyValueTable>
</li>
);
};
return (
<>
<Heading title={t('pageTitleGovernance')} />
<p>{t('proposedChangesToVegaNetwork')}</p>
<p>{t('vegaTokenHoldersCanVote')}</p>
<p>{t('requiredMajorityDescription')}</p>
<h2>{t('proposals')}</h2>
<ul>{proposals.map((row) => renderRow(row))}</ul>
<p className="mb-8">{t('proposedChangesToVegaNetwork')}</p>
<p className="mb-8">{t('vegaTokenHoldersCanVote')}</p>
<p className="mb-8">{t('requiredMajorityDescription')}</p>
<h2 className="text-h4 text-white">{t('proposals')}</h2>
<ul>
{proposals.map((proposal) => (
<ProposalListItem proposal={proposal} />
))}
</ul>
</>
);
};
interface ProposalListItemProps {
proposal: Proposals_proposals;
}
const ProposalListItem = ({ proposal }: ProposalListItemProps) => {
const { t } = useTranslation();
if (!proposal || !proposal.id) return null;
return (
<li className="last:mb-0 mb-24" key={proposal.id}>
<Link to={proposal.id} className="underline text-white">
<header>{getProposalName(proposal)}</header>
</Link>
<KeyValueTable muted={true}>
<KeyValueTableRow>
{t('state')}
<span data-testid="governance-proposal-state">
<CurrentProposalState proposal={proposal} />
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{isFuture(new Date(proposal.terms.closingDatetime))
? t('closesOn')
: t('closedOn')}
<span data-testid="governance-proposal-closingDate">
{format(
new Date(proposal.terms.closingDatetime),
DATE_FORMAT_DETAILED
)}
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{isFuture(new Date(proposal.terms.enactmentDatetime))
? t('proposedEnactment')
: t('enactedOn')}
<span data-testid="governance-proposal-enactmentDate">
{format(
new Date(proposal.terms.enactmentDatetime),
DATE_FORMAT_DETAILED
)}
</span>
</KeyValueTableRow>
</KeyValueTable>
</li>
);
};

View File

@ -3,10 +3,8 @@ import { format } from 'date-fns';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import {
ProposalState,
VoteValue,
} from '../../../../__generated__/globalTypes';
import { ProposalState } from '../../../../__generated__/globalTypes';
import { VoteValue } from '../../../../__generated__/globalTypes';
import {
AppStateActionType,
useAppState,
@ -136,14 +134,11 @@ export const VoteButtons = ({
(voteState === VoteState.Yes || voteState === VoteState.No)
) {
const className =
voteState === VoteState.Yes
? 'text-intent-success'
: 'text-intent-danger';
voteState === VoteState.Yes ? 'text-success' : 'text-danger';
return (
<p>
<span>{t('youVoted')}</span>{' '}
<span className={className}>{t(`voteState_${voteState}`)}</span>
{'. '}
<span>{t('youVoted')}:</span>{' '}
<span className={className}>{t(`voteState_${voteState}`)}</span>{' '}
{voteDatetime ? (
<span>{format(voteDatetime, DATE_FORMAT_LONG)}. </span>
) : null}
@ -167,17 +162,11 @@ export const VoteButtons = ({
}
return (
<div className="flex">
<Button
onClick={() => submitVote(VoteValue.Yes)}
className="w-[calc(50%_-_7px)] mt-4 mr-12"
>
<div className="flex gap-4">
<Button onClick={() => submitVote(VoteValue.Yes)} className="flex-1">
{t('voteFor')}
</Button>
<Button
onClick={() => submitVote(VoteValue.No)}
className="w-[calc(50%_-_7px)] mt-4"
>
<Button onClick={() => submitVote(VoteValue.No)} className="flex-1">
{t('voteAgainst')}
</Button>
</div>

View File

@ -44,15 +44,15 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
return (
<section>
<h3>{t('votes')}</h3>
<p className="mb-0">
<h3 className="text-h4 text-white mb-8">{t('votes')}</h3>
<p className="mb-8">
<span>
<CurrentProposalStatus proposal={proposal} />
</span>
.&nbsp;
{proposal.state === ProposalState.Open ? daysLeft : null}
</p>
<table className="w-full font-normal">
<table className="w-full font-normal mb-12">
<thead>
<tr>
<th className="text-vega-green w-[18%] text-left">{t('for')}</th>
@ -62,9 +62,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
progress={yesPercentage}
/>
</th>
<th className="text-intent-danger w-[18%] text-right">
{t('against')}
</th>
<th className="text-danger w-[18%] text-right">{t('against')}</th>
</tr>
</thead>
<tbody>
@ -86,19 +84,19 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
{formatNumber(yesTokens, defaultDecimals)}
</td>
<td></td>
<td className="text-white-60">
<td className="text-white-60 text-right">
{formatNumber(noTokens, defaultDecimals)}
</td>
</tr>
</tbody>
</table>
<p>
<p className="mb-8">
{t('participation')}
{': '}
{participationMet ? (
<span className="text-vega-green mx-4">{t('met')}</span>
) : (
<span className="text-intent-danger mx-4">{t('notMet')}</span>
<span className="text-danger mx-4">{t('notMet')}</span>
)}{' '}
{formatNumber(totalTokensVoted, defaultDecimals)}{' '}
{formatNumber(totalTokensPercentage, defaultDecimals)}%
@ -109,7 +107,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
</p>
{keypair ? (
<>
<h3>{t('yourVote')}</h3>
<h3 className="text-h4 text-white mb-8">{t('yourVote')}</h3>
<VoteButtonsContainer
voteState={voteState}
castVote={castVote}

View File

@ -8,21 +8,21 @@ export const VoteProgress = ({
progress: BigNumber;
}) => {
return (
<>
<div className="w-full h-4 relative">
<div
data-testid="vote-progress-indicator"
className="relative top-[10px] w-[1px] h-16 bg-white z-[1]"
className="absolute top-[-5px] w-[1px] h-16 bg-white z-[1]"
style={{ left: `${threshold}%` }}
/>
<div className="bp3-progress-bar bp3-no-stripes bg-intent-danger rounded-none h-5">
<div className="w-full h-4">
<div
className="bp3-progress-meter bg-vega-green rounded-none"
className="absolute left-0 bg-vega-green h-4"
data-testid="vote-progress-bar"
style={{
width: `${progress}%`,
}}
/>
</div>
</>
</div>
);
};

View File

@ -34,7 +34,7 @@ const Home = ({ name }: RouteChildProps) => {
return (
<>
<Heading title={t('theVegaToken', { symbol: '$VEGA' })} />
<Heading title={t('pageTitleHome')} />
<HomeSection>
<TokenDetails
totalSupply={appState.totalSupply}
@ -42,7 +42,7 @@ const Home = ({ name }: RouteChildProps) => {
/>
</HomeSection>
<HomeSection>
<h2 className="text-h4">{t('Token Vesting')}</h2>
<h2 className="text-h4 text-white">{t('Token Vesting')}</h2>
<p className="mb-8">
{t(
'The vesting contract holds VEGA tokens until they have become unlocked.'
@ -68,7 +68,7 @@ const Home = ({ name }: RouteChildProps) => {
</Link>
</HomeSection>
<HomeSection>
<h2 className="text-h4">{t('USE YOUR VEGA TOKENS')}</h2>
<h2 className="text-h4 text-white">{t('Use your Vega tokens')}</h2>
<p className="mb-8">
{t(
'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.'
@ -83,6 +83,7 @@ const Home = ({ name }: RouteChildProps) => {
<a
data-test-id="get-vega-wallet-link"
href={Links.WALLET_GUIDE}
className="underline text-white"
target="_blank"
rel="nofollow noreferrer"
>
@ -90,15 +91,18 @@ const Home = ({ name }: RouteChildProps) => {
</a>
</p>
<p data-test-id="associate-vega-tokens-link-on-homepage">
<Link to={`${Routes.STAKING}/associate`}>
<Link
to={`${Routes.STAKING}/associate`}
className="underline text-white"
>
{t('Associate VEGA tokens')}
</Link>
</p>
</HomeSection>
<div style={{ display: 'flex', gap: 36 }}>
<div style={{ flex: 1 }}>
<div className="flex gap-40">
<div className="flex-1">
<HomeSection>
<h2 className="text-h4">{t('Staking')}</h2>
<h2 className="text-h4 text-white">{t('Staking')}</h2>
<p className="mb-8">
{t(
'VEGA token holders can nominate a validator node and receive staking rewards.'
@ -111,9 +115,9 @@ const Home = ({ name }: RouteChildProps) => {
</p>
</HomeSection>
</div>
<div style={{ flex: 1 }}>
<div className="flex-1">
<HomeSection>
<h2 className="text-h4">{t('Governance')}</h2>
<h2 className="text-h4 text-white">{t('Governance')}</h2>
<p className="mb-8">
{t(
'VEGA token holders can vote on proposed changes to the network and create proposals.'

View File

@ -1,13 +1,14 @@
import { useTranslation } from 'react-i18next';
import { Callout, Link, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '@vegaprotocol/network-switcher';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { useTranches } from '../../../hooks/use-tranches';
import type { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import { TokenDetailsCirculating } from './token-details-circulating';
import { SplashLoader } from '../../../components/splash-loader';
import { useEthereumConfig } from '@vegaprotocol/web3';
export const TokenDetails = ({
totalSupply,
@ -20,6 +21,7 @@ export const TokenDetails = ({
const { t } = useTranslation();
const { tranches, loading, error } = useTranches();
const { config } = useEthereumConfig();
if (error) {
return (
@ -29,7 +31,7 @@ export const TokenDetails = ({
);
}
if (!tranches || loading) {
if (!tranches || loading || !config) {
return (
<Splash>
<SplashLoader />
@ -43,22 +45,23 @@ export const TokenDetails = ({
{t('Token address').toUpperCase()}
<Link
data-testid="token-address"
title={t('View address on Etherscan')}
title={t('View on Etherscan (opens in a new tab)')}
className="font-mono"
href={`${ETHERSCAN_URL}/address/${ADDRESSES.vegaTokenAddress}`}
target="_blank"
>
{ADDRESSES.vegaTokenAddress}
</Link>
</KeyValueTableRow>
<KeyValueTableRow>
{t('Vesting contract'.toUpperCase())}
{t('Vesting contract').toUpperCase()}
<Link
data-testid="token-contract"
title={t('View address on Etherscan')}
title={t('View on Etherscan (opens in a new tab)')}
className="font-mono"
href={`${ETHERSCAN_URL}/address/${ADDRESSES.vestingAddress}`}
href={`${ETHERSCAN_URL}/address/${config.token_vesting_contract.address}`}
>
{ADDRESSES.vestingAddress}
{config.token_vesting_contract.address}
</Link>
</KeyValueTableRow>
<KeyValueTableRow>

View File

@ -13,9 +13,9 @@ import { Tranche0Table, TrancheTable } from '../tranche-table';
import { VestingTable } from './vesting-table';
export const RedemptionInformation = () => {
const { state, address } = useOutletContext<{
const { state, account } = useOutletContext<{
state: RedemptionState;
address: string;
account: string;
}>();
const navigate = useNavigate();
const { t } = useTranslation();
@ -28,7 +28,9 @@ export const RedemptionInformation = () => {
trancheBalances,
},
} = useAppState();
const { userTranches } = state;
const filteredTranches = React.useMemo(
() =>
userTranches.filter((tr) => {
@ -41,6 +43,7 @@ export const RedemptionInformation = () => {
}),
[trancheBalances, userTranches]
);
const zeroTranche = React.useMemo(() => {
const zeroTranche = trancheBalances.find((t) => t.id === 0);
if (zeroTranche && zeroTranche.locked.isGreaterThan(0)) {
@ -56,7 +59,9 @@ export const RedemptionInformation = () => {
<Trans
i18nKey="noVestingTokens"
components={{
tranchesLink: <Link to={Routes.TRANCHES} />,
tranchesLink: (
<Link className="underline text-white" to={Routes.TRANCHES} />
),
}}
/>
</p>
@ -67,25 +72,29 @@ export const RedemptionInformation = () => {
return (
<section data-testid="redemption-page">
<Callout>
<div className="mb-12">
<AddLockedTokenAddress />
</Callout>
<p className="mb-12" data-testid="redemption-description">
</div>
<p className="mb-24" data-testid="redemption-description">
{t(
'{{address}} has {{balance}} VEGA tokens in {{tranches}} tranches of the vesting contract.',
{
address: truncateMiddle(address),
address: truncateMiddle(account),
balance: formatNumber(balanceFormatted),
tranches: filteredTranches.length,
}
)}
</p>
<VestingTable
associated={lien}
locked={totalLockedBalance}
vested={totalVestedBalance}
/>
{filteredTranches.length ? <h2>{t('Tranche breakdown')}</h2> : null}
<div className="mb-24">
<VestingTable
associated={lien}
locked={totalLockedBalance}
vested={totalVestedBalance}
/>
</div>
{filteredTranches.length ? (
<h2 className="text-h4 text-white mb-12">{t('Tranche breakdown')}</h2>
) : null}
{zeroTranche && (
<Tranche0Table
trancheId={0}
@ -122,10 +131,12 @@ export const RedemptionInformation = () => {
<Callout
title={t('Stake your Locked VEGA tokens!')}
iconName="hand-up"
intent={Intent.Prompt}
intent={Intent.Warning}
>
<p>{t('Find out more about Staking.')}</p>
<Link to="/staking">{t('Stake VEGA tokens')}</Link>
<p className="mb-12">{t('Find out more about Staking.')}</p>
<Link to="/staking" className="underline text-white">
{t('Stake VEGA tokens')}
</Link>
</Callout>
</section>
);

View File

@ -4,11 +4,13 @@ import { Heading } from '../../components/heading';
import { useDocumentTitle } from '../../hooks/use-document-title';
import type { RouteChildProps } from '..';
import RedemptionRouter from './redemption';
import { useMatch } from 'react-router-dom';
import { Routes } from '../router-config';
const RedemptionIndex = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
const tranche = ':id';
const tranche = useMatch(`${Routes.VESTING}/:id`);
return (
<>

View File

@ -70,7 +70,7 @@ const RedemptionRouter = () => {
if (!account) {
return (
<EthConnectPrompt>
<p data-testid="eth-connect-prompt">
<p data-testid="eth-connect-prompt" className="mb-8">
{t(
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
)}

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