Compare commits

...
Author SHA1 Message Date
Dariusz Majcherczyk 83c6889d26 test: added rerun mechanism for cypress tests 2023-02-23 13:12:17 +01:00
mattrussell36 5789d3496b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-23 12:06:42 +00:00
m.ray d95fdca0d4 fix(trading): update step on market dp (#2972) 2023-02-23 11:10:56 +00:00
Maciek e4bf61c2e2 chore(trading): add market dropdown to account history (#2971) 2023-02-23 11:02:13 +00:00
Edd 0ba54cd8a4 fix(explorer): incorrect destination in market link components (#2968) 2023-02-23 10:46:55 +00:00
daro-maj eee85c231b test(trading): improved a dropdown verification test (#2969) 2023-02-23 10:16:22 +00:00
mattrussell36 afdb387742 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-23 06:08:36 +00:00
Matthew Russell 63698fbb80 feat(trading,deposits): improve ethereum connection and approve step (#2926) 2023-02-22 16:27:17 -08:00
mattrussell36 58d8f7857e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-23 00:13:13 +00:00
21 changed files with 761 additions and 327 deletions
+4 -2
View File
@@ -18,9 +18,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v3
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16
node-version: 16.15.1
- name: Run Cypress tests
uses: cypress-io/github-action@v4
@@ -48,7 +48,7 @@ const MarketLink = ({
<Link
className="underline"
{...props}
to={`/${Routes.MARKETS}#${id}`}
to={`/${Routes.MARKETS}/${id}`}
title={id}
>
{label}
@@ -56,7 +56,7 @@ const MarketLink = ({
);
} else {
return (
<Link className="underline" {...props} to={`/${Routes.MARKETS}#${id}`}>
<Link className="underline" {...props} to={`/${Routes.MARKETS}/${id}`}>
<Hash text={id} />
</Link>
);
+108 -39
View File
@@ -2761,7 +2761,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "67729.2576574087727620419",
"locked_amount": "67551.1212618119962533247",
"deposits": [
{
"amount": "86666.297",
@@ -2827,7 +2827,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1349.527338980464",
"locked_amount": "1339.22196148758675",
"deposits": [
{
"amount": "2500",
@@ -3215,7 +3215,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "3143.644010625",
"locked_amount": "32992.87091390423625",
"locked_amount": "32837.43621470227125",
"deposits": [
{
"amount": "7500",
@@ -3348,7 +3348,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "67667.46323181834793839",
"locked_amount": "67489.48936326858252012",
"deposits": [
{
"amount": "129999.45",
@@ -3414,7 +3414,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "32971.4354832572315",
"locked_amount": "32842.76564561136774",
"deposits": [
{
"amount": "10000",
@@ -3607,7 +3607,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2825.2820585996955",
"locked_amount": "2815.0049150177575",
"deposits": [
{
"amount": "5000",
@@ -3818,7 +3818,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "9597.679543998605393109",
"locked_amount": "9423.1037579670513950004",
"deposits": [
{
"amount": "97499.58",
@@ -3851,7 +3851,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "13118.370966397761248605501116",
"locked_amount": "12879.756006144750319358883816",
"deposits": [
{
"amount": "135173.4239508",
@@ -3897,7 +3897,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "4037.578041099815540999",
"locked_amount": "3964.1370226789100048754",
"deposits": [
{
"amount": "32499.86",
@@ -3930,7 +3930,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1314.1924726480796773436",
"locked_amount": "1290.2881338068485965526",
"deposits": [
{
"amount": "10833.29",
@@ -3963,7 +3963,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "4720.860935375",
"locked_amount": "4912.744982346204917697",
"locked_amount": "4823.3852248200248753928",
"deposits": [
{
"amount": "6500",
@@ -4115,7 +4115,7 @@
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "4680.6740139",
"locked_amount": "8359.25845994475165",
"locked_amount": "8265.997640423574",
"deposits": [
{
"amount": "7500",
@@ -4368,7 +4368,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "527477.636081406074478674",
"locked_amount": "523490.2515814186817628832",
"deposits": [
{
"amount": "1852091.69",
@@ -36682,7 +36682,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "609657.626547646980493",
"locked_amount": "835050.708652286678998350086",
"locked_amount": "828923.486935640836165707372",
"deposits": [
{
"amount": "1998.95815",
@@ -38019,8 +38019,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "577305.37788588626314452",
"locked_amount": "8260723.7831418714883175190553139690213572",
"total_removed": "577690.67124866818551952",
"locked_amount": "8238997.0483398786834501176341895734712084",
"deposits": [
{
"amount": "16249.93",
@@ -38594,6 +38594,11 @@
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tx": "0xba0a3eb5ac67cad5c0d3da9ef438bfeedc73c1f24554dbd47da65948d809c62f"
},
{
"amount": "385.293362781922375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb55fa2352ed3079722cfc023a587ac1d089f287267f6e2eedb4ebfbda89ac6c9"
},
{
"amount": "856.08784586478614",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -40332,6 +40337,12 @@
"tranche_id": 2,
"tx": "0xaf8d0b715946e8724a7013e1bbc17ace8f1ad153f37a9ae2f63ac02323d3f6a1"
},
{
"amount": "385.293362781922375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xb55fa2352ed3079722cfc023a587ac1d089f287267f6e2eedb4ebfbda89ac6c9"
},
{
"amount": "966.75883976995675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -41474,8 +41485,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "124285.5383358268185",
"remaining_tokens": "135713.3491641731815"
"withdrawn_tokens": "124670.831698608740875",
"remaining_tokens": "135328.055801391259125"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -43706,8 +43717,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "4397321.114354183114240906",
"locked_amount": "1904802.22508690544427934674053588",
"total_removed": "4397854.702109741180950406",
"locked_amount": "1884744.24224421827521951700476425",
"deposits": [
{
"amount": "129284.449",
@@ -43971,6 +43982,11 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xec81b6b72e023ad393f023ea8ca6ff754d39370a61f77b3e519e3d2285835d39"
},
{
"amount": "533.5877555580667095",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x4b9a1720f8b7cdd71f9b219186b0d8b5f07864ccac02b3ec278201064ca5b5ac"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -46897,6 +46913,12 @@
"tranche_id": 3,
"tx": "0xec81b6b72e023ad393f023ea8ca6ff754d39370a61f77b3e519e3d2285835d39"
},
{
"amount": "533.5877555580667095",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x4b9a1720f8b7cdd71f9b219186b0d8b5f07864ccac02b3ec278201064ca5b5ac"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -49341,8 +49363,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "311738.5671673571535575",
"remaining_tokens": "47384.9024076428464425"
"withdrawn_tokens": "312272.154922915220267",
"remaining_tokens": "46851.314652084779733"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -50685,8 +50707,8 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2749067.463242913023296295",
"locked_amount": "435695.475739600827611075817838343",
"total_removed": "3055768.040200763217391295",
"locked_amount": "427770.445408785243391682636297101",
"deposits": [
{
"amount": "552496.6455",
@@ -50850,6 +50872,11 @@
"user": "0x6ae83EAB68b7112BaD5AfD72d6B24546AbFF137D",
"tx": "0xb49529fae65600b960a8a6cebd02a4c8480983d0f399a14167b14462583cac47"
},
{
"amount": "306700.576957850194095",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tx": "0x94de638a3a061db6b45ad77bc5b7a086f55e37026b1e9d13c0bc17f645697f88"
},
{
"amount": "19117.297184565582142",
"user": "0x1dD2718fd01d05C9F50Fce8Bb723A4C7483A1E15",
@@ -52678,10 +52705,17 @@
"tx": "0xdf5c6e44ef0763e785721802704e5fd186fa3964302a566899027447d0dda57b"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "306700.576957850194095",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tranche_id": 4,
"tx": "0x94de638a3a061db6b45ad77bc5b7a086f55e37026b1e9d13c0bc17f645697f88"
}
],
"total_tokens": "331498.5873",
"withdrawn_tokens": "0",
"remaining_tokens": "331498.5873"
"withdrawn_tokens": "306700.576957850194095",
"remaining_tokens": "24798.010342149805905"
},
{
"address": "0x2895059cB5a492BEd58D1fB22713006EfaD465eA",
@@ -52712,8 +52746,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "34175.2617719782685",
"locked_amount": "132318.5581393060470258907356672",
"total_removed": "34187.9039333162685",
"locked_amount": "131347.6648336110261253122222222",
"deposits": [
{
"amount": "3000",
@@ -59442,6 +59476,11 @@
"user": "0xF55A2B967DDAA5049f537D8402b791901cC9d34E",
"tx": "0x7a9a67e318e04dfb381e22d4305c9711a331db3a5ca8136a8cbd849bd89d32f4"
},
{
"amount": "12.642161338",
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
"tx": "0x788465bb6094c84382566a7287c05a4a91ebbb374030ad3bce890e0d8d60620f"
},
{
"amount": "13.1116203702",
"user": "0x4cBC0C88d8FE503f62823B42f30a4900292C13bE",
@@ -77612,6 +77651,12 @@
}
],
"withdrawals": [
{
"amount": "12.642161338",
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
"tranche_id": 5,
"tx": "0x788465bb6094c84382566a7287c05a4a91ebbb374030ad3bce890e0d8d60620f"
},
{
"amount": "9.342237444",
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
@@ -77632,8 +77677,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "131.696949518",
"remaining_tokens": "68.303050482"
"withdrawn_tokens": "144.339110856",
"remaining_tokens": "55.660889144"
},
{
"address": "0x311944e80915b08248111173671093623Fd74851",
@@ -135612,7 +135657,7 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2021-11-05T00:00:00.000Z",
"total_added": "2576065.7689906998",
"total_removed": "1732544.457494831",
"total_removed": "1935733.591594831",
"locked_amount": "0",
"deposits": [
{
@@ -135822,6 +135867,11 @@
}
],
"withdrawals": [
{
"amount": "203189.1341",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tx": "0x901ef06328d83d21c9dc73c00ab9c171db8c483ff4435e8cff87d1d4d68ad5a3"
},
{
"amount": "12999.9446",
"user": "0x35022e6c85B50F4904C7BC7e692A9F3bbEE8D2CE",
@@ -135999,10 +136049,17 @@
"tx": "0xe2a427f7ce650e00572f627b72d04af11590f7ceed1c0e68d33b3095276f352a"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "203189.1341",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tranche_id": 8,
"tx": "0x901ef06328d83d21c9dc73c00ab9c171db8c483ff4435e8cff87d1d4d68ad5a3"
}
],
"total_tokens": "203189.1341",
"withdrawn_tokens": "0",
"remaining_tokens": "203189.1341"
"withdrawn_tokens": "203189.1341",
"remaining_tokens": "0"
},
{
"address": "0x1dC9B91DE003fd503F25cB5d114cf0fc68F7aFe6",
@@ -136822,7 +136879,7 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2021-10-05T00:00:00.000Z",
"total_added": "1019683.304332223",
"total_removed": "831508.956472223",
"total_removed": "890008.707172223",
"locked_amount": "0",
"deposits": [
{
@@ -136972,6 +137029,11 @@
}
],
"withdrawals": [
{
"amount": "58499.7507",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tx": "0xc4ee94cad30d056561bab57ffe5c635b68a039b4fa09fd042795f8125381d254"
},
{
"amount": "38999.8338",
"user": "0xfa1b33289a70fEC1E983B242bF9f3b4576cDB079",
@@ -137550,10 +137612,17 @@
"tx": "0x1e891a421557e1fdad89d485cfad22ed57faacc57bc7647b6bf36a4a96a9a78f"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "58499.7507",
"user": "0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F",
"tranche_id": 9,
"tx": "0xc4ee94cad30d056561bab57ffe5c635b68a039b4fa09fd042795f8125381d254"
}
],
"total_tokens": "58499.7507",
"withdrawn_tokens": "0",
"remaining_tokens": "58499.7507"
"withdrawn_tokens": "58499.7507",
"remaining_tokens": "0"
},
{
"address": "0x2895059cB5a492BEd58D1fB22713006EfaD465eA",
+1
View File
@@ -22,6 +22,7 @@ module.exports = defineConfig({
viewportHeight: 900,
responseTimeout: 50000,
requestTimeout: 20000,
retries: 2,
},
env: {
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
@@ -72,7 +72,11 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
cy.getByTestId('deposit-button').click();
connectEthereumWallet('Unknown');
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
cy.getByTestId('deposit-approve-submit').click();
cy.getByTestId('approve-warning').should(
'contain.text',
`Deposits of ${btcSymbol} not approved`
);
cy.getByTestId('deposit-submit').click();
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
cy.get('[data-testid="Return to deposit"]').click();
cy.get(amountField).clear().type('10');
@@ -407,7 +411,7 @@ describe('capsule', { tags: '@slow' }, () => {
cy.getByTestId('deposit-button').click();
connectEthereumWallet('Unknown');
cy.get(assetSelectField, txTimeout).select(vegaName, { force: true });
cy.getByTestId('deposit-approve-submit').click();
cy.getByTestId('deposit-submit').click();
cy.getByTestId('dialog-title').should('contain.text', 'Approve complete');
cy.get('[data-testid="Return to deposit"]').click();
cy.get(amountField).clear().type('10000');
+110 -6
View File
@@ -1,3 +1,5 @@
import { removeDecimal } from '@vegaprotocol/cypress';
import { ethers } from 'ethers';
import { connectEthereumWallet } from '../support/ethereum-wallet';
import { selectAsset } from '../support/helpers';
@@ -9,7 +11,7 @@ const formFieldError = 'input-error-text';
const ASSET_EURO = 1;
describe('deposit form validation', { tags: '@smoke' }, () => {
before(() => {
function openDepositForm() {
cy.mockWeb3Provider();
cy.mockSubscription();
cy.mockTradingPage();
@@ -20,10 +22,14 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
cy.getByTestId('deposit-button').click();
cy.wait('@Assets');
connectEthereumWallet('MetaMask');
cy.getByTestId('deposit-submit').click();
}
before(() => {
openDepositForm();
});
it('handles empty fields', () => {
cy.getByTestId('deposit-submit').click();
cy.getByTestId(formFieldError).should('contain.text', 'Required');
cy.getByTestId(formFieldError).should('have.length', 2);
});
@@ -44,6 +50,13 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
});
it('invalid amount', () => {
mockWeb3DepositCalls({
allowance: '1000',
depositLifetimeLimit: '1000',
balance: '800',
deposited: '0',
dps: 5,
});
// Deposit amount smaller than minimum viable for selected asset
// Select an amount so that we have a known decimal places value to work with
selectAsset(ASSET_EURO);
@@ -56,12 +69,16 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
});
it('insufficient funds', () => {
// 1001-DEPO-005
// Deposit amount is valid, but less than approved. This will always be the case because our
// CI wallet wont have approved any assets
mockWeb3DepositCalls({
allowance: '1000',
depositLifetimeLimit: '1000',
balance: '800',
deposited: '0',
dps: 5,
});
cy.get(amountField)
.clear()
.type('100')
.type('850')
.next(`[data-testid="${formFieldError}"]`)
.should('have.text', 'Insufficient amount in Ethereum wallet');
});
@@ -88,3 +105,90 @@ describe('deposit actions', { tags: '@smoke' }, () => {
cy.getByTestId('deposit-submit').should('be.visible');
});
});
function mockWeb3DepositCalls({
allowance,
depositLifetimeLimit,
balance,
deposited,
dps,
}: {
allowance: string;
depositLifetimeLimit: string;
balance: string;
deposited: string;
dps: number;
}) {
const assetContractAddress = '0x0158031158bb4df2ad02eaa31e8963e84ea978a4';
const collateralBridgeAddress = '0x7fe27d970bc8afc3b11cc8d9737bfb66b1efd799';
const toResult = (value: string, dps: number) => {
const rawValue = removeDecimal(value, dps);
return ethers.utils.hexZeroPad(
ethers.utils.hexlify(parseInt(rawValue)),
32
);
};
cy.intercept('POST', 'http://localhost:8545', (req) => {
// Mock chainId call
if (req.body.method === 'eth_chainId') {
req.alias = 'eth_chainId';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: '0xaa36a7', // 11155111 for sepolia chain id
});
}
// Mock deposited amount
if (req.body.method === 'eth_getStorageAt') {
req.alias = 'eth_getStorageAt';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(deposited, dps),
});
}
if (req.body.method === 'eth_call') {
// Mock approved amount for asset on collateral bridge
if (
req.body.params[0].to === assetContractAddress &&
req.body.params[0].data ===
'0xdd62ed3e000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f0000000000000000000000007fe27d970bc8afc3b11cc8d9737bfb66b1efd799'
) {
req.alias = 'eth_call_allowance';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(allowance, dps),
});
}
// Mock balance of asset in Ethereum wallet
else if (
req.body.params[0].to === assetContractAddress &&
req.body.params[0].data ===
'0x70a08231000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f'
) {
req.alias = 'eth_call_balanceOf';
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(balance, dps),
});
}
// Mock deposit lifetime limit
else if (
req.body.params[0].to === collateralBridgeAddress &&
req.body.params[0].data ===
'0x354a897a0000000000000000000000000158031158bb4df2ad02eaa31e8963e84ea978a4'
) {
req.alias = 'eth_call_get_deposit_maximum'; // deposit lifetime limit
req.reply({
id: req.body.id,
jsonrpc: req.body.jsonrpc,
result: toResult(depositLifetimeLimit, dps),
});
}
}
});
}
+10 -4
View File
@@ -4,6 +4,7 @@ import { marketsQuery } from '@vegaprotocol/mock';
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
const dialogCloseBtn = 'dialog-close';
const popoverTrigger = 'popover-trigger';
describe('markets table', { tags: '@smoke' }, () => {
beforeEach(() => {
@@ -173,8 +174,13 @@ describe('markets table', { tags: '@smoke' }, () => {
});
function openMarketDropDown() {
cy.getByTestId(dialogCloseBtn).should('be.visible');
cy.getByTestId(dialogCloseBtn).click();
cy.getByTestId('popover-trigger').click();
cy.contains('Loading market data...').should('not.exist');
cy.getByTestId(dialogCloseBtn).then((button) => {
if (button.is(':visible')) {
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
cy.getByTestId(dialogCloseBtn).click();
}
cy.get('[data-testid^="ask-vol-"]').should('be.visible');
cy.getByTestId(popoverTrigger).click({ force: true });
cy.contains('Loading market data...').should('not.exist');
});
}
+36 -5
View File
@@ -17,19 +17,50 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
});
it('can connect', () => {
// Mock authentication
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
body: {
token: 'test-token',
},
});
// Mock getting keys from wallet
cy.intercept('GET', 'https://wallet.testnet.vega.xyz/api/v1/keys', {
body: {
keys: [
{
algorithm: {
name: 'algo',
version: 1,
},
index: 0,
meta: [],
pub: 'HOSTED_PUBKEY',
tainted: false,
},
],
},
});
cy.getByTestId(connectVegaBtn).click();
mockConnectWallet();
cy.contains('Connect Vega wallet');
cy.contains('Hosted Fairground wallet');
cy.getByTestId('connectors-list')
.find('[data-testid="connector-jsonRpc"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.wait('@walletReq');
cy.getByTestId(form).find('#wallet').click().type('user');
cy.getByTestId(form).find('#passphrase').click().type('pass');
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
cy.getByTestId(manageVegaBtn).should('exist');
});
it('doesnt connect with invalid credentials', () => {
// Mock incorrect username/password
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
body: {
error: 'No wallet',
},
statusCode: 403, // 403 forbidden invalid crednetials
});
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-hosted"]')
@@ -37,10 +68,10 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
cy.getByTestId(form).find('#wallet').click().type('invalid name');
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
cy.getByTestId('form-error').should('have.text', 'No wallet detected');
cy.getByTestId('form-error').should('have.text', 'Invalid credentials');
});
it('doesnt connect with invalid fields', () => {
it('doesnt connect with empty fields', () => {
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-hosted"]')
@@ -3,12 +3,14 @@ query AccountHistory(
$assetId: ID!
$accountTypes: [AccountType!]
$dateRange: DateRange
$marketIds: [ID!]
) {
balanceChanges(
filter: {
partyIds: [$partyId]
accountTypes: $accountTypes
assetId: $assetId
marketIds: $marketIds
}
dateRange: $dateRange
) {
@@ -8,6 +8,7 @@ export type AccountHistoryQueryVariables = Types.Exact<{
assetId: Types.Scalars['ID'];
accountTypes?: Types.InputMaybe<Array<Types.AccountType> | Types.AccountType>;
dateRange?: Types.InputMaybe<Types.DateRange>;
marketIds?: Types.InputMaybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>;
}>;
@@ -23,9 +24,9 @@ export type AccountsWithBalanceQuery = { __typename?: 'Query', balanceChanges: {
export const AccountHistoryDocument = gql`
query AccountHistory($partyId: ID!, $assetId: ID!, $accountTypes: [AccountType!], $dateRange: DateRange) {
query AccountHistory($partyId: ID!, $assetId: ID!, $accountTypes: [AccountType!], $dateRange: DateRange, $marketIds: [ID!]) {
balanceChanges(
filter: {partyIds: [$partyId], accountTypes: $accountTypes, assetId: $assetId}
filter: {partyIds: [$partyId], accountTypes: $accountTypes, assetId: $assetId, marketIds: $marketIds}
dateRange: $dateRange
) {
edges {
@@ -58,6 +59,7 @@ export const AccountHistoryDocument = gql`
* assetId: // value for 'assetId'
* accountTypes: // value for 'accountTypes'
* dateRange: // value for 'dateRange'
* marketIds: // value for 'marketIds'
* },
* });
*/
@@ -7,7 +7,7 @@ import {
import { useVegaWallet } from '@vegaprotocol/wallet';
import compact from 'lodash/compact';
import type { ChangeEvent } from 'react';
import { useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { AccountHistoryQuery } from './__generated__/AccountHistory';
import { useAccountHistoryQuery } from './__generated__/AccountHistory';
import * as Schema from '@vegaprotocol/types';
@@ -25,8 +25,10 @@ import {
import { AccountTypeMapping } from '@vegaprotocol/types';
import { PriceChart } from 'pennant';
import 'pennant/dist/style.css';
import { accountsOnlyDataProvider } from '@vegaprotocol/accounts';
import type { Account } from '@vegaprotocol/accounts';
import { accountsDataProvider } from '@vegaprotocol/accounts';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import type { Market } from '@vegaprotocol/market-list';
const DateRange = {
RANGE_1D: '1D',
@@ -97,7 +99,7 @@ const AccountHistoryManager = ({
);
const { data: accounts } = useDataProvider({
dataProvider: accountsOnlyDataProvider,
dataProvider: accountsDataProvider,
variables: variablesForOneTimeQuery,
skip: !pubKey,
});
@@ -118,6 +120,45 @@ const AccountHistoryManager = ({
const [range, setRange] = useState<typeof DateRange[keyof typeof DateRange]>(
DateRange.RANGE_1M
);
const [market, setMarket] = useState<Market | null>(null);
const marketFilterCb = useCallback(
(item: Market) =>
!asset?.id ||
item.tradableInstrument.instrument.product.settlementAsset.id ===
asset?.id,
[asset?.id]
);
const markets = useMemo<Market[] | null>(() => {
const arr =
accounts
?.filter((item: Account) => Boolean(item && item.market))
.map<Market>((item) => item.market as Market) ?? null;
return arr
? Array.from(
new Set(
arr
.filter(marketFilterCb)
.sort((a, b) =>
a.tradableInstrument.instrument.code.localeCompare(
b.tradableInstrument.instrument.code
)
)
)
)
: null;
}, [accounts, marketFilterCb]);
const resolveMarket = useCallback(
(m: Market) => {
setMarket(m);
const newAssetId =
m.tradableInstrument.instrument.product.settlementAsset.id;
const newAsset = assets.find((item) => item.id === newAssetId);
if ((!asset || (assets && newAssetId !== asset.id)) && newAsset) {
setAsset(newAsset);
}
},
[asset, assets]
);
const variables = useMemo(
() => ({
@@ -126,62 +167,110 @@ const AccountHistoryManager = ({
accountTypes: accountType ? [accountType] : undefined,
dateRange:
range === 'All' ? undefined : { start: calculateStartDate(range) },
marketIds: market?.id ? [market.id] : undefined,
}),
[pubKey, asset, accountType, range]
[pubKey, asset, accountType, range, market?.id]
);
const { data } = useAccountHistoryQuery({
variables,
skip: !asset || !pubKey,
});
const accountTypeMenu = useMemo(() => {
return (
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{accountType
? `${
AccountTypeMapping[
accountType as keyof typeof Schema.AccountType
]
} Account`
: t('Select account type')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{[
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
Schema.AccountType.ACCOUNT_TYPE_BOND,
Schema.AccountType.ACCOUNT_TYPE_MARGIN,
].map((type) => (
<DropdownMenuItem
key={type}
onClick={() => setAccountType(type as Schema.AccountType)}
>
{AccountTypeMapping[type as keyof typeof Schema.AccountType]}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}, [accountType]);
const assetsMenu = useMemo(() => {
return (
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{asset ? asset.symbol : t('Select asset')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{assets.map((a) => (
<DropdownMenuItem key={a.id} onClick={() => setAsset(a)}>
{a.symbol}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}, [assets, asset]);
const marketsMenu = useMemo(() => {
return accountType === Schema.AccountType.ACCOUNT_TYPE_MARGIN &&
markets?.length ? (
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{market
? market.tradableInstrument.instrument.code
: t('Select market')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{markets?.map((m) => (
<DropdownMenuItem key={m.id} onClick={() => resolveMarket(m)}>
{m.tradableInstrument.instrument.code}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
) : null;
}, [markets, market, accountType, resolveMarket]);
useEffect(() => {
if (
accountType !== Schema.AccountType.ACCOUNT_TYPE_MARGIN ||
(asset &&
market &&
market.tradableInstrument.instrument.product.settlementAsset.id !==
asset.id)
) {
setMarket(null);
}
}, [accountType, asset, market]);
return (
<div className="h-full w-full flex flex-col gap-8">
<div className="w-full flex flex-col-reverse lg:flex-row items-start lg:items-center justify-between gap-4 px-2">
<div className="flex items-center gap-4 shrink-0">
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{accountType
? `${
AccountTypeMapping[
accountType as keyof typeof Schema.AccountType
]
} Account`
: t('Select account type')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{[
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
Schema.AccountType.ACCOUNT_TYPE_BOND,
Schema.AccountType.ACCOUNT_TYPE_MARGIN,
].map((type) => (
<DropdownMenuItem
key={type}
onClick={() => setAccountType(type as Schema.AccountType)}
>
{AccountTypeMapping[type as keyof typeof Schema.AccountType]}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu
trigger={
<DropdownMenuTrigger>
{asset ? asset.symbol : t('Select asset')}
</DropdownMenuTrigger>
}
>
<DropdownMenuContent>
{assets.map((a) => (
<DropdownMenuItem key={a.id} onClick={() => setAsset(a)}>
{a.symbol}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<>
{accountTypeMenu}
{assetsMenu}
{marketsMenu}
</>
</div>
<div className="pt-1 justify-items-end">
<Toggle
@@ -318,33 +318,37 @@ const SummaryMessage = memo(
}
if (!pubKey) {
return (
<Notification
testId={'deal-ticket-connect-wallet'}
intent={Intent.Warning}
message={
<p className="text-sm pb-2">
You need a{' '}
<ExternalLink href="https://vega.xyz/wallet">
Vega wallet
</ExternalLink>{' '}
with {assetSymbol} to start trading in this market.
</p>
}
buttonProps={{
text: t('Connect wallet'),
action: openVegaWalletDialog,
dataTestId: 'order-connect-wallet',
size: 'md',
}}
/>
<div className="mb-4">
<Notification
testId={'deal-ticket-connect-wallet'}
intent={Intent.Warning}
message={
<p className="text-sm pb-2">
You need a{' '}
<ExternalLink href="https://vega.xyz/wallet">
Vega wallet
</ExternalLink>{' '}
with {assetSymbol} to start trading in this market.
</p>
}
buttonProps={{
text: t('Connect wallet'),
action: openVegaWalletDialog,
dataTestId: 'order-connect-wallet',
size: 'md',
}}
/>
</div>
);
}
if (errorMessage === SummaryValidationType.NoCollateral) {
return (
<ZeroBalanceError
asset={market.tradableInstrument.instrument.product.settlementAsset}
onClickCollateral={onClickCollateral}
/>
<div className="mb-4">
<ZeroBalanceError
asset={market.tradableInstrument.instrument.product.settlementAsset}
onClickCollateral={onClickCollateral}
/>
</div>
);
}
@@ -363,7 +367,11 @@ const SummaryMessage = memo(
// If there is no blocking error but user doesn't have enough
// balance render the margin warning, but still allow submission
if (balanceError) {
return <MarginWarning balance={balance} margin={margin} asset={asset} />;
return (
<div className="mb-4">
<MarginWarning balance={balance} margin={margin} asset={asset} />;
</div>
);
}
// Show auction mode warning
@@ -375,13 +383,15 @@ const SummaryMessage = memo(
].includes(marketData.marketTradingMode)
) {
return (
<Notification
intent={Intent.Warning}
testId={'dealticket-warning-auction'}
message={t(
'Any orders placed now will not trade until the auction ends'
)}
/>
<div className="mb-4">
<Notification
intent={Intent.Warning}
testId={'dealticket-warning-auction'}
message={t(
'Any orders placed now will not trade until the auction ends'
)}
/>
</div>
);
}
+75 -12
View File
@@ -4,10 +4,12 @@ import type { DepositFormProps } from './deposit-form';
import { DepositForm } from './deposit-form';
import * as Schema from '@vegaprotocol/types';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
jest.mock('@vegaprotocol/wallet');
jest.mock('@vegaprotocol/web3');
jest.mock('@web3-react/core');
const mockConnector = { deactivate: jest.fn() };
@@ -37,6 +39,8 @@ function generateAsset(): AssetFieldsFragment {
let asset: AssetFieldsFragment;
let props: DepositFormProps;
const MOCK_ETH_ADDRESS = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
const MOCK_VEGA_KEY =
'70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680';
beforeEach(() => {
asset = generateAsset();
@@ -89,14 +93,17 @@ describe('Deposit form', () => {
});
});
it('fails when submitted with invalid ethereum address', async () => {
(useWeb3React as jest.Mock).mockReturnValue({ account: '123' });
it('fails when Ethereum wallet not connected', async () => {
(useWeb3React as jest.Mock).mockReturnValue({
isActive: false,
account: '',
});
render(<DepositForm {...props} />);
fireEvent.submit(screen.getByTestId('deposit-form'));
expect(
await screen.findByText('Invalid Ethereum address')
await screen.findByText('Connect Ethereum wallet')
).toBeInTheDocument();
});
@@ -138,7 +145,7 @@ describe('Deposit form', () => {
fireEvent.submit(screen.getByTestId('deposit-form'));
expect(
await screen.findByText('Insufficient amount in Ethereum wallet')
await screen.findByText('Amount is above deposit limit')
).toBeInTheDocument();
});
@@ -159,7 +166,7 @@ describe('Deposit form', () => {
fireEvent.submit(screen.getByTestId('deposit-form'));
expect(
await screen.findByText('Amount is above approved amount')
await screen.findByText('Amount is above approved amount.')
).toBeInTheDocument();
});
@@ -193,9 +200,9 @@ describe('Deposit form', () => {
});
});
it('handles deposit approvals', () => {
it('handles deposit approvals', async () => {
const mockUseVegaWallet = useVegaWallet as jest.Mock;
mockUseVegaWallet.mockReturnValue({ pubKey: null });
mockUseVegaWallet.mockReturnValue({ pubKey: MOCK_VEGA_KEY });
const mockUseWeb3React = useWeb3React as jest.Mock;
mockUseWeb3React.mockReturnValue({
@@ -212,13 +219,18 @@ describe('Deposit form', () => {
/>
);
fireEvent.click(
screen.getByText(`Approve ${asset.symbol}`, {
selector: '[type="button"]',
})
expect(screen.queryByLabelText('Amount')).not.toBeInTheDocument();
expect(screen.getByTestId('approve-warning')).toHaveTextContent(
`Deposits of ${asset.symbol} not approved`
);
expect(props.submitApprove).toHaveBeenCalled();
fireEvent.click(
screen.getByRole('button', { name: `Approve ${asset.symbol}` })
);
await waitFor(() => {
expect(props.submitApprove).toHaveBeenCalled();
});
});
it('handles submitting a deposit', async () => {
@@ -284,4 +296,55 @@ describe('Deposit form', () => {
render(<DepositForm {...props} />);
expect(await screen.queryAllByTestId('view-asset-details')).toHaveLength(0);
});
it('renders a connect button if Ethereum wallet is not connected', () => {
(useWeb3React as jest.Mock).mockReturnValue({
isActive: false,
account: '',
});
render(<DepositForm {...props} />);
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
expect(
screen.queryByLabelText('From (Ethereum address)')
).not.toBeInTheDocument();
});
it('renders a disabled input if Ethereum wallet is connected', () => {
(useWeb3React as jest.Mock).mockReturnValue({
isActive: true,
account: MOCK_ETH_ADDRESS,
});
render(<DepositForm {...props} />);
expect(
screen.queryByRole('button', { name: 'Connect' })
).not.toBeInTheDocument();
const fromInput = screen.getByLabelText('From (Ethereum address)');
expect(fromInput).toHaveValue(MOCK_ETH_ADDRESS);
expect(fromInput).toBeDisabled();
expect(fromInput).toHaveAttribute('readonly');
});
it('prevents submission if you are on the wrong chain', () => {
(useWeb3React as jest.Mock).mockReturnValue({
isActive: true,
account: MOCK_ETH_ADDRESS,
chainId: 1,
});
(useWeb3ConnectStore as unknown as jest.Mock).mockImplementation(
// eslint-disable-next-line
(selector: (result: ReturnType<typeof useWeb3ConnectStore>) => any) => {
return selector({
desiredChainId: 11155111,
open: jest.fn(),
foo: 'asdf',
});
}
);
render(<DepositForm {...props} />);
expect(screen.getByTestId('chain-error')).toHaveTextContent(
/this app only works on/i
);
});
});
+201 -161
View File
@@ -1,8 +1,8 @@
import type { Asset } from '@vegaprotocol/assets';
import { AssetOption } from '@vegaprotocol/assets';
import {
ethereumAddress,
t,
ethereumAddress,
required,
vegaPublicKey,
minSafe,
@@ -14,17 +14,19 @@ import {
import {
Button,
FormGroup,
Icon,
Input,
InputError,
RichSelect,
Notification,
Intent,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useWeb3React } from '@web3-react/core';
import BigNumber from 'bignumber.js';
import type { ButtonHTMLAttributes, ReactNode } from 'react';
import type { ButtonHTMLAttributes } from 'react';
import { useMemo } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';
import type { FieldError } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { DepositLimits } from './deposit-limits';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import {
@@ -72,7 +74,8 @@ export const DepositForm = ({
isFaucetable,
}: DepositFormProps) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const { account } = useWeb3React();
const openDialog = useWeb3ConnectStore((store) => store.open);
const { isActive, account } = useWeb3React();
const { pubKey } = useVegaWallet();
const {
register,
@@ -83,26 +86,27 @@ export const DepositForm = ({
formState: { errors },
} = useForm<FormFields>({
defaultValues: {
from: account,
to: pubKey ? pubKey : undefined,
asset: selectedAsset?.id || '',
},
});
const onDeposit = async (fields: FormFields) => {
const onSubmit = async (fields: FormFields) => {
if (!selectedAsset || selectedAsset.source.__typename !== 'ERC20') {
throw new Error('Invalid asset');
}
submitDeposit({
assetSource: selectedAsset.source.contractAddress,
amount: fields.amount,
vegaPublicKey: fields.to,
});
if (approved) {
submitDeposit({
assetSource: selectedAsset.source.contractAddress,
amount: fields.amount,
vegaPublicKey: fields.to,
});
} else {
submitApprove();
}
};
const amount = useWatch({ name: 'amount', control });
const maxAmount = useMemo(() => {
const maxApproved = allowance ? allowance : new BigNumber(0);
const maxAvailable = balance ? balance : new BigNumber(0);
@@ -133,9 +137,12 @@ export const DepositForm = ({
return minViableAmount;
}, [selectedAsset]);
const approved = allowance && allowance.isGreaterThan(0) ? true : false;
const formState = getFormState(selectedAsset, isActive, approved);
return (
<form
onSubmit={handleSubmit(onDeposit)}
onSubmit={handleSubmit(onSubmit)}
noValidate={true}
data-testid="deposit-form"
>
@@ -143,19 +150,54 @@ export const DepositForm = ({
label={t('From (Ethereum address)')}
labelFor="ethereum-address"
>
<Input
id="ethereum-address"
{...register('from', {
<Controller
name="from"
control={control}
rules={{
validate: {
required,
required: (value) => {
if (!value) return t('Connect Ethereum wallet');
return true;
},
ethereumAddress,
},
})}
/>
<EthereumButton
clearAddress={() => {
setValue('from', '');
clearErrors('from');
}}
defaultValue={account}
render={() => {
if (isActive && account) {
return (
<>
<Input
id="ethereum-address"
value={account}
readOnly={true}
disabled={true}
{...register('from', {
validate: {
required,
ethereumAddress,
},
})}
/>
<DisconnectEthereumButton
onDisconnect={() => {
setValue('from', ''); // clear from value so required ethereum connection validation works
}}
/>
</>
);
}
return (
<Button
onClick={openDialog}
variant="primary"
fill={true}
type="button"
data-testid="connect-eth-wallet-btn"
>
{t('Connect')}
</Button>
);
}}
/>
{errors.from?.message && (
@@ -241,148 +283,136 @@ export const DepositForm = ({
deposited={deposited}
balance={balance}
asset={selectedAsset}
allowance={allowance}
/>
</div>
)}
<FormGroup label={t('Amount')} labelFor="amount">
<Input
type="number"
autoComplete="off"
id="amount"
{...register('amount', {
validate: {
required,
minSafe: (value) => minSafe(new BigNumber(min))(value),
maxSafe: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(maxAmount.available)) {
return t('Insufficient amount in Ethereum wallet');
} else if (value.isGreaterThan(maxAmount.limit)) {
return t('Amount is above temporary deposit limit');
} else if (value.isGreaterThan(maxAmount.approved)) {
return t('Amount is above approved amount');
}
return maxSafe(maxAmount.amount)(v);
{formState === 'deposit' && (
<FormGroup label={t('Amount')} labelFor="amount">
<Input
type="number"
autoComplete="off"
id="amount"
{...register('amount', {
validate: {
required,
minSafe: (value) => minSafe(new BigNumber(min))(value),
approved: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(maxAmount.approved)) {
return t('Amount is above approved amount');
}
return true;
},
limit: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(maxAmount.limit)) {
return t('Amount is above deposit limit');
}
return true;
},
balance: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(maxAmount.available)) {
return t('Insufficient amount in Ethereum wallet');
}
return true;
},
maxSafe: (v) => {
return maxSafe(maxAmount.amount)(v);
},
},
},
})}
/>
{errors.amount?.message && (
<InputError intent="danger" forInput="amount">
{errors.amount.message}
</InputError>
)}
{selectedAsset && balance && (
<UseButton
onClick={() => {
setValue('amount', balance.toFixed(selectedAsset.decimals));
clearErrors('amount');
}}
>
{t('Use maximum')}
</UseButton>
)}
</FormGroup>
<FormButton
selectedAsset={selectedAsset}
amount={new BigNumber(amount || 0)}
allowance={allowance}
onApproveClick={submitApprove}
/>
})}
/>
{errors.amount?.message && (
<AmountError error={errors.amount} submitApprove={submitApprove} />
)}
{selectedAsset && balance && (
<UseButton
onClick={() => {
setValue('amount', balance.toFixed(selectedAsset.decimals));
clearErrors('amount');
}}
>
{t('Use maximum')}
</UseButton>
)}
</FormGroup>
)}
<FormButton selectedAsset={selectedAsset} formState={formState} />
</form>
);
};
const AmountError = ({
error,
submitApprove,
}: {
error: FieldError;
submitApprove: () => void;
}) => {
if (error.type === 'approved') {
return (
<InputError intent="danger" forInput="amount">
{error.message}.
<button onClick={submitApprove} className="underline ml-2">
{t('Update approve amount')}
</button>
</InputError>
);
}
return (
<InputError intent="danger" forInput="amount">
{error.message}
</InputError>
);
};
interface FormButtonProps {
selectedAsset?: Asset;
amount: BigNumber;
allowance: BigNumber | undefined;
onApproveClick: () => void;
formState: ReturnType<typeof getFormState>;
}
const FormButton = ({
selectedAsset,
amount,
allowance,
onApproveClick,
}: FormButtonProps) => {
const { open, desiredChainId } = useWeb3ConnectStore((store) => ({
open: store.open,
desiredChainId: store.desiredChainId,
}));
const FormButton = ({ selectedAsset, formState }: FormButtonProps) => {
const { isActive, chainId } = useWeb3React();
const approved =
allowance && allowance.isGreaterThan(0) && amount.isLessThan(allowance);
let button = null;
let message: ReactNode = '';
if (!isActive) {
button = (
<Button onClick={open} data-testid="connect-eth-wallet-btn">
{t('Connect Ethereum wallet')}
</Button>
);
} else if (chainId !== desiredChainId) {
const chainName = getChainName(desiredChainId);
message = t(`This app only works on ${chainName}.`);
button = (
<Button
type="submit"
data-testid="deposit-submit"
variant="primary"
fill={true}
disabled={true}
>
{t('Deposit')}
</Button>
);
} else if (!selectedAsset) {
button = (
<Button
type="submit"
data-testid="deposit-submit"
variant="primary"
fill={true}
>
{t('Deposit')}
</Button>
);
} else if (approved) {
message = (
<>
<Icon name="tick" className="mr-2" />
<span>{t('Approved')}</span>
</>
);
button = (
<Button
type="submit"
data-testid="deposit-submit"
variant="primary"
fill={true}
>
{t('Deposit')}
</Button>
);
} else {
message = t(`Deposits of ${selectedAsset.symbol} not approved`);
button = (
<Button
onClick={onApproveClick}
data-testid="deposit-approve-submit"
variant="primary"
fill={true}
>
{t(`Approve ${selectedAsset.symbol}`)}
</Button>
);
}
const desiredChainId = useWeb3ConnectStore((store) => store.desiredChainId);
const submitText =
formState === 'approve'
? t(`Approve ${selectedAsset ? selectedAsset.symbol : ''}`)
: t('Deposit');
const invalidChain = isActive && chainId !== desiredChainId;
return (
<div className="flex flex-col gap-4">
{message && <p className="text-center">{message}</p>}
{button}
</div>
<>
{formState === 'approve' && (
<div className="mb-2">
<Notification
intent={Intent.Warning}
testId="approve-warning"
message={t(`Deposits of ${selectedAsset?.symbol} not approved`)}
/>
</div>
)}
{invalidChain && (
<div className="mb-2">
<Notification
intent={Intent.Danger}
testId="chain-error"
message={t(
`This app only works on ${getChainName(desiredChainId)}.`
)}
/>
</div>
)}
<Button
type="submit"
data-testid="deposit-submit"
variant={isActive ? 'primary' : 'default'}
fill={true}
disabled={invalidChain}
>
{submitText}
</Button>
</>
);
};
@@ -398,21 +428,20 @@ const UseButton = (props: UseButtonProps) => {
);
};
const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => {
const openDialog = useWeb3ConnectStore((state) => state.open);
const { isActive, connector } = useWeb3React();
const DisconnectEthereumButton = ({
onDisconnect,
}: {
onDisconnect: () => void;
}) => {
const { connector } = useWeb3React();
const [, , removeEagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT);
if (!isActive) {
return <UseButton onClick={openDialog}>{t('Connect')}</UseButton>;
}
return (
<UseButton
onClick={() => {
connector.deactivate();
clearAddress();
removeEagerConnector();
onDisconnect();
}}
data-testid="disconnect-ethereum-wallet"
>
@@ -420,3 +449,14 @@ const EthereumButton = ({ clearAddress }: { clearAddress: () => void }) => {
</UseButton>
);
};
const getFormState = (
selectedAsset: Asset | undefined,
isActive: boolean,
approved: boolean
) => {
if (!selectedAsset) return 'deposit';
if (!isActive) return 'deposit';
if (approved) return 'deposit';
return 'approve';
};
+8
View File
@@ -11,6 +11,7 @@ interface DepositLimitsProps {
deposited: BigNumber;
asset: Asset;
balance?: BigNumber;
allowance?: BigNumber;
}
export const DepositLimits = ({
@@ -18,6 +19,7 @@ export const DepositLimits = ({
deposited,
asset,
balance,
allowance,
}: DepositLimitsProps) => {
const limits = [
{
@@ -44,6 +46,12 @@ export const DepositLimits = ({
rawValue: max.minus(deposited),
value: compactNumber(max.minus(deposited), asset.decimals),
},
{
key: 'ALLOWANCE',
label: t('Approved'),
rawValue: allowance,
value: allowance ? compactNumber(allowance, asset.decimals) : '-',
},
];
return (
@@ -27,12 +27,13 @@ export const AssetProposalNotification = ({
</>
);
return (
<Notification
intent={Intent.Warning}
message={message}
testId="asset-proposal-notification"
className="mb-2"
/>
<div className="mb-2">
<Notification
intent={Intent.Warning}
message={message}
testId="asset-proposal-notification"
/>
</div>
);
}
@@ -34,7 +34,6 @@ export const MarketProposalNotification = ({
intent={Intent.Warning}
message={message}
testId="market-proposal-notification"
className="px-2 py-1"
/>
</div>
);
+3 -1
View File
@@ -10,7 +10,9 @@ const MIN_FRACTION_DIGITS = 2;
const MAX_FRACTION_DIGITS = 20;
export function toDecimal(numberOfDecimals: number) {
return 1 / Math.pow(10, numberOfDecimals);
return new BigNumber(1)
.dividedBy(Math.pow(10, numberOfDecimals))
.toString(10);
}
export function toBigNum(
@@ -1,6 +1,6 @@
import { t } from '../i18n';
export const validateAmount = (step: number, field: string) => {
export const validateAmount = (step: number | string, field: string) => {
const [, stepDecimals = ''] = String(step).split('.');
return (value: string) => {
+4
View File
@@ -2761,12 +2761,16 @@ export type PositionUpdate = {
__typename?: 'PositionUpdate';
/** Average entry price for this position */
averageEntryPrice: Scalars['String'];
/** The total amount of profit and loss that was not transferred due to loss socialisation */
lossSocializationAmount: Scalars['String'];
/** Market relating to this position */
marketId: Scalars['ID'];
/** Open volume (int64) */
openVolume: Scalars['String'];
/** The party holding this position */
partyId: Scalars['ID'];
/** Enum set if the position was closed out or orders were removed because party was distressed */
positionStatus: PositionStatus;
/** Realised Profit and Loss (int64) */
realisedPNL: Scalars['String'];
/** Unrealised Profit and Loss (int64) */
@@ -19,7 +19,6 @@ type NotificationProps = {
size?: ButtonSize;
};
testId?: string;
className?: string;
};
const getIcon = (intent: Intent): IconName => {
@@ -39,7 +38,6 @@ export const Notification = ({
title,
testId,
buttonProps,
className,
}: NotificationProps) => {
return (
<div
@@ -61,8 +59,7 @@ export const Notification = ({
intent === Intent.Warning,
'bg-vega-pink-300 dark:bg-vega-pink-650': intent === Intent.Danger,
},
'border rounded p-2 flex items-start gap-2.5 my-4',
className
'border rounded p-2 flex items-start gap-2.5'
)}
>
<div