feat(token): 1823 tranches service (#2742)

This commit is contained in:
Dexter Edwards 2023-02-24 14:53:30 +00:00 committed by GitHub
parent 56b5214dbf
commit 92ec7166bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 547 additions and 140020 deletions

View File

@ -1,32 +0,0 @@
name: Generate tranches
on:
schedule:
- cron: '0 */6 * * *'
jobs:
master:
name: Generate Queries
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
- name: Install root dependencies
run: yarn install
- name: Generate queries
run: node ./scripts/get-tranches.js
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 'chore: update tranches'
commit_options: '--no-verify --signoff'
skip_fetch: true
skip_checkout: true

View File

@ -1 +0,0 @@
[]

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
[]

View File

@ -1 +0,0 @@
[]

View File

@ -1 +0,0 @@
[]

View File

@ -1 +0,0 @@
[]

View File

@ -1 +0,0 @@
[]

View File

@ -12,6 +12,7 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -1,4 +1,3 @@
const connectPrompt = '[data-testid="eth-connect-prompt"]';
const connectButton = '[data-testid="connect-to-eth-btn"]';
context(
@ -18,7 +17,7 @@ context(
});
it('should have connect Eth wallet info', function () {
cy.get(connectPrompt).should('be.visible');
cy.get(connectButton).should('be.visible');
});
it('should have connect Eth wallet button', function () {

View File

@ -12,6 +12,7 @@ NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -15,6 +15,7 @@ NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
#Test configuration variables
CYPRESS_FAIRGROUND=false

View File

@ -9,3 +9,4 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz

View File

@ -10,3 +10,4 @@ NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz

View File

@ -6,3 +6,4 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz

View File

@ -7,3 +7,4 @@ NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz

View File

@ -10,3 +10,4 @@ NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz

View File

@ -1,17 +1,17 @@
import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useWeb3React } from '@web3-react/core';
import { useEffect } from 'react';
import { useAppState } from '../../contexts/app-state/app-state-context';
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 { useGetUserBalances } from '../../hooks/use-get-user-balances';
import { useBalances } from '../../lib/balances/balances-store';
import type { ReactElement } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
import { useTranches } from '../../lib/tranches/tranches-store';
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
import { useEthereumConfig } from '@vegaprotocol/web3';
interface BalanceManagerProps {
children: ReactElement;
@ -24,7 +24,13 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
const {
appState: { decimals },
} = useAppState();
const { updateBalances: updateStoreBalances } = useBalances();
const updateStoreBalances = useBalances((state) => state.updateBalances);
const setTranchesBalances = useBalances((state) => state.setTranchesBalances);
const getUserBalances = useGetUserBalances(account);
const userTrancheBalances = useUserTrancheBalances(account);
useEffect(() => {
setTranchesBalances(userTrancheBalances);
}, [setTranchesBalances, userTrancheBalances]);
const { config } = useEthereumConfig();
const numberOfConfirmations = config?.confirmations || 0;
@ -41,10 +47,10 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
numberOfConfirmations
);
const getUserTrancheBalances = useGetUserTrancheBalances(
account || '',
contracts?.vesting
);
const getTranches = useTranches((state) => state.getTranches);
useEffect(() => {
getTranches(decimals);
}, [decimals, getTranches]);
const getAssociationBreakdown = useGetAssociationBreakdown(
account || '',
contracts?.staking,
@ -54,50 +60,14 @@ export const BalanceManager = ({ children }: BalanceManagerProps) => {
// update balances on connect to Ethereum
useEffect(() => {
const updateBalances = async () => {
if (!account || !config) return;
try {
const [b, w, stats, a] = await Promise.all([
contracts.vesting.user_total_all_tranches(account),
contracts.token.balanceOf(account),
contracts.vesting.user_stats(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);
updateStoreBalances({
balanceFormatted: balance,
walletBalance,
lien,
allowance,
});
} catch (err) {
Sentry.captureException(err);
const balances = await getUserBalances();
if (balances) {
updateStoreBalances(balances);
}
};
updateBalances();
}, [
decimals,
contracts.token,
contracts.vesting,
account,
config,
updateStoreBalances,
]);
// This use effect hook is very expensive and is kept separate to prevent expensive reloading of data.
useEffect(() => {
if (account) {
getUserTrancheBalances();
}
}, [account, getUserTrancheBalances]);
}, [getUserBalances, updateStoreBalances]);
useEffect(() => {
if (account) {

View File

@ -6,27 +6,22 @@ import {
useAppState,
} from '../../contexts/app-state/app-state-context';
interface EthConnectPrompProps {
children?: React.ReactNode;
}
export const EthConnectPrompt = ({ children }: EthConnectPrompProps) => {
export const EthConnectPrompt = () => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
return (
<>
{children}
<Button
onClick={() =>
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
data-testid="connect-to-eth-btn"
>
{t('connectEthWallet')}
</Button>
</>
<Button
variant="primary"
onClick={() =>
appDispatch({
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
isOpen: true,
})
}
fill={true}
data-testid="connect-to-eth-btn"
>
{t('connectEthWallet')}
</Button>
);
};

View File

@ -58,6 +58,7 @@ const envName = windowOrDefault('NX_VEGA_ENV') ?? 'local';
export const ENV = {
// Environment
tranchesServiceUrl: windowOrDefault('NX_TRANCHES_SERVICE_URL'),
dsn: windowOrDefault('NX_SENTRY_DSN'),
urlConnect: TRUTHY.includes(windowOrDefault('NX_ETH_URL_CONNECT')),
explorerUrl: windowOrDefault('NX_VEGA_EXPLORER'),

View File

@ -1,4 +1,3 @@
import type { Tranche } from '@vegaprotocol/smart-contracts';
import React from 'react';
import type { BigNumber } from '../../lib/bignumber';
@ -19,21 +18,7 @@ export interface VegaKey {
meta: Array<{ key: string; value: string }> | null;
}
export interface UserTrancheBalance {
/** ID of tranche */
id: number;
/** Users vesting tokens on tranche */
locked: BigNumber;
/** Users vested tokens on tranche */
vested: BigNumber;
}
export interface AppState {
/** Array of tranche objects */
tranches: Tranche[] | null;
/** Number of decimal places of the VEGA token (18 on Mainnet, 5 on Testnet) */
decimals: number;
@ -52,9 +37,6 @@ export interface AppState {
/** Whether or not the connect to Ethereum wallet overlay is open */
ethConnectOverlay: boolean;
/** The error if one was thrown during retrieval of tranche data */
trancheError: Error | null;
/** Whether or not the mobile drawer is open. Only relevant on screens smaller than 960 */
drawerOpen: boolean;
@ -71,12 +53,10 @@ export enum AppStateActionType {
SET_TOKEN,
SET_ALLOWANCE,
REFRESH_BALANCES,
SET_TRANCHE_DATA,
SET_VEGA_WALLET_OVERLAY,
SET_VEGA_WALLET_MANAGE_OVERLAY,
SET_ETH_WALLET_OVERLAY,
SET_DRAWER,
SET_TRANCHE_ERROR,
REFRESH_ASSOCIATED_BALANCES,
SET_ASSOCIATION_BREAKDOWN,
SET_TRANSACTION_OVERLAY,
@ -90,14 +70,6 @@ export type AppStateAction =
totalSupply: BigNumber;
totalAssociated: BigNumber;
}
| {
type: AppStateActionType.SET_TRANCHE_DATA;
tranches: Tranche[];
}
| {
type: AppStateActionType.SET_TRANCHE_ERROR;
error: Error | null;
}
| {
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY;
isOpen: boolean;

View File

@ -14,11 +14,9 @@ const initialAppState: AppState = {
totalAssociated: new BigNumber(0),
decimals: 0,
totalSupply: new BigNumber(0),
tranches: null,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
ethConnectOverlay: false,
trancheError: null,
drawerOpen: false,
transactionOverlay: false,
bannerMessage: '',
@ -34,17 +32,6 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
totalAssociated: action.totalAssociated,
};
}
case AppStateActionType.SET_TRANCHE_DATA:
return {
...state,
tranches: action.tranches,
};
case AppStateActionType.SET_TRANCHE_ERROR: {
return {
...state,
trancheError: action.error,
};
}
case AppStateActionType.SET_VEGA_WALLET_OVERLAY: {
return {
...state,

View File

@ -1,666 +0,0 @@
import parseJSON from 'date-fns/parseJSON';
import { BigNumber } from '../../lib/bignumber';
import type { Tranche } from '@vegaprotocol/smart-contracts';
const json: Tranche[] = [
{
tranche_id: 1,
tranche_start: parseJSON('2022-03-05T00:00:00.000Z'),
tranche_end: parseJSON('2023-06-05T00:00:00.000Z'),
total_added: new BigNumber('3505372.53445'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('3505372.53445'),
deposits: [
{
amount: new BigNumber('187637.95'),
user: '0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0',
tx: '0xc4491908e3347b05f2394ac7e1006f573fe8cbc490a57cd1dadad70785e95024',
},
{
amount: new BigNumber('200000'),
user: '0x93b478148FF792B00076B7EdC89Db1FdE7772079',
tx: '0xcc8fba855a0d965044d4cc587701fe9ee9f5863852cede017382ffe02963d9b8',
},
{
amount: new BigNumber('21666.5743'),
user: '0xB523235B6c7C74DDB26b10E78bFb2d0Cb63Ae289',
tx: '0x8cc5159f3d665dd33a2bdac6e990cf1b65dc18b6b5e37a07b37dd54495a8d33c',
},
{
amount: new BigNumber('200000'),
user: '0x7227e17101E6C70F4dAfC7DDB77BB7D83DdfC1C8',
tx: '0x0cdef6868bd6b977362396fd06525e1e757e827659c5d75cd512db121947e6f7',
},
{
amount: new BigNumber('137998.56'),
user: '0x69eFc5642CfcCB1777bc663433640531F044D1F5',
tx: '0xaa9b3b39836a69f760f5d1d2fdba44ad348b27c8f31eb094ad6f779b39d7949c',
},
{
amount: new BigNumber('16249.93'),
user: '0x006B59DD3bC838A74476c0F4a33C1565831dA0DD',
tx: '0xda9bdc846300600c0d360edcaf4a5ed40fa2586ba7872f2bf68251a669adff0e',
},
],
withdrawals: [],
users: [
{
address: '0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0',
deposits: [
{
amount: new BigNumber('187637.95'),
user: '0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0',
tranche_id: 1,
tx: '0xc4491908e3347b05f2394ac7e1006f573fe8cbc490a57cd1dadad70785e95024',
},
],
withdrawals: [],
total_tokens: new BigNumber('187637.95'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('187637.95'),
},
{
address: '0x1A71e3ED1996CAbB91bB043f880CE963D601707e',
deposits: [
{
amount: new BigNumber('112323.67'),
user: '0x1A71e3ED1996CAbB91bB043f880CE963D601707e',
tranche_id: 1,
tx: '0xaa378d2d0d4d7b964a675bb19f19c4f7401deec6ce66b1bd98ad5b812026e53e',
},
],
withdrawals: [],
total_tokens: new BigNumber('112323.67'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('112323.67'),
},
],
},
{
tranche_id: 2,
tranche_start: parseJSON('2022-06-05T00:00:00.000Z'),
tranche_end: parseJSON('2023-12-05T00:00:00.000Z'),
total_added: new BigNumber('15464357.550320999700000001'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('15464357.550320999700000001'),
deposits: [
{
amount: new BigNumber('1'),
user: '0x7fff551249D223f723557a96a0e1a469C79cC934',
tx: '0x55fa59d71aac37428a5804442c48da677a4426a4e92447919a8199520ce20f53',
},
{
amount: new BigNumber('100000'),
user: '0xe20D4d4fFb165e4b9926467d82d03c0e9ab66D89',
tx: '0xabf2cc96c41c8b4f0cff8d8ff7448e241ff83e296a7b7dea79d9d9868f33b7ad',
},
{
amount: new BigNumber('500000'),
user: '0x5f01A497e4033E4812ba5D494bCBc2220cd510Ed',
tx: '0xe3a1a74378a42eace12c21c24889d8b0da6a3736ca7987720e70f59886caa5a3',
},
{
amount: new BigNumber('59228.95'),
user: '0x1d20f66eF3889aa48Bb4Badbbf993dE965BDb029',
tx: '0xf31eab593b86aac54f5fa9fe24bb26105ff52386be60bc24617dd362df7c6f15',
},
{
amount: new BigNumber('206374.1211'),
user: '0x1b979e8AE3BbbaF96Dd1bbbC0060b360A23f2EBE',
tx: '0x2983daa9acf6da5fba0debc2aaaaf47389b92aea0de0c27b20809fb69a63ce69',
},
{
amount: new BigNumber('200000'),
user: '0xe45993F39183E148bC35BA02ba8C289111181c0f',
tx: '0xbda7b70ab8ac629617e74daaaa5451b04d7830bc0da516e10d533efc2eef1530',
},
],
withdrawals: [
{
amount: new BigNumber('0'),
user: '0x4527F5A12bbbbb7c88c5863F8AB9a708928Fe702',
tx: '0xbaa8632cd162265ca46baaaad746ec3d283a474e344727854d962e057380a51',
},
],
users: [
{
address: '0x7fff551249D223f723557a96a0e1aCCCC79cC934',
deposits: [
{
amount: new BigNumber('1'),
user: '0x7fff551249D223f723557a96a0e1aCCCC79cC934',
tranche_id: 2,
tx: '0x55fa59d71aac37428a0000042c48da677a4426a4e92447919a8199520ce20f53',
},
],
withdrawals: [],
total_tokens: new BigNumber('1'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('1'),
},
{
address: '0xCc5CAFD3daA3bb2c1168521F35d1eBEB6cf7c051',
deposits: [
{
amount: new BigNumber('200000'),
user: '0xCc5CAFD3daA3bb2c1168521F35d1eBEB6cf7c051',
tranche_id: 2,
tx: '0x8168230eb08320a7a874ebeeea20f0def842b8059a2b05a036870e00ca624c88',
},
],
withdrawals: [],
total_tokens: new BigNumber('200000'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('200000'),
},
{
address: '0x9cd59376F896a5F2084232E386A65c17EEA4Fe9f',
deposits: [
{
amount: new BigNumber('200000'),
user: '0x9cd59376F896a5F2084232E386A65c17EEA4Fe9f',
tranche_id: 2,
tx: '0x2eb27449e08a245314faaaaf0d93fafc70733586495f18cf2fb69ef979459efd',
},
],
withdrawals: [],
total_tokens: new BigNumber('200000'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('200000'),
},
{
address: '0xe45993F39183E148bC35BA02ba8aaaa807981caa',
deposits: [
{
amount: new BigNumber('200000'),
user: '0xe45993F39183E148bC35BA02ba8aaaa07981caa',
tranche_id: 2,
tx: '0xbda7b70ab8ac629617e74d33575451b04d7830bc0da516e10d533efc2eef1530',
},
],
withdrawals: [],
total_tokens: new BigNumber('200000'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('200000'),
},
],
},
{
tranche_id: 3,
tranche_start: parseJSON('2021-11-05T00:00:00.000Z'),
tranche_end: parseJSON('2023-05-05T00:00:00.000Z'),
total_added: new BigNumber('14597706.0446472999'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('14445316.74229298796336861823365303'),
deposits: [
{
amount: new BigNumber('129284.449'),
user: '0x26100B2C8168Cb0A6c869a5698265086A3Dfeaaa',
tx: '0xcc67a776a2e3b48864470aaa9e0940c1814663b4fde6df60c1a099a636dcae79',
},
{
amount: new BigNumber('1151405.093'),
user: '0x777Ec2e2beaB6a63c1E763D0dc4120AF60BEe39F',
tx: '0x1237d45a40aacc5dff7f8f69e8a4c0536fd0460b915de576bd3f0fdf5892c0a4',
},
{
amount: new BigNumber('1151073.595'),
user: '0x5CD0Ec63687588817044794bF15d4e37991efAB3',
tx: '0xb4eaca7d8abeaf7e1d9d28b1f1fa09fc1933af0e11bff4b0120dcef7d2dfc64d',
},
{
amount: new BigNumber('54034.5'),
user: '0xc01F2E57554Bb392384feCA6a54c8E3A3Ca94E42',
tx: '0xf33289a2a73a65b132accdddd600a8d2c7556c2d80784d5f8d4eea1ecacf93cc',
},
{
amount: new BigNumber('115049.22'),
user: '0x01a8055A97b461b58ba8e37cd349721FeAe77A8D',
tx: '0xf650c3599b24d01dfa1f3e19b22f870ff8ac8dfac529893f0b22d548c3535eda',
},
],
withdrawals: [
{
amount: new BigNumber('0'),
user: '0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b',
tx: '0x1af894d1f9ce5ea79aa52d180fbff5f30b8b456e43b76ca5d7d73366e422ea37',
},
],
users: [
{
address: '0x26100B2C8168Cb0A6c869a5698265086A3DfeF98',
deposits: [
{
amount: new BigNumber('129284.449'),
user: '0x26100B2C8168Cb0A6c869a5698265086A3DfeF98',
tranche_id: 3,
tx: '0xcc67a776a2e3b48864470eff9e0940c1814663b4fde6df60c1a099a636dcae79',
},
],
withdrawals: [],
total_tokens: new BigNumber('129284.449'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('129284.449'),
},
{
address: '0xd4632B682228Db5f38E2283869AEe8c29ee6Eec8',
deposits: [
{
amount: new BigNumber('44499.2'),
user: '0xd4632B682228Db5f38E2283869AEe8c29ee6Eec8',
tranche_id: 3,
tx: '0x57019840d1ce05e0ef65c45801d6699e5eb1032bd8ad56493594d4866996ea82',
},
],
withdrawals: [],
total_tokens: new BigNumber('44499.2'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('44499.2'),
},
{
address: '0xe2E6F37cb1f1980418012BF69f43910d6Bc73e73',
deposits: [
{
amount: new BigNumber('66748.8'),
user: '0xe2E6F37cb1f1980418012BF69f43910d6Bc73e73',
tranche_id: 3,
tx: '0xd257a3aacd5bdf86cbea0ed3db3697f55dff9092129db6baedacaf00b50af936',
},
],
withdrawals: [],
total_tokens: new BigNumber('66748.8'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('66748.8'),
},
{
address: '0xc01F2E57554Bb392384feCA6a54c8E3A3Ca94E42',
deposits: [
{
amount: new BigNumber('54034.5'),
user: '0xc01F2E57554Bb392384feCA6a54c8E3A3Ca94E42',
tranche_id: 3,
tx: '0xf33289a2a73a65b132accdddd600a8d2c7556c2d80784d5f8d4eea1ecacf93cc',
},
],
withdrawals: [],
total_tokens: new BigNumber('54034.5'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('54034.5'),
},
],
},
{
tranche_id: 4,
tranche_start: parseJSON('2021-10-05T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('5198082.8647159303'),
total_removed: new BigNumber('12706.1452878164044708'),
locked_amount: new BigNumber('4849328.20502099651595959714823613'),
deposits: [
{
amount: new BigNumber('110499.5291'),
user: '0xa8679b60612Fb2e19d68964326CA02dCe6a08D08',
tx: '0x9d3432b818054796489848c415af5c523acb16c1540e5865010baf71964c03a7',
},
{
amount: new BigNumber('331498.5873'),
user: '0x39fEc2e2beaB6a63c1E763D0dc4120AF60BEe39F',
tx: '0xdf5c6e44ef0763e785721802704e5fd186fa3964302a566899027447d0dda57b',
},
{
amount: new BigNumber('27624.032275'),
user: '0xF5Fb27b912D987B5b6e02A1B1BE0C1F0740E2c6f',
tx: '0x1860d39ae3e9ad710da9e2a9bf9606eedc1b176fca673473d6854ea91ed8beb5',
},
{
amount: new BigNumber('73666.3527333333'),
user: '0x2895059cB5a492BEd58D1fB22713006EfaD465eA',
tx: '0x260619e2129cc58ae03f6b4ecfc5a6eeab29b5998de69875accb3cb945beed04',
},
],
withdrawals: [
{
amount: new BigNumber('1290.014571009862016'),
user: '0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75',
tx: '0x637c3648ce941a77e08e741f981806a5d56275db6d280f041902386ae8567d06',
},
{
amount: new BigNumber('5197.621879605058623'),
user: '0xafa64cCa337eFEE0AD827F6C2684e69275226e90',
tx: '0xe8b8c1a38d3ae809dcaae54a11adeeba0f46be03924e91d8f251f3f2d48c3553',
},
{
amount: new BigNumber('376.2625308599198808'),
user: '0x1dD2718fd01d05C9F50Fce8Bb723A4C7483A1E15',
tx: '0x78af737235f1123bb1c6a607a0b4b7a3c5ed6859a4e8912f45bb7c812724b1de',
},
{
amount: new BigNumber('4162.301372742020875'),
user: '0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75',
tx: '0x85c7d9979f0e3a3660dc5952732eaf9414f7c28a1c00a9a10f449843f91d85e0',
},
{
amount: new BigNumber('1679.944933599543076'),
user: '0x9058e12e2F32cB1cD4D3123359963D77786477FC',
tx: '0xd92083f2e90e84cb70ef27ac410ed1144200c41b1714fb54d29f5f23ca086ecb',
},
],
users: [
{
address: '0xa8679b60612Fb2e19d68964326CA02dCe6a08D08',
deposits: [
{
amount: new BigNumber('110499.5291'),
user: '0xa8679b60612Fb2e19d68964326CA02dCe6a08D08',
tranche_id: 4,
tx: '0x9d3432b818054796489848c415af5c523acb16c1540e5865010baf71964c03a7',
},
],
withdrawals: [],
total_tokens: new BigNumber('110499.5291'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('110499.5291'),
},
{
address: '0x8767d65677Cabaa2050b764AEf40610f2f9796F5',
deposits: [
{
amount: new BigNumber('1104995.291'),
user: '0x8767d65677Cabaa2050b764AEf40610f2f9796F5',
tranche_id: 4,
tx: '0xc6298f52c173a837abd051ed810b01eb5731be307376b73df6e31b4de39d0122',
},
],
withdrawals: [],
total_tokens: new BigNumber('1104995.291'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('1104995.291'),
},
{
address: '0x91715128a71c9C734CDC20E5EdaaeA02E72e422E',
deposits: [
{
amount: new BigNumber('165749.29365'),
user: '0x91715128a71c9C734CDC20E5EdEEeA02E72e422E',
tranche_id: 4,
tx: '0xf828cea685a0689f27446f02d3376c3afa4398182d3b5bf1c81e949f5965d1c1',
},
],
withdrawals: [],
total_tokens: new BigNumber('165749.29365'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('165749.29365'),
},
{
address: '0x2895059cB5a492BEd58D1fB22713006EfaD465eA',
deposits: [
{
amount: new BigNumber('73666.3527333333'),
user: '0x2895059cB5a492BEd58D1fB22713006EfaD465eA',
tranche_id: 4,
tx: '0x260619e2129cc58ae03f6b4ecfc5a6eeab29b5998de69875accb3cb945beed04',
},
],
withdrawals: [],
total_tokens: new BigNumber('73666.3527333333'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('73666.3527333333'),
},
],
},
{
tranche_id: 23,
tranche_start: parseJSON('2022-04-30T00:00:00.000Z'),
tranche_end: parseJSON('2022-04-30T00:00:00.000Z'),
total_added: new BigNumber('10833.29'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('10833.29'),
deposits: [
{
amount: new BigNumber('10833.29'),
user: '0xF3359E5B89f7804c8c9283781Aaaa33BBd979c9D',
tx: '0x65f904ef34d6992b52f449a709d7a24411a501fd07f90aaa9255eacc994bc229',
},
],
withdrawals: [],
users: [
{
address: '0xF3359E5B89f7804c8c9283781Aaaa33BBd979c9D',
deposits: [
{
amount: new BigNumber('10833.29'),
user: '0xF3359E5B89f7804c8c9283781Aaaa33BBd979c9D',
tranche_id: 23,
tx: '0x65f904ef34d6992b52f449a709d7a24411a501fd07f90aaa9255eacc994bc229',
},
],
withdrawals: [],
total_tokens: new BigNumber('10833.29'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('10833.29'),
},
],
},
{
tranche_id: 24,
tranche_start: parseJSON('2022-09-26T00:00:00.000Z'),
tranche_end: parseJSON('2022-09-26T00:00:00.000Z'),
total_added: new BigNumber('16249.93'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('16249.93'),
deposits: [
{
amount: new BigNumber('16249.93'),
user: '0xcE96670971ec2E1E79D0d96688adbA2FfD6F6C7f',
tx: '0x3dbd991b7914986505d89a7c1562278dffffa2f5f444bdbf5d6bbd838e3d8d5d',
},
],
withdrawals: [],
users: [
{
address: '0xcE96670971ec2E1E79D0d96688adbA2FfD6F6C7f',
deposits: [
{
amount: new BigNumber('16249.93'),
user: '0xcE96670971ec2E1E79D0d96688adbA2FfD6F6C7f',
tranche_id: 24,
tx: '0x3dbd991b7914986505d89a7c1562278dffffa2f5f444bdbf5d6bbd838e3d8d5d',
},
],
withdrawals: [],
total_tokens: new BigNumber('16249.93'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('16249.93'),
},
],
},
{
tranche_id: 25,
tranche_start: parseJSON('2022-02-10T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('0'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('0'),
deposits: [],
withdrawals: [],
users: [],
},
{
tranche_id: 26,
tranche_start: parseJSON('2022-02-04T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('135173.4239508'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('135173.4239508'),
deposits: [
{
amount: new BigNumber('135173.4239508'),
user: '0xc90eA4d8D214D548221EE3622a8BE1D61f7077A2',
tx: '0x123fb1e293a8246b92d85a47c8d33def9fb6468c7cbb70f1754d78914b12dbf8',
},
],
withdrawals: [],
users: [
{
address: '0x222eA4d8D214D548221EE3622a8BE1D61f7077A2',
deposits: [
{
amount: new BigNumber('135173.4239508'),
user: '0x222eA4d8D214D548221EE3622a8BE1D61f7077A2',
tranche_id: 26,
tx: '0x9fafb1e293a8246b92d85a47c8d33def9fb6468c7cbb70f1754d78914b12dbf8',
},
],
withdrawals: [],
total_tokens: new BigNumber('135173.4239508'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('135173.4239508'),
},
],
},
{
tranche_id: 27,
tranche_start: parseJSON('2022-05-09T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('32499.86'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('32499.86'),
deposits: [
{
amount: new BigNumber('32499.86'),
user: '0x1E7c4E57A1dc4dD4bBE81b833e3E437f69619DaB',
tx: '0x25a3dd4852ce8ac1c15fe42123cfab14e4bf8a5c1cb97167cb4d6fe20bd319ae',
},
],
withdrawals: [],
users: [
{
address: '0x3E7c4E57A1dc4dD4bBE81bbEFBe3Eaaaf69619Da9',
deposits: [
{
amount: new BigNumber('32499.86'),
user: '0x3E7c4E57A1dc4dD4bBE81bbEFBe3Eaaaf69619Da9',
tranche_id: 27,
tx: '0x75a3dd4852ce8ac1c15fe42ff6cfab1455bf8a5c1cb97167cb4d6fe20bd319ae',
},
],
withdrawals: [],
total_tokens: new BigNumber('32499.86'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('32499.86'),
},
],
},
{
tranche_id: 28,
tranche_start: parseJSON('2022-04-30T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('10833.29'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('10833.29'),
deposits: [
{
amount: new BigNumber('10833.29'),
user: '0xF3359E5B89f7804c8c9283781Aaa133BBd979c9D',
tx: '0x166d235eff44e7bcc63597c0d6e698552e4440fe90ec7cf07a1d510c275cf3e0',
},
],
withdrawals: [],
users: [
{
address: '0x12349E5B89f7804c8c9283781A23133BBd979c22',
deposits: [
{
amount: new BigNumber('10833.29'),
user: '0x12349E5B89f7804c8c9283781A23133BBd979c22',
tranche_id: 28,
tx: '0x166d235eff44e7baa6359712345698552e6150fe90ec7cf07a1d510c275cf3e0',
},
],
withdrawals: [],
total_tokens: new BigNumber('10833.29'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('10833.29'),
},
],
},
{
tranche_id: 29,
tranche_start: parseJSON('2022-09-26T00:00:00.000Z'),
tranche_end: parseJSON('2023-04-05T00:00:00.000Z'),
total_added: new BigNumber('16249.93'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('16249.93'),
deposits: [
{
amount: new BigNumber('16249.93'),
user: '0xcE96670971ec2E1E79D0d12388adbA2FfD6F6C7f',
tx: '0xa770ab2d05e6c81be46b73ac2326e81a111da4ce55a2d8544bd95b48ad530674',
},
],
withdrawals: [],
users: [
{
address: '0xcE96670971ec2E1E79D0d96688adbA2FfD6F6C7f',
deposits: [
{
amount: new BigNumber('16249.93'),
user: '0xcE96670971ec212379D0d12388adbA2Ffc6F6C7f',
tranche_id: 29,
tx: '0xa220ab2d05e6c81be12373ac2326e81a912da4ce55a2d8544bd95b48ad530674',
},
],
withdrawals: [],
total_tokens: new BigNumber('16249.93'),
withdrawn_tokens: new BigNumber('0'),
remaining_tokens: new BigNumber('16249.93'),
},
],
},
{
tranche_id: 30,
tranche_start: parseJSON('2021-11-01T00:00:00.000Z'),
tranche_end: parseJSON('2022-05-01T00:00:00.000Z'),
total_added: new BigNumber('0'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('0'),
deposits: [],
withdrawals: [],
users: [],
},
{
tranche_id: 31,
tranche_start: parseJSON('2022-02-01T00:00:00.000Z'),
tranche_end: parseJSON('2022-08-01T00:00:00.000Z'),
total_added: new BigNumber('0'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('0'),
deposits: [],
withdrawals: [],
users: [],
},
{
tranche_id: 32,
tranche_start: parseJSON('2022-05-01T00:00:00.000Z'),
tranche_end: parseJSON('2022-11-01T00:00:00.000Z'),
total_added: new BigNumber('0'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('0'),
deposits: [],
withdrawals: [],
users: [],
},
{
tranche_id: 33,
tranche_start: parseJSON('2022-11-01T00:00:00.000Z'),
tranche_end: parseJSON('2023-05-01T00:00:00.000Z'),
total_added: new BigNumber('0'),
total_removed: new BigNumber('0'),
locked_amount: new BigNumber('0'),
deposits: [],
withdrawals: [],
users: [],
},
];
export default json;

View File

@ -1,21 +0,0 @@
import React from 'react';
import mock from './tranches-mock';
import type { Tranche } from '@vegaprotocol/smart-contracts';
export function useTranches() {
const [tranches, setTranches] = React.useState<Tranche[] | null>(null);
const [error, setError] = React.useState<string | null>(null);
React.useEffect(() => {
const run = async () => {
try {
setTranches(mock);
} catch (err) {
setError((err as Error).message);
}
};
run();
}, []);
return { tranches, error };
}

View File

@ -0,0 +1,41 @@
import { useCallback } from 'react';
import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useContracts } from '../contexts/contracts/contracts-context';
import { useAppState } from '../contexts/app-state/app-state-context';
import { useEthereumConfig } from '@vegaprotocol/web3';
export const useGetUserBalances = (account: string | undefined) => {
const { token, vesting } = useContracts();
const { config } = useEthereumConfig();
const {
appState: { decimals },
} = useAppState();
return useCallback(async () => {
if (!account || !config) return;
try {
const [b, w, stats, a] = await Promise.all([
vesting.user_total_all_tranches(account),
token.balanceOf(account),
vesting.user_stats(account),
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);
return {
balanceFormatted: balance,
walletBalance,
lien,
allowance,
balance,
};
} catch (err) {
Sentry.captureException(err);
return null;
}
}, [account, config, decimals, token, vesting]);
};

View File

@ -1,70 +0,0 @@
import React from 'react';
import * as Sentry from '@sentry/react';
import type { TokenVesting } from '@vegaprotocol/smart-contracts';
import {
AppStateActionType,
useAppState,
} from '../contexts/app-state/app-state-context';
import { BigNumber } from '../lib/bignumber';
import { useTranches } from './use-tranches';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useBalances } from '../lib/balances/balances-store';
export const useGetUserTrancheBalances = (
address: string,
vesting: TokenVesting
) => {
const {
appState: { decimals },
appDispatch,
} = useAppState();
const { setTranchesBalances } = useBalances();
const { tranches } = useTranches();
return React.useCallback(async () => {
appDispatch({
type: AppStateActionType.SET_TRANCHE_ERROR,
error: null,
});
try {
if (!tranches) {
return;
}
const userTranches = tranches?.filter((t) =>
t.users.some(
({ address: a }) =>
a && address && a.toLowerCase() === address.toLowerCase()
)
);
const trancheIds = [0, ...userTranches.map((t) => t.tranche_id)];
const promises = trancheIds.map(async (tId) => {
const [t, v] = await Promise.all([
vesting.get_tranche_balance(address, tId),
vesting.get_vested_for_tranche(address, tId),
]);
const total = toBigNum(t, decimals);
const vested = toBigNum(v, decimals);
return {
id: tId,
locked: tId === 0 ? total : total.minus(vested),
vested: tId === 0 ? new BigNumber(0) : vested,
};
});
const trancheBalances = await Promise.all(promises);
setTranchesBalances(trancheBalances);
appDispatch({
type: AppStateActionType.SET_TRANCHE_DATA,
tranches,
});
} catch (e) {
Sentry.captureException(e);
appDispatch({
type: AppStateActionType.SET_TRANCHE_ERROR,
error: e as Error,
});
}
}, [appDispatch, tranches, setTranchesBalances, address, vesting, decimals]);
};

View File

@ -1,70 +0,0 @@
import { useFetch } from '@vegaprotocol/react-helpers';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import React, { useEffect } from 'react';
import type { Networks } from '@vegaprotocol/environment';
import { useEnvironment } from '@vegaprotocol/environment';
import { BigNumber } from '../lib/bignumber';
const TRANCHES_URLS: { [N in Networks]: string } = {
MAINNET: 'https://static.vega.xyz/assets/mainnet-tranches.json',
MIRROR: 'https://static.vega.xyz/assets/mirror-tranches.json',
TESTNET: 'https://static.vega.xyz/assets/testnet-tranches.json',
SANDBOX: 'https://static.vega.xyz/assets/sandbox-tranches.json',
STAGNET1: 'https://static.vega.xyz/assets/stagnet1-tranches.json',
STAGNET3: 'https://static.vega.xyz/assets/stagnet3-tranches.json',
DEVNET: 'https://static.vega.xyz/assets/devnet-tranches.json',
CUSTOM: 'https://static.vega.xyz/assets/testnet-tranches.json',
};
export function useTranches() {
const { VEGA_ENV } = useEnvironment();
const [tranches, setTranches] = React.useState<Tranche[] | null>(null);
const url = React.useMemo(() => TRANCHES_URLS[VEGA_ENV], [VEGA_ENV]);
const {
state: { data, loading, error },
} = useFetch<Tranche[] | null>(url);
useEffect(() => {
const processedTrances = data
?.map((t) => ({
...t,
tranche_start: new Date(t.tranche_start),
tranche_end: new Date(t.tranche_end),
total_added: new BigNumber(t.total_added),
total_removed: new BigNumber(t.total_removed),
locked_amount: new BigNumber(t.locked_amount),
deposits: t.deposits.map((d) => ({
...d,
amount: new BigNumber(d.amount),
})),
withdrawals: t.withdrawals.map((w) => ({
...w,
amount: new BigNumber(w.amount),
})),
users: t.users.map((u) => ({
...u,
// @ts-ignore - types are incorrect in the SDK lib
deposits: u.deposits.map((d) => ({
...d,
amount: new BigNumber(d.amount),
})),
// @ts-ignore - types are incorrect in the SDK lib
withdrawals: u.withdrawals.map((w) => ({
...w,
amount: new BigNumber(w.amount),
})),
total_tokens: new BigNumber(u.total_tokens),
withdrawn_tokens: new BigNumber(u.withdrawn_tokens),
remaining_tokens: new BigNumber(u.remaining_tokens),
})),
}))
.sort((a: Tranche, b: Tranche) => a.tranche_id - b.tranche_id);
setTranches(processedTrances ? processedTrances : null);
}, [data]);
return {
tranches,
loading,
error,
};
}

View File

@ -1,21 +0,0 @@
import type { Tranche } from '@vegaprotocol/smart-contracts';
import { BigNumber } from '../bignumber';
export function generateTranche(id: number): Tranche {
return {
tranche_id: id,
tranche_start: new Date(),
tranche_end: new Date(),
total_added: new BigNumber(0),
total_removed: new BigNumber(0),
locked_amount: new BigNumber(0),
deposits: [],
withdrawals: [],
users: [],
};
}
export function generateTranches(count = 1) {
return new Array(count).fill(null).map((_, i) => generateTranche(i));
}

View File

@ -1,6 +1,11 @@
import BigNumber from 'bignumber.js';
import { create } from 'zustand';
import type { UserTrancheBalance } from '../../contexts/app-state/app-state-context';
interface UserTrancheBalance {
id: number;
locked: BigNumber;
vested: BigNumber;
}
export interface AssociationBreakdown {
stakingAssociations: { [vegaKey: string]: BigNumber };
@ -13,8 +18,8 @@ export type BalancesStore = {
balanceFormatted: BigNumber;
totalVestedBalance: BigNumber;
totalLockedBalance: BigNumber;
walletBalance: BigNumber;
trancheBalances: UserTrancheBalance[];
walletBalance: BigNumber;
lien: BigNumber;
walletAssociatedBalance: BigNumber;
vestingAssociatedBalance: BigNumber;
@ -38,8 +43,8 @@ export const useBalances = create<BalancesStore>((set) => ({
stakingAssociations: {},
vestingAssociations: {},
},
trancheBalances: [],
allowance: new BigNumber(0),
trancheBalances: [],
totalVestedBalance: new BigNumber(0),
totalLockedBalance: new BigNumber(0),
balanceFormatted: new BigNumber(0),

View File

@ -0,0 +1,80 @@
import { toBigNum } from '@vegaprotocol/react-helpers';
import type { TrancheServiceResponse } from '@vegaprotocol/smart-contracts';
import type BigNumber from 'bignumber.js';
import create from 'zustand';
import { ENV } from '../../config';
export interface Tranche {
tranche_id: number;
tranche_start: Date;
tranche_end: Date;
total_added: BigNumber;
total_removed: BigNumber;
locked_amount: BigNumber;
users: string[];
}
const URL = `${ENV.tranchesServiceUrl}/tranches/stats`;
export interface UserTrancheBalance {
/** ID of tranche */
id: number;
/** Users vesting tokens on tranche */
locked: BigNumber;
/** Users vested tokens on tranche */
vested: BigNumber;
}
export type TranchesStore = {
tranches: Tranche[] | null;
loading: boolean;
error: Error | null;
getTranches: (decimals: number) => void;
};
const secondsToDate = (seconds: number) => new Date(seconds * 1000);
export const useTranches = create<TranchesStore>((set) => ({
tranches: null,
loading: false,
error: null,
getTranches: async (decimals: number) => {
set({ loading: true, error: null });
try {
const res = await fetch(URL);
const data = (await res.json()) as TrancheServiceResponse;
const now = Math.round(Date.now() / 1000);
const tranches = Object.values(data.tranches)
?.map((t) => {
const tranche_progress =
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress;
return {
tranche_id: t.tranche_id,
tranche_start: secondsToDate(t.cliff_start),
tranche_end: secondsToDate(t.cliff_start + t.duration),
total_added: toBigNum(t.initial_balance, decimals),
total_removed: toBigNum(t.initial_balance, decimals).minus(
toBigNum(t.current_balance, decimals)
),
locked_amount: toBigNum(t.initial_balance, decimals).times(
lockedDecimal
),
users: t.users,
};
})
.sort((a, b) => a.tranche_id - b.tranche_id);
set({
tranches,
});
} catch (e) {
set({ error: e as unknown as Error });
} finally {
set({
loading: false,
});
}
},
}));

View File

@ -4,7 +4,6 @@ import { format } from 'date-fns';
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { useContracts } from '../../contexts/contracts/contracts-context';
@ -22,6 +21,7 @@ import { TrancheNotFound } from './tranche-not-found';
import { UntargetedClaim } from './untargeted-claim';
import { Verifying } from './verifying';
import type { ClaimAction, ClaimState } from './claim-reducer';
import type { Tranche } from '../../lib/tranches/tranches-store';
interface ClaimFlowProps {
state: ClaimState;

View File

@ -1,8 +1,8 @@
import { useTranslation } from 'react-i18next';
import { format } from 'date-fns';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import { DATE_FORMAT_LONG } from '../../lib/date-formats';
import type { Tranche } from '../../lib/tranches/tranches-store';
interface ClaimInfoProps {
tranche: Tranche;

View File

@ -1,7 +1,5 @@
import React from 'react';
import { useAppState } from '../../contexts/app-state/app-state-context';
import { useContracts } from '../../contexts/contracts/contracts-context';
import { useGetUserTrancheBalances } from '../../hooks/use-get-user-tranche-balances';
import { useRefreshBalances } from '../../hooks/use-refresh-balances';
import { useSearchParams } from '../../hooks/use-search-params';
import { ClaimError } from './claim-error';
@ -13,7 +11,7 @@ import {
initialClaimState,
} from './claim-reducer';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import type { Tranche } from '../../lib/tranches/tranches-store';
const Claim = ({
address,
@ -23,10 +21,8 @@ const Claim = ({
tranches: Tranche[];
}) => {
const params = useSearchParams();
const { vesting } = useContracts();
const { appState } = useAppState();
const [state, dispatch] = React.useReducer(claimReducer, initialClaimState);
const getUserTrancheBalances = useGetUserTrancheBalances(address, vesting);
const refreshBalances = useRefreshBalances(address);
React.useEffect(() => {
@ -48,10 +44,9 @@ const Claim = ({
// If the claim has been committed refetch the new VEGA balance
React.useEffect(() => {
if (state.claimStatus === ClaimStatus.Finished && address) {
getUserTrancheBalances();
refreshBalances();
}
}, [address, getUserTrancheBalances, refreshBalances, state.claimStatus]);
}, [address, refreshBalances, state.claimStatus]);
if (state.error) {
return <ClaimError />;

View File

@ -5,7 +5,7 @@ import { EthConnectPrompt } from '../../components/eth-connect-prompt';
import { Heading } from '../../components/heading';
import { SplashLoader } from '../../components/splash-loader';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { useTranches } from '../../hooks/use-tranches';
import { useTranches } from '../../lib/tranches/tranches-store';
import type { RouteChildProps } from '..';
import Claim from './claim';
import { ClaimRestricted } from './claim-restricted';
@ -16,7 +16,11 @@ const ClaimIndex = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
const { account } = useWeb3React();
const { tranches, loading, error } = useTranches();
const { tranches, loading, error } = useTranches((state) => ({
loading: state.loading,
error: state.error,
tranches: state.tranches,
}));
if (loading || !tranches) {
return (
@ -38,13 +42,14 @@ const ClaimIndex = ({ name }: RouteChildProps) => {
if (!account) {
content = (
<EthConnectPrompt>
<>
<p data-testid="eth-connect-prompt">
{t(
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
)}
</p>
</EthConnectPrompt>
<EthConnectPrompt />
</>
);
} else {
content = isRestricted() ? (

View File

@ -51,11 +51,9 @@ const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'),
decimals: 18,
totalSupply: new BigNumber(65000000),
tranches: null,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
ethConnectOverlay: false,
trancheError: null,
drawerOpen: false,
transactionOverlay: false,
bannerMessage: '',

View File

@ -14,11 +14,9 @@ const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'),
decimals: 18,
totalSupply: mockTotalSupply,
tranches: null,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
ethConnectOverlay: false,
trancheError: null,
drawerOpen: false,
transactionOverlay: false,
bannerMessage: '',

View File

@ -1,56 +1,81 @@
import { Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useBalances } from '../../../lib/balances/balances-store';
import React from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useNavigate, useOutletContext } from 'react-router-dom';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { AddLockedTokenAddress } from '../../../components/add-locked-token';
import { formatNumber } from '../../../lib/format-number';
import { truncateMiddle } from '../../../lib/truncate-middle';
import Routes from '../../routes';
import type { RedemptionState } from '../redemption-reducer';
import { Tranche0Table, TrancheTable } from '../tranche-table';
import { VestingTable } from './vesting-table';
import { useTranches } from '../../../lib/tranches/tranches-store';
import { useGetUserBalances } from '../../../hooks/use-get-user-balances';
import BigNumber from 'bignumber.js';
import { useUserTrancheBalances } from '../hooks';
interface UserBalances {
balanceFormatted: BigNumber;
walletBalance: BigNumber;
lien: BigNumber;
allowance: BigNumber;
balance: BigNumber;
}
export const RedemptionInformation = () => {
const { state, account } = useOutletContext<{
state: RedemptionState;
account: string;
}>();
const navigate = useNavigate();
const { t } = useTranslation();
const {
balanceFormatted,
lien,
totalVestedBalance,
totalLockedBalance,
trancheBalances,
} = useBalances();
const { userTranches } = state;
const filteredTranches = React.useMemo(
const navigate = useNavigate();
const tranches = useTranches((state) => state.tranches);
const { address } = useParams<{ address: string }>();
const [userBalances, setUserBalances] = useState<null | UserBalances>();
const getUsersBalances = useGetUserBalances(address);
useEffect(() => {
getUsersBalances().then(setUserBalances);
}, [getUsersBalances]);
const userTrancheBalances = useUserTrancheBalances(address);
const filteredTranches = useMemo(
() =>
userTranches.filter((tr) => {
const balance = trancheBalances.find(
tranches?.filter((tr) => {
const balance = userTrancheBalances.find(
({ id }) => id.toString() === tr.tranche_id.toString()
);
return (
balance?.locked.isGreaterThan(0) || balance?.vested.isGreaterThan(0)
);
}),
[trancheBalances, userTranches]
}) || [],
[userTrancheBalances, tranches]
);
const { totalLocked, totalVested } = useMemo(() => {
return {
totalLocked: BigNumber.sum.apply(null, [
new BigNumber(0),
...userTrancheBalances.map(({ locked }) => locked),
]),
totalVested: BigNumber.sum.apply(null, [
new BigNumber(0),
...userTrancheBalances.map(({ vested }) => vested),
]),
};
}, [userTrancheBalances]);
const zeroTranche = React.useMemo(() => {
const zeroTranche = trancheBalances.find((t) => t.id === 0);
const zeroTranche = useMemo(() => {
const zeroTranche = userTrancheBalances.find((t) => t.id === 0);
if (zeroTranche && zeroTranche.locked.isGreaterThan(0)) {
return zeroTranche;
}
return null;
}, [trancheBalances]);
}, [userTrancheBalances]);
if (!filteredTranches.length) {
const isAccountValid = useMemo(
() => address && address.length === 42 && address.startsWith('0x'),
[address]
);
if (!isAccountValid || !address) {
return <div>The address {address} is not a valid Ethereum address</div>;
}
if (!filteredTranches.length || !userBalances) {
return (
<section data-testid="redemption-page">
<div className="mb-8">
@ -79,17 +104,17 @@ export const RedemptionInformation = () => {
{t(
'{{address}} has {{balance}} VEGA tokens in {{tranches}} tranches of the vesting contract.',
{
address: truncateMiddle(account),
balance: formatNumber(balanceFormatted),
address: truncateMiddle(address),
balance: formatNumber(userBalances.balanceFormatted),
tranches: filteredTranches.length,
}
)}
</p>
<div className="mb-24">
<VestingTable
associated={lien}
locked={totalLockedBalance}
vested={totalVestedBalance}
associated={userBalances.lien}
locked={totalLocked}
vested={totalVested}
/>
</div>
{filteredTranches.length ? <h2>{t('Tranche breakdown')}</h2> : null}
@ -98,7 +123,7 @@ export const RedemptionInformation = () => {
trancheId={0}
total={
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
trancheBalances.find(
userTrancheBalances.find(
({ id }) => id.toString() === zeroTranche.id.toString()
)!.locked
}
@ -108,22 +133,25 @@ export const RedemptionInformation = () => {
<TrancheTable
key={tr.tranche_id}
tranche={tr}
lien={lien}
lien={userBalances.lien}
locked={
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
trancheBalances.find(
userTrancheBalances.find(
({ id }) => id.toString() === tr.tranche_id.toString()
)!.locked
}
vested={
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
trancheBalances.find(
userTrancheBalances.find(
({ id }) => id.toString() === tr.tranche_id.toString()
)!.vested
}
totalVested={totalVestedBalance}
totalLocked={totalLockedBalance}
onClick={() => navigate(`/vesting/${tr.tranche_id}`)}
totalVested={totalVested}
totalLocked={totalLocked}
onClick={() =>
navigate(`${Routes.REDEEM}/${address}/${tr.tranche_id}`)
}
address={address}
/>
))}
<Callout
@ -132,7 +160,7 @@ export const RedemptionInformation = () => {
intent={Intent.Warning}
>
<p>{t('Find out more about Staking.')}</p>
<Link to="/staking" className="underline text-white">
<Link to={Routes.VALIDATORS} className="underline text-white">
{t('Stake VEGA tokens')}
</Link>
</Callout>

View File

@ -12,7 +12,7 @@ export interface VestingTableProps {
}
const VestingTableIndicatorSquare = ({ colour }: { colour: string }) => (
<span className={`bg-${colour} inline-block h-12 w-12 mr-4`} />
<span className={`bg-${colour} inline-block h-4 w-4 mr-1`} />
);
export const VestingTable = ({
@ -65,17 +65,17 @@ export const VestingTable = ({
</KeyValueTable>
<div className="flex border-white border">
<div
className="bg-vega-pink h-16"
className="bg-vega-pink h-4"
style={{ flex: lockedPercentage.toNumber() }}
/>
<div
className="bg-vega-green h-16"
className="bg-vega-green h-4"
style={{ flex: vestedPercentage.toNumber() }}
/>
</div>
<div className="flex h-4 mt-4">
<div className="flex h-1 mt-1">
<div
className="bg-vega-yellow h-4"
className="bg-vega-yellow h-1"
style={{ flex: stakedPercentage.toNumber() }}
/>
<div

View File

@ -0,0 +1,54 @@
import { useCallback, useEffect, useState } from 'react';
import { useTranches } from '../../lib/tranches/tranches-store';
import { useContracts } from '../../contexts/contracts/contracts-context';
import BigNumber from 'bignumber.js';
import { toBigNum } from '@vegaprotocol/react-helpers';
import { useAppState } from '../../contexts/app-state/app-state-context';
export const useUserTrancheBalances = (address: string | undefined) => {
const [userTrancheBalances, setUserTrancheBalances] = useState<
{
id: number;
locked: BigNumber;
vested: BigNumber;
}[]
>([]);
const {
appState: { decimals },
} = useAppState();
const { vesting } = useContracts();
const tranches = useTranches((state) => state.tranches);
const loadUserTrancheBalances = useCallback(async () => {
if (!address) return;
const userTranches =
tranches?.filter((t) =>
t.users.some(
(a) => a && address && a.toLowerCase() === address.toLowerCase()
)
) || [];
const trancheIds = [0, ...userTranches.map((t) => t.tranche_id)];
const promises = trancheIds.map(async (tId) => {
const [t, v] = await Promise.all([
vesting.get_tranche_balance(address, tId),
vesting.get_vested_for_tranche(address, tId),
]);
const total = toBigNum(t, decimals);
const vested = toBigNum(v, decimals);
return {
id: tId,
locked: tId === 0 ? total : total.minus(vested),
vested: tId === 0 ? new BigNumber(0) : vested,
};
});
const trancheBalances = await Promise.all(promises);
setUserTrancheBalances(trancheBalances);
}, [address, decimals, tranches, vesting]);
useEffect(() => {
loadUserTrancheBalances();
}, [loadUserTrancheBalances]);
return userTrancheBalances;
};

View File

@ -1,38 +0,0 @@
import type { Tranche } from '@vegaprotocol/smart-contracts';
import type { BigNumber } from '../../lib/bignumber';
export interface TrancheBalance {
id: number;
locked: BigNumber;
vested: BigNumber;
}
export interface RedemptionState {
userTranches: Tranche[];
}
export const initialRedemptionState: RedemptionState = {
userTranches: [],
};
export enum RedemptionActionType {
SET_USER_TRANCHES,
}
export type RedemptionAction = {
type: RedemptionActionType.SET_USER_TRANCHES;
userTranches: Tranche[];
};
export function redemptionReducer(
state: RedemptionState,
action: RedemptionAction
): RedemptionState {
switch (action.type) {
case RedemptionActionType.SET_USER_TRANCHES:
return {
...state,
userTranches: action.userTranches,
};
}
}

View File

@ -1,51 +1,62 @@
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import {
Button,
Callout,
FormGroup,
Input,
InputError,
Intent,
Splash,
} from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useCallback } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { Outlet, useNavigate, useParams } from 'react-router-dom';
import { EthConnectPrompt } from '../../components/eth-connect-prompt';
import { SplashLoader } from '../../components/splash-loader';
import { useTranches } from '../../hooks/use-tranches';
import { useBalances } from '../../lib/balances/balances-store';
import { useTranches } from '../../lib/tranches/tranches-store';
import RoutesConfig from '../routes';
import {
initialRedemptionState,
RedemptionActionType,
redemptionReducer,
} from './redemption-reducer';
interface FormFields {
address: string;
}
const RedemptionRouter = () => {
const { address } = useParams<{ address: string }>();
const navigate = useNavigate();
const { t } = useTranslation();
const [state, dispatch] = React.useReducer(
redemptionReducer,
initialRedemptionState
);
const { trancheBalances } = useBalances();
const { account } = useWeb3React();
const { tranches, error, loading } = useTranches();
React.useEffect(() => {
const run = (address: string) => {
const userTranches = tranches?.filter((t) =>
t.users.some(
({ address: a }) => a.toLowerCase() === address.toLowerCase()
)
);
if (userTranches) {
dispatch({
type: RedemptionActionType.SET_USER_TRANCHES,
userTranches,
});
const validatePubkey = useCallback(
(value: string) => {
if (!value.startsWith('0x')) {
return t('Address must begin with 0x');
} else if (value.length !== 42) {
return t('Pubkey must be 42 characters in length');
} else if (Number.isNaN(+value)) {
return t('Pubkey must be be valid hex');
}
};
return true;
},
[t]
);
const { account } = useWeb3React();
const { tranches, error, loading } = useTranches((state) => ({
loading: state.loading,
error: state.error,
tranches: state.tranches,
}));
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormFields>();
if (account) {
run(account);
}
}, [account, tranches]);
const onSubmit = useCallback(
(fields: FormFields) => {
navigate(`${RoutesConfig.REDEEM}/${fields.address}`);
},
[navigate]
);
if (error) {
return (
@ -63,30 +74,48 @@ const RedemptionRouter = () => {
);
}
if (!account) {
if (!address) {
return (
<EthConnectPrompt>
<p data-testid="eth-connect-prompt">
{t(
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
)}
</p>
</EthConnectPrompt>
<div className="max-w-md">
{!account ? (
<EthConnectPrompt />
) : (
<Button
fill={true}
variant="primary"
onClick={() => navigate(`${RoutesConfig.REDEEM}/${account}`)}
>
{t('View connected Eth Wallet')}
</Button>
)}
<p className="py-4 flex justify-center">{t('OR')}</p>
<form
onSubmit={handleSubmit(onSubmit)}
data-testid="view-connector-form"
>
<FormGroup label={'View Ethereum as user:'} labelFor="address">
<Input
{...register('address', {
required: t('Required'),
validate: validatePubkey,
})}
id="address"
data-testid="address"
type="text"
/>
{errors.address?.message && (
<InputError intent="danger">{errors.address.message}</InputError>
)}
</FormGroup>
<Button data-testid="connect" type="submit" fill={true}>
{t('View Ethereum user')}
</Button>
</form>
</div>
);
}
if (!trancheBalances.length) {
return (
<>
<Callout>
<p>{t('You have no VEGA tokens currently vesting.')}</p>
</Callout>
<Link to={RoutesConfig.SUPPLY}>{t('viewAllTranches')}</Link>
</>
);
}
return <Outlet context={{ state, account }} />;
return <Outlet />;
};
export default RedemptionRouter;

View File

@ -33,10 +33,10 @@ export const TrancheItem = ({
}: TrancheItemProps) => {
const { t } = useTranslation();
const labelClasses =
'inline-block uppercase bg-white text-black py-4 px-8 font-mono';
'inline-block uppercase bg-white text-black py-1 px-2 font-mono';
return (
<section data-testid="tranche-item" className="mb-40">
<section data-testid="tranche-item" className="mb-8">
<div className="flex border-b">
{link ? (
<Link to={link}>

View File

@ -8,6 +8,7 @@ import { formatNumber } from '../../lib/format-number';
import Routes from '../routes';
import { TrancheItem } from './tranche-item';
import { Button } from '@vegaprotocol/ui-toolkit';
import { useWeb3React } from '@web3-react/core';
export interface TrancheTableProps {
tranche: {
@ -22,6 +23,7 @@ export interface TrancheTableProps {
totalLocked: BigNumber;
onClick: () => void;
disabled?: boolean;
address: string | null;
}
export const Tranche0Table = ({
@ -65,9 +67,11 @@ export const TrancheTable = ({
totalVested,
totalLocked,
disabled = false,
address,
}: TrancheTableProps) => {
const { t } = useTranslation();
const total = vested.plus(locked);
const { account: connectedAddress } = useWeb3React();
const trancheFullyLocked =
tranche.tranche_start.getTime() > new Date().getTime();
const totalAllTranches = totalVested.plus(totalLocked);
@ -93,13 +97,20 @@ export const TrancheTable = ({
amount: reduceAmount,
}}
components={{
stakeLink: <Link to={`/staking`} />,
disassociateLink: <Link to={`/staking/disassociate`} />,
stakeLink: <Link className="underline" to={Routes.VALIDATORS} />,
disassociateLink: (
<Link className="underline" to={Routes.DISASSOCIATE} />
),
}}
/>
</div>
);
} else if (!trancheFullyLocked && redeemable) {
} else if (
!trancheFullyLocked &&
redeemable &&
connectedAddress &&
address === connectedAddress
) {
message = (
<Button onClick={onClick} disabled={disabled}>
{t('Redeem unlocked VEGA from tranche {{id}}', {

View File

@ -1,7 +1,7 @@
import { useBalances } from '../../../lib/balances/balances-store';
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useParams, useOutletContext } from 'react-router-dom';
import { Link, useParams } from 'react-router-dom';
import { TransactionCallout } from '../../../components/transaction-callout';
import { useContracts } from '../../../contexts/contracts/contracts-context';
@ -9,32 +9,36 @@ import {
TransactionActionType,
TxState,
} from '../../../hooks/transaction-reducer';
import { useGetUserTrancheBalances } from '../../../hooks/use-get-user-tranche-balances';
import { useRefreshBalances } from '../../../hooks/use-refresh-balances';
import { useTransaction } from '../../../hooks/use-transaction';
import { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import Routes from '../../routes';
import type { RedemptionState } from '../redemption-reducer';
import { TrancheTable } from '../tranche-table';
import { useTranches } from '../../../lib/tranches/tranches-store';
import { useWeb3React } from '@web3-react/core';
import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
import { useUserTrancheBalances } from '../hooks';
import { useAppState } from '../../../contexts/app-state/app-state-context';
export const RedeemFromTranche = () => {
const { state, address } = useOutletContext<{
state: RedemptionState;
address: string;
}>();
const { account: address } = useWeb3React();
const { vesting } = useContracts();
const { t } = useTranslation();
const { lien, totalVestedBalance, trancheBalances, totalLockedBalance } =
useBalances();
const refreshBalances = useRefreshBalances(address);
const getUserTrancheBalances = useGetUserTrancheBalances(address, vesting);
const {
appState: { decimals },
} = useAppState();
const { lien, totalVestedBalance, totalLockedBalance } = useBalances();
const refreshBalances = useRefreshBalances(address || '');
const { tranches, getTranches } = useTranches((state) => ({
tranches: state.tranches,
getTranches: state.getTranches,
}));
const { id } = useParams<{ id: string }>();
const numberId = Number(id);
const { userTranches } = state;
const tranche = React.useMemo(
() => userTranches.find(({ tranche_id }) => tranche_id === numberId),
[numberId, userTranches]
() => tranches?.find(({ tranche_id }) => tranche_id === numberId) || null,
[numberId, tranches]
);
const {
state: txState,
@ -42,7 +46,7 @@ export const RedeemFromTranche = () => {
dispatch: txDispatch,
} = useTransaction(() => vesting.withdraw_from_tranche(numberId));
const { token } = useContracts();
const trancheBalances = useUserTrancheBalances(address || '');
const redeemedAmount = React.useMemo(() => {
return (
trancheBalances.find(({ id: bId }) => bId.toString() === id?.toString())
@ -55,10 +59,10 @@ export const RedeemFromTranche = () => {
// If the claim has been committed refetch the new VEGA balance
React.useEffect(() => {
if (txState.txState === TxState.Complete && address) {
getUserTrancheBalances();
refreshBalances();
getTranches(decimals);
}
}, [address, getUserTrancheBalances, refreshBalances, txState.txState]);
}, [address, decimals, getTranches, refreshBalances, txState.txState]);
const trancheBalance = React.useMemo(() => {
return trancheBalances.find(
@ -66,6 +70,10 @@ export const RedeemFromTranche = () => {
);
}, [id, trancheBalances]);
if (!address) {
return <EthConnectPrompt />;
}
if (
!tranche ||
tranche.total_removed.isEqualTo(tranche.total_added) ||
@ -147,6 +155,7 @@ export const RedeemFromTranche = () => {
locked={trancheBalance.locked}
vested={trancheBalance.vested}
onClick={perform}
address={address || ''}
/>
)}
</section>

View File

@ -300,12 +300,17 @@ const routerConfig = [
element: <LazyRedemption name="Vesting" />,
children: [
{
index: true,
element: <LazyRedemptionIndex />,
},
{
path: ':id',
element: <LazyRedemptionTranche />,
path: ':address',
children: [
{
index: true,
element: <LazyRedemptionIndex />,
},
{
path: ':id',
element: <LazyRedemptionTranche />,
},
],
},
],
},

View File

@ -16,10 +16,11 @@ export const StakingWalletsContainer = ({
if (!account) {
return (
<EthConnectPrompt>
<>
<p>{t('associateInfo1')}</p>
<p>{t('associateInfo2')}</p>
</EthConnectPrompt>
<EthConnectPrompt />
</>
);
}

View File

@ -1,35 +1,34 @@
import { BigNumber } from '../../../lib/bignumber';
import { sumCirculatingTokens } from './token-details-circulating';
import type { Tranche } from '@vegaprotocol/smart-contracts';
it('It sums some easy tranches correctly', () => {
const tranches: Partial<Tranche>[] = [
{ total_added: new BigNumber('100'), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber('100'), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber('100'), locked_amount: new BigNumber(0) },
const tranches = [
{ total_added: new BigNumber(100), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber(100), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber(100), locked_amount: new BigNumber(0) },
];
const result = sumCirculatingTokens(tranches as Tranche[]);
const result = sumCirculatingTokens(tranches);
expect(result.toString()).toEqual('300');
});
it('It sums some longer tranches correctly', () => {
const tranches: Partial<Tranche>[] = [
const tranches = [
{
total_added: new BigNumber('10000000000'),
total_added: new BigNumber(10000000000),
locked_amount: new BigNumber(0),
},
{ total_added: new BigNumber('20'), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber('3000'), locked_amount: new BigNumber(3020) },
{ total_added: new BigNumber(20), locked_amount: new BigNumber(0) },
{ total_added: new BigNumber(3000), locked_amount: new BigNumber(3020) },
];
const result = sumCirculatingTokens(tranches as Tranche[]);
const result = sumCirculatingTokens(tranches);
expect(result.toString()).toEqual('10000000000');
});
it('Handles null tranche array', () => {
const tranches = null;
const result = sumCirculatingTokens(tranches as unknown as Tranche[]);
const result = sumCirculatingTokens(tranches);
expect(result.toString()).toEqual('0');
});

View File

@ -1,6 +1,6 @@
import { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import type { Tranche } from '../../../lib/tranches/tranches-store';
/**
* Add together the circulating tokens from all tranches
@ -9,7 +9,9 @@ import type { Tranche } from '@vegaprotocol/smart-contracts';
* @param decimals decimal places for the formatted result
* @return The total circulating tokens from all tranches
*/
export function sumCirculatingTokens(tranches: Tranche[] | null): BigNumber {
export function sumCirculatingTokens(
tranches: { total_added: BigNumber; locked_amount: BigNumber }[] | null
): BigNumber {
let totalCirculating: BigNumber = new BigNumber(0);
tranches?.forEach(

View File

@ -7,7 +7,7 @@ import {
KeyValueTableRow,
RoundedWrapper,
} from '@vegaprotocol/ui-toolkit';
import { useTranches } from '../../../hooks/use-tranches';
import { useTranches } from '../../../lib/tranches/tranches-store';
import type { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import { TokenDetailsCirculating } from './token-details-circulating';
@ -25,7 +25,11 @@ export const TokenDetails = ({
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const { tranches, loading, error } = useTranches();
const { tranches, loading, error } = useTranches((state) => ({
loading: state.loading,
error: state.error,
tranches: state.tranches,
}));
const { config } = useEthereumConfig();
const { token } = useContracts();

View File

@ -4,14 +4,18 @@ import { Outlet } from 'react-router-dom';
import { Heading } from '../../components/heading';
import { SplashLoader } from '../../components/splash-loader';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { useTranches } from '../../hooks/use-tranches';
import type { RouteChildProps } from '..';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useTranches } from '../../lib/tranches/tranches-store';
const TrancheRouter = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
const { tranches, error, loading } = useTranches();
const { tranches, error, loading } = useTranches((state) => ({
loading: state.loading,
error: state.error,
tranches: state.tranches,
}));
if (!tranches || loading) {
return (

View File

@ -1,48 +1,32 @@
import type { Tranche as ITranche } from '@vegaprotocol/smart-contracts';
import { Link } from '@vegaprotocol/ui-toolkit';
import {
KeyValueTable,
KeyValueTableRow,
Link,
RoundedWrapper,
} from '@vegaprotocol/ui-toolkit';
import { Link as RouterLink } from 'react-router-dom';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router';
import { Navigate } from 'react-router-dom';
import { formatNumber } from '@vegaprotocol/react-helpers';
import { useOutletContext } from 'react-router-dom';
import { useEnvironment } from '@vegaprotocol/environment';
import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number';
import { TrancheItem } from '../redemption/tranche-item';
import Routes from '../routes';
import { TrancheLabel } from './tranche-label';
const TrancheProgressContents = ({
children,
}: {
children: React.ReactNode;
}) => (
<div className="flex justify-between gap-4 font-mono py-2 px-4">
{children}
</div>
);
import { useTranches } from '../../lib/tranches/tranches-store';
export const Tranche = () => {
const tranches = useOutletContext<ITranche[]>();
const tranches = useTranches((state) => state.tranches);
const { ETHERSCAN_URL } = useEnvironment();
const { t } = useTranslation();
const { trancheId } = useParams<{ trancheId: string }>();
const { trancheId } = useParams<{ trancheId: string; address: string }>();
const { chainId } = useWeb3React();
const tranche = tranches.find(
const tranche = tranches?.find(
(tranche) => trancheId && parseInt(trancheId) === tranche.tranche_id
);
const lockedData = React.useMemo(() => {
if (!tranche) return null;
const locked = tranche.locked_amount.div(tranche.total_added);
return {
locked,
unlocked: new BigNumber(1).minus(locked),
};
}, [tranche]);
if (!tranche) {
return <Navigate to={Routes.NOT_FOUND} />;
}
@ -67,33 +51,36 @@ export const Tranche = () => {
</div>
<h2>{t('Holders')}</h2>
{tranche.users.length ? (
<ul role="list">
{tranche.users.map((user, i) => {
const unlocked = user.remaining_tokens.times(
lockedData?.unlocked || 0
);
const locked = user.remaining_tokens.times(lockedData?.locked || 0);
return (
<li className="pb-4" key={i}>
<Link
title={t('View on Etherscan (opens in a new tab)')}
href={`${ETHERSCAN_URL}/tx/${user.address}`}
target="_blank"
>
{user.address}
</Link>
<TrancheProgressContents>
<span>{t('Locked')}</span>
<span>{t('Unlocked')}</span>
</TrancheProgressContents>
<TrancheProgressContents>
<span>{formatNumber(locked)}</span>
<span>{formatNumber(unlocked)}</span>
</TrancheProgressContents>
</li>
);
})}
</ul>
<RoundedWrapper>
<KeyValueTable>
<KeyValueTableRow>
<h1>{t('Ethereum Address')}</h1>
<h1>{t('View tranche data')}</h1>
</KeyValueTableRow>
{tranche.users.map((user) => (
<KeyValueTableRow key={user}>
{
<Link
title={t('View on Etherscan (opens in a new tab)')}
href={`${ETHERSCAN_URL}/address/${user}`}
target="_blank"
>
{user}
</Link>
}
{
<RouterLink
className="underline"
title={t('View vesting information')}
to={`${Routes.REDEEM}/${user}`}
>
{t('View vesting information')}
</RouterLink>
}
</KeyValueTableRow>
))}
</KeyValueTable>
</RoundedWrapper>
) : (
<p>{t('No users')}</p>
)}

View File

@ -1,5 +1,3 @@
import { useOutletContext } from 'react-router-dom';
import type { Tranche } from '@vegaprotocol/smart-contracts';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -10,6 +8,8 @@ import { TrancheLabel } from './tranche-label';
import { VestingChart } from './vesting-chart';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { useEthereumConfig } from '@vegaprotocol/web3';
import type { Tranche } from '../../lib/tranches/tranches-store';
import { useTranches } from '../../lib/tranches/tranches-store';
const trancheMinimum = 10;
@ -17,7 +17,7 @@ const shouldShowTranche = (t: Tranche) =>
!t.total_added.isLessThanOrEqualTo(trancheMinimum);
export const Tranches = () => {
const tranches = useOutletContext<Tranche[]>();
const tranches = useTranches((state) => state.tranches);
const [showAll, setShowAll] = React.useState<boolean>(false);
const { t } = useTranslation();
const { chainId } = useWeb3React();
@ -38,25 +38,24 @@ export const Tranches = () => {
<ul role="list">
{(showAll ? tranches : filteredTranches).map((tranche) => {
return (
<React.Fragment key={tranche.tranche_id}>
<TrancheItem
link={`${tranche.tranche_id}`}
tranche={tranche}
locked={tranche.locked_amount}
unlocked={tranche.total_added.minus(tranche.locked_amount)}
total={tranche.total_added}
secondaryHeader={
<TrancheLabel chainId={chainId} id={tranche.tranche_id} />
}
/>
</React.Fragment>
<TrancheItem
key={tranche.tranche_id}
link={`${tranche.tranche_id}`}
tranche={tranche}
locked={tranche.locked_amount}
unlocked={tranche.total_added.minus(tranche.locked_amount)}
total={tranche.total_added}
secondaryHeader={
<TrancheLabel chainId={chainId} id={tranche.tranche_id} />
}
/>
);
})}
</ul>
) : (
<p>{t('No tranches')}</p>
)}
<section className="text-center mt-32">
<section className="text-center mt-4">
<ButtonLink onClick={() => setShowAll(!showAll)}>
{showAll
? t(

View File

@ -2,51 +2,15 @@ import type BigNumber from 'bignumber.js';
export interface Tranche {
tranche_id: number;
tranche_start: Date;
tranche_end: Date;
total_added: BigNumber;
total_removed: BigNumber;
locked_amount: BigNumber;
deposits: Array<TrancheDeposit>;
withdrawals: Array<TrancheWithdrawal>;
users: Array<TrancheUser>;
users: string[];
initial_balance: number;
current_balance: number;
cliff_start: number;
duration: number;
}
export interface TrancheDeposit {
amount: BigNumber;
user: string;
tx: string;
}
export interface TrancheWithdrawal {
amount: BigNumber;
user: string;
tx: string;
}
export interface TrancheUser {
address: string;
deposits: Array<{
amount: BigNumber;
user: string;
tx: string;
tranche_id: number;
}>;
withdrawals: Array<{
amount: BigNumber;
user: string;
tx: string;
tranche_id: number;
}>;
total_tokens: BigNumber;
withdrawn_tokens: BigNumber;
remaining_tokens: BigNumber;
}
export enum TrancheEvents {
Created = 'Tranche_Created',
BalanceAdded = 'Tranche_Balance_Added',
BalanceRemoved = 'Tranche_Balance_Removed',
export interface TrancheServiceResponse {
tranches: Tranche[];
}
export interface IVegaClaimData {
@ -67,9 +31,3 @@ export interface IClaimTokenParams {
signature: IVegaClaimSignature;
country: string | null;
}
export interface EpochDetails {
id: string;
startSeconds: BigNumber;
endSeconds: BigNumber;
}

View File

@ -1,986 +0,0 @@
const ethers = require('ethers');
const BigNumber = require('bignumber.js');
const uniq = require('lodash/uniq');
const fs = require('fs');
const MAX_ATTEMPTS = 5;
const CONFIG = [
{
env: 'mainnet',
contract: '0x23d1bfe8fa50a167816fbd79d7932577c06011f4',
provider: 'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
startBlock: 12834524,
},
{
env: 'testnet',
contract: '0xe2deBB240b43EDfEBc9c38B67c0894B9A92Bf07c',
provider: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
startBlock: 11340808,
},
{
env: 'stagnet3',
contract: '0x9F10cBeEf03A564Fb914c2010c0Cd55E9BB11406',
provider: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
startBlock: 11790009,
},
{
env: 'devnet',
contract: '0xd1216AAb948f5FC706Df73df6d71c64CcaA8550a',
provider: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
startBlock: 11790003,
},
];
const vestingAbi = [
{
inputs: [
{
internalType: 'address',
name: 'token_v1_address',
type: 'address',
},
{
internalType: 'address',
name: 'token_v2_address',
type: 'address',
},
{
internalType: 'address[]',
name: 'old_addresses',
type: 'address[]',
},
{
internalType: 'address[]',
name: 'new_addresses',
type: 'address[]',
},
],
stateMutability: 'nonpayable',
type: 'constructor',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'new_controller',
type: 'address',
},
],
name: 'Controller_Set',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'issuer',
type: 'address',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'Issuer_Permitted',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'issuer',
type: 'address',
},
],
name: 'Issuer_Revoked',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'user',
type: 'address',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
indexed: true,
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'Stake_Deposited',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'user',
type: 'address',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
indexed: true,
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'Stake_Removed',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'from',
type: 'address',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
indexed: true,
internalType: 'address',
name: 'to',
type: 'address',
},
{
indexed: true,
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'Stake_Transferred',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'user',
type: 'address',
},
{
indexed: true,
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'Tranche_Balance_Added',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'user',
type: 'address',
},
{
indexed: true,
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
indexed: false,
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'Tranche_Balance_Removed',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
indexed: false,
internalType: 'uint256',
name: 'cliff_start',
type: 'uint256',
},
{
indexed: false,
internalType: 'uint256',
name: 'duration',
type: 'uint256',
},
],
name: 'Tranche_Created',
type: 'event',
},
{
inputs: [],
name: 'accuracy_scale',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
name: 'address_migration',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
internalType: 'address',
name: 'target',
type: 'address',
},
],
name: 'assisted_withdraw_from_tranche',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'controller',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'cliff_start',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'duration',
type: 'uint256',
},
],
name: 'create_tranche',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'default_tranche_id',
outputs: [
{
internalType: 'uint8',
name: '',
type: 'uint8',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'user',
type: 'address',
},
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
],
name: 'get_tranche_balance',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'user',
type: 'address',
},
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
],
name: 'get_vested_for_tranche',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'user',
type: 'address',
},
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'issue_into_tranche',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'user',
type: 'address',
},
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'move_into_tranche',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'issuer',
type: 'address',
},
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
],
name: 'permit_issuer',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
name: 'permitted_issuance',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'remove_stake',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'issuer',
type: 'address',
},
],
name: 'revoke_issuer',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'new_controller',
type: 'address',
},
],
name: 'set_controller',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'target',
type: 'address',
},
{
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'stake_balance',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'amount',
type: 'uint256',
},
{
internalType: 'bytes32',
name: 'vega_public_key',
type: 'bytes32',
},
],
name: 'stake_tokens',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'staking_token',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'total_locked',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'total_staked',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'tranche_count',
outputs: [
{
internalType: 'uint8',
name: '',
type: 'uint8',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint8',
name: '',
type: 'uint8',
},
],
name: 'tranches',
outputs: [
{
internalType: 'uint256',
name: 'cliff_start',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'duration',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
name: 'user_stats',
outputs: [
{
internalType: 'uint256',
name: 'total_in_all_tranches',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'lien',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'user',
type: 'address',
},
],
name: 'user_total_all_tranches',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'v1_address',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
name: 'v1_migrated',
outputs: [
{
internalType: 'bool',
name: '',
type: 'bool',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'v2_address',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint8',
name: 'tranche_id',
type: 'uint8',
},
],
name: 'withdraw_from_tranche',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
];
function addDecimal(value, decimals) {
return value.dividedBy(Math.pow(10, decimals)).decimalPlaces(decimals);
}
function createUserTransactions(events, decimals) {
return events.map((event) => {
return {
amount: addDecimal(
new BigNumber(event.args?.amount.toString()),
decimals
),
user: event.args?.user,
tranche_id: event.args?.tranche_id,
tx: event.transactionHash,
};
});
}
function getUsersInTranche(
balanceAddedEvents,
balanceRemovedEvents,
addresses,
decimals
) {
return addresses.map((address) => {
const userDeposits = balanceAddedEvents.filter(
(event) => event.args?.user === address
);
const userWithdraws = balanceRemovedEvents.filter(
(event) => event.args?.user === address
);
const deposits = createUserTransactions(userDeposits, decimals);
const withdrawals = createUserTransactions(userWithdraws, decimals);
const total_tokens = deposits.reduce(
(pre, cur) => pre.plus(cur.amount),
new BigNumber(0)
);
const withdrawn_tokens = withdrawals.reduce(
(pre, cur) => pre.plus(cur.amount),
new BigNumber(0)
);
const remaining_tokens = total_tokens.minus(withdrawn_tokens);
return {
address,
deposits,
withdrawals,
total_tokens,
withdrawn_tokens,
remaining_tokens,
};
});
}
function sumFromEvents(events, decimals) {
const amounts = events.map((e) =>
addDecimal(new BigNumber(e.args?.amount.toString()), decimals)
);
// Start with a 0 so if there are none there is no NaN
return BigNumber.sum.apply(null, [new BigNumber(0), ...amounts]);
}
function getLockedAmount(totalAdded, cliffStart, trancheDuration) {
let amount = new BigNumber(0);
const ts = Math.round(new Date().getTime() / 1000);
const tranche_progress = (ts - cliffStart) / trancheDuration;
if (tranche_progress < 0) {
amount = totalAdded;
} else if (tranche_progress < 1) {
amount = totalAdded.times(1 - tranche_progress);
}
return amount;
}
function createTransactions(events, decimals) {
return events.map((event) => {
return {
amount: addDecimal(
new BigNumber(event.args?.amount.toString()),
decimals
),
user: event.args?.user,
tx: event.transactionHash,
};
});
}
function getTranchesFromHistory(
createEvents,
addEvents,
removeEvents,
decimals
) {
return createEvents.map((event) => {
const tranche_id = event.args?.tranche_id;
const balanceAddedEvents = addEvents.filter(
(e) =>
e.event === 'Tranche_Balance_Added' && e.args?.tranche_id === tranche_id
);
const balanceRemovedEvents = removeEvents.filter(
(e) =>
e.event === 'Tranche_Balance_Removed' &&
e.args?.tranche_id === tranche_id
);
//get tranche start and end dates
const tranche_duration = event.args?.duration;
const cliff_start = event.args?.cliff_start;
const tranche_start = new Date(cliff_start.mul(1000).toNumber());
const tranche_end = new Date(
cliff_start.add(tranche_duration).mul(1000).toNumber()
);
// get added and removed values
const total_added = sumFromEvents(balanceAddedEvents, decimals);
const total_removed = sumFromEvents(balanceRemovedEvents, decimals);
// get locked amount
const locked_amount = getLockedAmount(
total_added,
cliff_start,
tranche_duration
);
// get all deposits and withdrawals
const deposits = createTransactions(balanceAddedEvents, decimals);
const withdrawals = createTransactions(balanceRemovedEvents, decimals);
// get all users
const uniqueAddresses = uniq(
balanceAddedEvents.map((event) => event.args?.user)
);
const users = getUsersInTranche(
balanceAddedEvents,
balanceRemovedEvents,
uniqueAddresses,
decimals
);
return {
tranche_id: parseInt(tranche_id),
tranche_start,
tranche_end,
total_added,
total_removed,
locked_amount,
deposits,
withdrawals,
users,
};
});
}
const getBatched = async (filter, contract, provider, startblock) => {
const batchSize = 100000;
const currentBlockNumber = await provider.getBlockNumber();
const batches = Math.ceil(currentBlockNumber / batchSize);
const startIndex = Math.floor(startblock / batchSize);
console.log(
`Processing batches of size ${batches} starting at batch ${startIndex}`
);
let res = [];
for (let index = startIndex; index < batches; index++) {
let events = [];
let attempts = 1;
// Sometimes the queries fail for timeout even on small batch sizes. Some basic retry logic.
while (attempts < MAX_ATTEMPTS) {
try {
events = await contract.queryFilter(
filter,
index * batchSize,
(index + 1) * batchSize - 1
);
break;
} catch {
console.log('retry batch', index);
++attempts;
if (attempts >= MAX_ATTEMPTS) {
throw new Error('Could not get in 4 attempts');
}
}
}
res = [...events, ...res];
console.log(
`Processed blocks ${index * batchSize} to ${
(index + 1) * batchSize - 1
} as part of batch ${index}`
);
}
return res;
};
const run = async (c) => {
const provider = new ethers.providers.JsonRpcProvider({
url: c.provider,
timeout: 1000 * 60 * 1000,
});
const contract = new ethers.Contract(c.contract, vestingAbi, provider);
const [created, added, removed] = await Promise.all([
getBatched(
contract.filters.Tranche_Created(),
contract,
provider,
c.startBlock
),
getBatched(
contract.filters.Tranche_Balance_Added(),
contract,
provider,
c.startBlock
),
getBatched(
contract.filters.Tranche_Balance_Removed(),
contract,
provider,
c.startBlock
),
]);
fs.writeFileSync(
`./apps/static/src/assets/${c.env}-tranches.json`,
JSON.stringify(
getTranchesFromHistory(created, added, removed, 18),
null,
2
),
{ encoding: 'UTF-8' }
);
};
CONFIG.forEach((c) => run(c));