diff --git a/assets/png/marketing/market-order-preview.png b/assets/png/marketing/market-order-preview.png index 0f47fb1e4b..c36d9008fb 100644 Binary files a/assets/png/marketing/market-order-preview.png and b/assets/png/marketing/market-order-preview.png differ diff --git a/assets/svg/app/eligible.svg b/assets/svg/app/eligible.svg index 6ef15432be..25518843f6 100644 --- a/assets/svg/app/eligible.svg +++ b/assets/svg/app/eligible.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/svg/app/not-eligible.svg b/assets/svg/app/not-eligible.svg index 355c41d690..d8fdfc2c00 100644 --- a/assets/svg/app/not-eligible.svg +++ b/assets/svg/app/not-eligible.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/svg/brand/logo-yellow.svg b/assets/svg/brand/logo-yellow.svg new file mode 100644 index 0000000000..a3929a3fe3 --- /dev/null +++ b/assets/svg/brand/logo-yellow.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/components/Text/LogoText.tsx b/components/Text/LogoText.tsx index 7b568f3e1a..f0ee27c767 100644 --- a/components/Text/LogoText.tsx +++ b/components/Text/LogoText.tsx @@ -1,19 +1,22 @@ import { memo, FC } from 'react'; import styled, { css } from 'styled-components'; +import HelpIcon from 'assets/svg/app/question-mark.svg'; import KwentaLogo from 'assets/svg/earn/KWENTA.svg'; import Heading from './Heading'; type LogoTextProps = { yellow?: boolean; + isToolTip?: boolean; }; -export const LogoText: FC = memo(({ children, yellow }) => { +export const LogoText: FC = memo(({ children, yellow, isToolTip = false }) => { return (
{children} + {isToolTip && }
); }); @@ -30,4 +33,8 @@ const TitleText = styled(Heading)<{ $yellow?: boolean; $mono?: boolean }>` `} `; +const SpacedHelpIcon = styled(HelpIcon)` + margin-left: 8px; +`; + export default LogoText; diff --git a/constants/links.ts b/constants/links.ts index d893229be5..284cb2a7a9 100644 --- a/constants/links.ts +++ b/constants/links.ts @@ -5,6 +5,7 @@ export const PROD_HOSTNAME = 'kwenta.eth.limo'; export const EXTERNAL_LINKS = { Trading: { Legacy: 'https://legacy.kwenta.io/exchange', + PerpsV1: 'https://v1.kwenta.eth.limo', DexAG: 'https://dex.ag/', Uniswap: 'https://uniswap.exchange/', OneInch: `https://1inch.exchange/`, @@ -32,23 +33,27 @@ export const EXTERNAL_LINKS = { DocsRoot: 'https://docs.kwenta.io/', FeeReclamation: 'https://docs.kwenta.io/resources/fee-reclamation', HowToTrade: 'https://docs.kwenta.io/products/futures', - Governance: 'https://docs.kwenta.io/dao/governance-framework', - DaoRoles: 'https://docs.kwenta.io/dao/dao-roles', + Governance: + 'https://app.radicle.xyz/seeds/maple.radicle.garden/rad:git:hnrkq1oex148yz4zi9tm7spfnjaryyuc93yay/tree/master/sections/1.md', + DaoRoles: + 'https://app.radicle.xyz/seeds/maple.radicle.garden/rad:git:hnrkq1oex148yz4zi9tm7spfnjaryyuc93yay/tree/master/sections/2.md', HowToUse: 'https://docs.kwenta.io/onboard/how-to-start-using-kwenta', Perpetuals: 'https://docs.kwenta.io/products/futures', Spot: 'https://docs.kwenta.io/products/swaps ', DevDao: 'https://docs.kwenta.io/dao/contribute/devdao-contribute', - MarketingDao: 'https://docs.kwenta.io/dao/contribute/marketingDAO', + MarketingDao: + 'https://app.radicle.xyz/seeds/maple.radicle.garden/rad:git:hnrkq1oex148yz4zi9tm7spfnjaryyuc93yay/tree/master/sections/2.md#marketingdao-grants-council-trial', Faq: 'https://docs.kwenta.io/resources/faq', CrossMarginFaq: 'https://docs.kwenta.io/products/futures/cross-margin-accounts', Staking: 'https://docs.kwenta.io/using-kwenta/staking-kwenta', + TradingRewardsV2: 'https://mirror.xyz/kwenta.eth/7k-5UYXXcCNJ_DRRWvYBsK5zDm5UA945My4QrInhxoI', }, Optimism: { Home: 'https://optimism.io/', }, Trade: { - NextPriceBlogPost: 'https://docs.kwenta.io/products/futures/next-price', - PerpsV2: 'https://alpha.kwenta.eth.limo/market/?accountType=isolated_margin&asset=sETH', + PerpsV2: 'https://kwenta.eth.limo/market/?accountType=isolated_margin&asset=sETH', + Spot: 'https://kwenta.eth.limo/exchange/', V1: 'https://v1.kwenta.eth.limo/dashboard', }, Governance: { @@ -59,7 +64,4 @@ export const EXTERNAL_LINKS = { Competition: { LearnMore: 'https://mirror.xyz/kwenta.eth/s_PO64SxvuwDHz9fdHebsYeQAOOc73D3bL2q4nC6LvU', }, - Aelin: { - Pool: 'https://app.aelin.xyz/pool/mainnet/0x21f4F88a95f656ef4ee1ea107569b3b38cf8daef', - }, }; diff --git a/package-lock.json b/package-lock.json index 3d95ff1d65..afa4c48ed0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55483,9 +55483,9 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "node_modules/undici": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz", - "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz", + "integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==", "dependencies": { "busboy": "^1.6.0" }, @@ -102216,9 +102216,9 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "undici": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.16.0.tgz", - "integrity": "sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.19.1.tgz", + "integrity": "sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==", "requires": { "busboy": "^1.6.0" } diff --git a/queries/staking/utils.ts b/queries/staking/utils.ts index cbdeb66d2e..b548512ffd 100644 --- a/queries/staking/utils.ts +++ b/queries/staking/utils.ts @@ -50,6 +50,7 @@ export const STAKING_REWARDS_RATIO = 0.6; export const TRADING_REWARDS_RATIO = 0.05; export const STAKING_HIGH_GAS_LIMIT = BigNumber.from('400000'); export const STAKING_LOW_GAS_LIMIT = BigNumber.from('200000'); +export const TRADING_REWARDS_CUTOFF_EPOCH = 13; const SUPPLY_RATE = wei(1).sub(wei(DECAY_RATE)); diff --git a/sdk/contracts/abis/BatchClaimer.json b/sdk/contracts/abis/BatchClaimer.json new file mode 100644 index 0000000000..22e71b279e --- /dev/null +++ b/sdk/contracts/abis/BatchClaimer.json @@ -0,0 +1,47 @@ +[ + { + "inputs": [ + { + "internalType": "contract IMultipleMerkleDistributor[]", + "name": "_distributors", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "merkleProof", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "internalType": "struct IMultipleMerkleDistributor.Claims[][]", + "name": "_claims", + "type": "tuple[][]" + } + ], + "name": "claimMultiple", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/sdk/contracts/abis/MultipleMerkleDistributorPerpsV2.json b/sdk/contracts/abis/MultipleMerkleDistributorPerpsV2.json new file mode 100644 index 0000000000..7ac9534cd3 --- /dev/null +++ b/sdk/contracts/abis/MultipleMerkleDistributorPerpsV2.json @@ -0,0 +1,305 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + }, + { + "internalType": "address", + "name": "_rewardEscrow", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "Claimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "MerkleRootModified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerNominated", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "merkleProof", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32[]", + "name": "merkleProof", + "type": "bytes32[]" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "internalType": "struct IMultipleMerkleDistributor.Claims[]", + "name": "claims", + "type": "tuple[]" + } + ], + "name": "claimMultiple", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "isClaimed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "merkleRoots", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "nominateNewOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "nominatedOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardEscrow", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "merkleRoot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "setMerkleRootForEpoch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/sdk/contracts/constants.ts b/sdk/contracts/constants.ts index a3ff29772d..9c6c73e9cb 100644 --- a/sdk/contracts/constants.ts +++ b/sdk/contracts/constants.ts @@ -119,4 +119,10 @@ export const ADDRESSES: Record> = { 10: '0xf486A72E8c8143ACd9F65A104A16990fDb38be14', 420: '0x74c0A3bD10634759DC8B4CA7078C8Bf85bFE1271', }, + TradingRewardsPerpsV2: { + 10: '0x2787CC20e5ECb4BF1bfB79eAE284201027683179', + }, + BatchClaimer: { + 10: '0x6Fd879830D9b1EE5d4f9ef12f8D5deE916bebD0b', + }, }; diff --git a/sdk/contracts/index.ts b/sdk/contracts/index.ts index 9e7b091e69..79cb5f9700 100644 --- a/sdk/contracts/index.ts +++ b/sdk/contracts/index.ts @@ -4,6 +4,7 @@ import { Contract, ethers } from 'ethers'; import ERC20ABI from '../contracts/abis/ERC20.json'; import MultipleMerkleDistributorABI from '../contracts/abis/MultipleMerkleDistributor.json'; +import MultipleMerkleDistributorPerpsV2ABI from '../contracts/abis/MultipleMerkleDistributorPerpsV2.json'; import RewardEscrowABI from '../contracts/abis/RewardEscrow.json'; import SupplyScheduleABI from '../contracts/abis/SupplySchedule.json'; import CrossMarginBaseSettingsABI from './abis/CrossMarginBaseSettings.json'; @@ -43,6 +44,7 @@ import { StakingRewards__factory, VeKwentaRedeemer__factory, Pyth__factory, + BatchClaimer__factory, } from './types'; type ContractFactory = { @@ -138,6 +140,15 @@ export const getContractsByNetwork = ( MultipleMerkleDistributor: ADDRESSES.TradingRewards[networkId] ? MultipleMerkleDistributor__factory.connect(ADDRESSES.TradingRewards[networkId], provider) : undefined, + MultipleMerkleDistributorPerpsV2: ADDRESSES.TradingRewardsPerpsV2[networkId] + ? MultipleMerkleDistributor__factory.connect( + ADDRESSES.TradingRewardsPerpsV2[networkId], + provider + ) + : undefined, + BatchClaimer: ADDRESSES.BatchClaimer[networkId] + ? BatchClaimer__factory.connect(ADDRESSES.BatchClaimer[networkId], provider) + : undefined, veKwentaToken: ADDRESSES.veKwentaToken[networkId] ? ERC20__factory.connect(ADDRESSES.veKwentaToken[networkId], provider) : undefined, @@ -197,6 +208,12 @@ export const getMulticallContractsByNetwork = (networkId: NetworkId) => { MultipleMerkleDistributor: ADDRESSES.TradingRewards[networkId] ? new EthCallContract(ADDRESSES.TradingRewards[networkId], MultipleMerkleDistributorABI) : undefined, + MultipleMerkleDistributorPerpsV2: ADDRESSES.TradingRewardsPerpsV2[networkId] + ? new EthCallContract( + ADDRESSES.TradingRewardsPerpsV2[networkId], + MultipleMerkleDistributorPerpsV2ABI + ) + : undefined, vKwentaToken: ADDRESSES.vKwentaToken[networkId] ? new EthCallContract(ADDRESSES.vKwentaToken[networkId], ERC20ABI) : undefined, diff --git a/sdk/contracts/types/BatchClaimer.ts b/sdk/contracts/types/BatchClaimer.ts new file mode 100644 index 0000000000..f93ebd8a6d --- /dev/null +++ b/sdk/contracts/types/BatchClaimer.ts @@ -0,0 +1,138 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PopulatedTransaction, + Signer, + utils, +} from "ethers"; +import type { FunctionFragment, Result } from "@ethersproject/abi"; +import type { Listener, Provider } from "@ethersproject/providers"; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, + PromiseOrValue, +} from "./common"; + +export declare namespace IMultipleMerkleDistributor { + export type ClaimsStruct = { + index: PromiseOrValue; + account: PromiseOrValue; + amount: PromiseOrValue; + merkleProof: PromiseOrValue[]; + epoch: PromiseOrValue; + }; + + export type ClaimsStructOutput = [ + BigNumber, + string, + BigNumber, + string[], + BigNumber + ] & { + index: BigNumber; + account: string; + amount: BigNumber; + merkleProof: string[]; + epoch: BigNumber; + }; +} + +export interface BatchClaimerInterface extends utils.Interface { + functions: { + "claimMultiple(address[],tuple[][])": FunctionFragment; + }; + + getFunction(nameOrSignatureOrTopic: "claimMultiple"): FunctionFragment; + + encodeFunctionData( + functionFragment: "claimMultiple", + values: [ + PromiseOrValue[], + IMultipleMerkleDistributor.ClaimsStruct[][] + ] + ): string; + + decodeFunctionResult( + functionFragment: "claimMultiple", + data: BytesLike + ): Result; + + events: {}; +} + +export interface BatchClaimer extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: BatchClaimerInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + claimMultiple( + _distributors: PromiseOrValue[], + _claims: IMultipleMerkleDistributor.ClaimsStruct[][], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; + + claimMultiple( + _distributors: PromiseOrValue[], + _claims: IMultipleMerkleDistributor.ClaimsStruct[][], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + callStatic: { + claimMultiple( + _distributors: PromiseOrValue[], + _claims: IMultipleMerkleDistributor.ClaimsStruct[][], + overrides?: CallOverrides + ): Promise; + }; + + filters: {}; + + estimateGas: { + claimMultiple( + _distributors: PromiseOrValue[], + _claims: IMultipleMerkleDistributor.ClaimsStruct[][], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; + + populateTransaction: { + claimMultiple( + _distributors: PromiseOrValue[], + _claims: IMultipleMerkleDistributor.ClaimsStruct[][], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + }; +} diff --git a/sdk/contracts/types/MultipleMerkleDistributorPerpsV2.ts b/sdk/contracts/types/MultipleMerkleDistributorPerpsV2.ts new file mode 100644 index 0000000000..6b51a864bb --- /dev/null +++ b/sdk/contracts/types/MultipleMerkleDistributorPerpsV2.ts @@ -0,0 +1,519 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PopulatedTransaction, + Signer, + utils, +} from "ethers"; +import type { + FunctionFragment, + Result, + EventFragment, +} from "@ethersproject/abi"; +import type { Listener, Provider } from "@ethersproject/providers"; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, + PromiseOrValue, +} from "./common"; + +export declare namespace IMultipleMerkleDistributor { + export type ClaimsStruct = { + index: PromiseOrValue; + account: PromiseOrValue; + amount: PromiseOrValue; + merkleProof: PromiseOrValue[]; + epoch: PromiseOrValue; + }; + + export type ClaimsStructOutput = [ + BigNumber, + string, + BigNumber, + string[], + BigNumber + ] & { + index: BigNumber; + account: string; + amount: BigNumber; + merkleProof: string[]; + epoch: BigNumber; + }; +} + +export interface MultipleMerkleDistributorPerpsV2Interface + extends utils.Interface { + functions: { + "acceptOwnership()": FunctionFragment; + "claim(uint256,address,uint256,bytes32[],uint256)": FunctionFragment; + "claimMultiple((uint256,address,uint256,bytes32[],uint256)[])": FunctionFragment; + "isClaimed(uint256,uint256)": FunctionFragment; + "merkleRoots(uint256)": FunctionFragment; + "nominateNewOwner(address)": FunctionFragment; + "nominatedOwner()": FunctionFragment; + "owner()": FunctionFragment; + "rewardEscrow()": FunctionFragment; + "setMerkleRootForEpoch(bytes32,uint256)": FunctionFragment; + "token()": FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | "acceptOwnership" + | "claim" + | "claimMultiple" + | "isClaimed" + | "merkleRoots" + | "nominateNewOwner" + | "nominatedOwner" + | "owner" + | "rewardEscrow" + | "setMerkleRootForEpoch" + | "token" + ): FunctionFragment; + + encodeFunctionData( + functionFragment: "acceptOwnership", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "claim", + values: [ + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + PromiseOrValue[], + PromiseOrValue + ] + ): string; + encodeFunctionData( + functionFragment: "claimMultiple", + values: [IMultipleMerkleDistributor.ClaimsStruct[]] + ): string; + encodeFunctionData( + functionFragment: "isClaimed", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "merkleRoots", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "nominateNewOwner", + values: [PromiseOrValue] + ): string; + encodeFunctionData( + functionFragment: "nominatedOwner", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "owner", values?: undefined): string; + encodeFunctionData( + functionFragment: "rewardEscrow", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "setMerkleRootForEpoch", + values: [PromiseOrValue, PromiseOrValue] + ): string; + encodeFunctionData(functionFragment: "token", values?: undefined): string; + + decodeFunctionResult( + functionFragment: "acceptOwnership", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "claim", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "claimMultiple", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "isClaimed", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "merkleRoots", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "nominateNewOwner", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "nominatedOwner", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "rewardEscrow", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "setMerkleRootForEpoch", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "token", data: BytesLike): Result; + + events: { + "Claimed(uint256,address,uint256,uint256)": EventFragment; + "MerkleRootModified(uint256)": EventFragment; + "OwnerChanged(address,address)": EventFragment; + "OwnerNominated(address)": EventFragment; + }; + + getEvent(nameOrSignatureOrTopic: "Claimed"): EventFragment; + getEvent(nameOrSignatureOrTopic: "MerkleRootModified"): EventFragment; + getEvent(nameOrSignatureOrTopic: "OwnerChanged"): EventFragment; + getEvent(nameOrSignatureOrTopic: "OwnerNominated"): EventFragment; +} + +export interface ClaimedEventObject { + index: BigNumber; + account: string; + amount: BigNumber; + epoch: BigNumber; +} +export type ClaimedEvent = TypedEvent< + [BigNumber, string, BigNumber, BigNumber], + ClaimedEventObject +>; + +export type ClaimedEventFilter = TypedEventFilter; + +export interface MerkleRootModifiedEventObject { + epoch: BigNumber; +} +export type MerkleRootModifiedEvent = TypedEvent< + [BigNumber], + MerkleRootModifiedEventObject +>; + +export type MerkleRootModifiedEventFilter = + TypedEventFilter; + +export interface OwnerChangedEventObject { + oldOwner: string; + newOwner: string; +} +export type OwnerChangedEvent = TypedEvent< + [string, string], + OwnerChangedEventObject +>; + +export type OwnerChangedEventFilter = TypedEventFilter; + +export interface OwnerNominatedEventObject { + newOwner: string; +} +export type OwnerNominatedEvent = TypedEvent< + [string], + OwnerNominatedEventObject +>; + +export type OwnerNominatedEventFilter = TypedEventFilter; + +export interface MultipleMerkleDistributorPerpsV2 extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: MultipleMerkleDistributorPerpsV2Interface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + acceptOwnership( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claim( + index: PromiseOrValue, + account: PromiseOrValue, + amount: PromiseOrValue, + merkleProof: PromiseOrValue[], + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claimMultiple( + claims: IMultipleMerkleDistributor.ClaimsStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + isClaimed( + index: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[boolean]>; + + merkleRoots( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[string]>; + + nominateNewOwner( + _owner: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nominatedOwner(overrides?: CallOverrides): Promise<[string]>; + + owner(overrides?: CallOverrides): Promise<[string]>; + + rewardEscrow(overrides?: CallOverrides): Promise<[string]>; + + setMerkleRootForEpoch( + merkleRoot: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + token(overrides?: CallOverrides): Promise<[string]>; + }; + + acceptOwnership( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claim( + index: PromiseOrValue, + account: PromiseOrValue, + amount: PromiseOrValue, + merkleProof: PromiseOrValue[], + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claimMultiple( + claims: IMultipleMerkleDistributor.ClaimsStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + isClaimed( + index: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + merkleRoots( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nominateNewOwner( + _owner: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nominatedOwner(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + rewardEscrow(overrides?: CallOverrides): Promise; + + setMerkleRootForEpoch( + merkleRoot: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + token(overrides?: CallOverrides): Promise; + + callStatic: { + acceptOwnership(overrides?: CallOverrides): Promise; + + claim( + index: PromiseOrValue, + account: PromiseOrValue, + amount: PromiseOrValue, + merkleProof: PromiseOrValue[], + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + claimMultiple( + claims: IMultipleMerkleDistributor.ClaimsStruct[], + overrides?: CallOverrides + ): Promise; + + isClaimed( + index: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + merkleRoots( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nominateNewOwner( + _owner: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nominatedOwner(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + rewardEscrow(overrides?: CallOverrides): Promise; + + setMerkleRootForEpoch( + merkleRoot: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + token(overrides?: CallOverrides): Promise; + }; + + filters: { + "Claimed(uint256,address,uint256,uint256)"( + index?: null, + account?: null, + amount?: null, + epoch?: null + ): ClaimedEventFilter; + Claimed( + index?: null, + account?: null, + amount?: null, + epoch?: null + ): ClaimedEventFilter; + + "MerkleRootModified(uint256)"(epoch?: null): MerkleRootModifiedEventFilter; + MerkleRootModified(epoch?: null): MerkleRootModifiedEventFilter; + + "OwnerChanged(address,address)"( + oldOwner?: null, + newOwner?: null + ): OwnerChangedEventFilter; + OwnerChanged(oldOwner?: null, newOwner?: null): OwnerChangedEventFilter; + + "OwnerNominated(address)"(newOwner?: null): OwnerNominatedEventFilter; + OwnerNominated(newOwner?: null): OwnerNominatedEventFilter; + }; + + estimateGas: { + acceptOwnership( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claim( + index: PromiseOrValue, + account: PromiseOrValue, + amount: PromiseOrValue, + merkleProof: PromiseOrValue[], + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claimMultiple( + claims: IMultipleMerkleDistributor.ClaimsStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + isClaimed( + index: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + merkleRoots( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nominateNewOwner( + _owner: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nominatedOwner(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + rewardEscrow(overrides?: CallOverrides): Promise; + + setMerkleRootForEpoch( + merkleRoot: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + token(overrides?: CallOverrides): Promise; + }; + + populateTransaction: { + acceptOwnership( + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claim( + index: PromiseOrValue, + account: PromiseOrValue, + amount: PromiseOrValue, + merkleProof: PromiseOrValue[], + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + claimMultiple( + claims: IMultipleMerkleDistributor.ClaimsStruct[], + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + isClaimed( + index: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + merkleRoots( + arg0: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + + nominateNewOwner( + _owner: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + nominatedOwner(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + rewardEscrow(overrides?: CallOverrides): Promise; + + setMerkleRootForEpoch( + merkleRoot: PromiseOrValue, + epoch: PromiseOrValue, + overrides?: Overrides & { from?: PromiseOrValue } + ): Promise; + + token(overrides?: CallOverrides): Promise; + }; +} diff --git a/sdk/contracts/types/factories/BatchClaimer__factory.ts b/sdk/contracts/types/factories/BatchClaimer__factory.ts new file mode 100644 index 0000000000..a6771bace5 --- /dev/null +++ b/sdk/contracts/types/factories/BatchClaimer__factory.ts @@ -0,0 +1,68 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { BatchClaimer, BatchClaimerInterface } from "../BatchClaimer"; + +const _abi = [ + { + inputs: [ + { + internalType: "contract IMultipleMerkleDistributor[]", + name: "_distributors", + type: "address[]", + }, + { + components: [ + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "bytes32[]", + name: "merkleProof", + type: "bytes32[]", + }, + { + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + internalType: "struct IMultipleMerkleDistributor.Claims[][]", + name: "_claims", + type: "tuple[][]", + }, + ], + name: "claimMultiple", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export class BatchClaimer__factory { + static readonly abi = _abi; + static createInterface(): BatchClaimerInterface { + return new utils.Interface(_abi) as BatchClaimerInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): BatchClaimer { + return new Contract(address, _abi, signerOrProvider) as BatchClaimer; + } +} diff --git a/sdk/contracts/types/factories/MultipleMerkleDistributorPerpsV2__factory.ts b/sdk/contracts/types/factories/MultipleMerkleDistributorPerpsV2__factory.ts new file mode 100644 index 0000000000..2679775fc5 --- /dev/null +++ b/sdk/contracts/types/factories/MultipleMerkleDistributorPerpsV2__factory.ts @@ -0,0 +1,335 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from "ethers"; +import type { Provider } from "@ethersproject/providers"; +import type { + MultipleMerkleDistributorPerpsV2, + MultipleMerkleDistributorPerpsV2Interface, +} from "../MultipleMerkleDistributorPerpsV2"; + +const _abi = [ + { + inputs: [ + { + internalType: "address", + name: "_owner", + type: "address", + }, + { + internalType: "address", + name: "_token", + type: "address", + }, + { + internalType: "address", + name: "_rewardEscrow", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + name: "Claimed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + name: "MerkleRootModified", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "oldOwner", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnerChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnerNominated", + type: "event", + }, + { + inputs: [], + name: "acceptOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "bytes32[]", + name: "merkleProof", + type: "bytes32[]", + }, + { + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "bytes32[]", + name: "merkleProof", + type: "bytes32[]", + }, + { + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + internalType: "struct IMultipleMerkleDistributor.Claims[]", + name: "claims", + type: "tuple[]", + }, + ], + name: "claimMultiple", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + name: "isClaimed", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "merkleRoots", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_owner", + type: "address", + }, + ], + name: "nominateNewOwner", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "nominatedOwner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "rewardEscrow", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "merkleRoot", + type: "bytes32", + }, + { + internalType: "uint256", + name: "epoch", + type: "uint256", + }, + ], + name: "setMerkleRootForEpoch", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "token", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, +]; + +export class MultipleMerkleDistributorPerpsV2__factory { + static readonly abi = _abi; + static createInterface(): MultipleMerkleDistributorPerpsV2Interface { + return new utils.Interface( + _abi + ) as MultipleMerkleDistributorPerpsV2Interface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider + ): MultipleMerkleDistributorPerpsV2 { + return new Contract( + address, + _abi, + signerOrProvider + ) as MultipleMerkleDistributorPerpsV2; + } +} diff --git a/sdk/contracts/types/factories/index.ts b/sdk/contracts/types/factories/index.ts index 0db9af4726..927b7e03e7 100644 --- a/sdk/contracts/types/factories/index.ts +++ b/sdk/contracts/types/factories/index.ts @@ -1,6 +1,7 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export { BatchClaimer__factory } from "./BatchClaimer__factory"; export { CrossMarginAccountFactory__factory } from "./CrossMarginAccountFactory__factory"; export { CrossMarginBase__factory } from "./CrossMarginBase__factory"; export { CrossMarginBaseSettings__factory } from "./CrossMarginBaseSettings__factory"; @@ -13,6 +14,7 @@ export { FuturesMarketSettings__factory } from "./FuturesMarketSettings__factory export { KwentaArrakisVault__factory } from "./KwentaArrakisVault__factory"; export { KwentaStakingRewards__factory } from "./KwentaStakingRewards__factory"; export { MultipleMerkleDistributor__factory } from "./MultipleMerkleDistributor__factory"; +export { MultipleMerkleDistributorPerpsV2__factory } from "./MultipleMerkleDistributorPerpsV2__factory"; export { PerpsV2Market__factory } from "./PerpsV2Market__factory"; export { PerpsV2MarketData__factory } from "./PerpsV2MarketData__factory"; export { PerpsV2MarketSettings__factory } from "./PerpsV2MarketSettings__factory"; diff --git a/sdk/contracts/types/index.ts b/sdk/contracts/types/index.ts index be659dbdfb..b4b40775df 100644 --- a/sdk/contracts/types/index.ts +++ b/sdk/contracts/types/index.ts @@ -1,6 +1,7 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export type { BatchClaimer } from "./BatchClaimer"; export type { CrossMarginAccountFactory } from "./CrossMarginAccountFactory"; export type { CrossMarginBase } from "./CrossMarginBase"; export type { CrossMarginBaseSettings } from "./CrossMarginBaseSettings"; @@ -13,6 +14,7 @@ export type { FuturesMarketSettings } from "./FuturesMarketSettings"; export type { KwentaArrakisVault } from "./KwentaArrakisVault"; export type { KwentaStakingRewards } from "./KwentaStakingRewards"; export type { MultipleMerkleDistributor } from "./MultipleMerkleDistributor"; +export type { MultipleMerkleDistributorPerpsV2 } from "./MultipleMerkleDistributorPerpsV2"; export type { PerpsV2Market } from "./PerpsV2Market"; export type { PerpsV2MarketData } from "./PerpsV2MarketData"; export type { PerpsV2MarketSettings } from "./PerpsV2MarketSettings"; @@ -31,6 +33,7 @@ export type { SystemStatus } from "./SystemStatus"; export type { VKwentaRedeemer } from "./VKwentaRedeemer"; export type { VeKwentaRedeemer } from "./VeKwentaRedeemer"; export * as factories from "./factories"; +export { BatchClaimer__factory } from "./factories/BatchClaimer__factory"; export { CrossMarginAccountFactory__factory } from "./factories/CrossMarginAccountFactory__factory"; export { CrossMarginBase__factory } from "./factories/CrossMarginBase__factory"; export { CrossMarginBaseSettings__factory } from "./factories/CrossMarginBaseSettings__factory"; @@ -43,6 +46,7 @@ export { FuturesMarketSettings__factory } from "./factories/FuturesMarketSetting export { KwentaArrakisVault__factory } from "./factories/KwentaArrakisVault__factory"; export { KwentaStakingRewards__factory } from "./factories/KwentaStakingRewards__factory"; export { MultipleMerkleDistributor__factory } from "./factories/MultipleMerkleDistributor__factory"; +export { MultipleMerkleDistributorPerpsV2__factory } from "./factories/MultipleMerkleDistributorPerpsV2__factory"; export { PerpsV2Market__factory } from "./factories/PerpsV2Market__factory"; export { PerpsV2MarketData__factory } from "./factories/PerpsV2MarketData__factory"; export { PerpsV2MarketSettings__factory } from "./factories/PerpsV2MarketSettings__factory"; diff --git a/sdk/services/futures.ts b/sdk/services/futures.ts index a3ef2ace4b..18b68fec7a 100644 --- a/sdk/services/futures.ts +++ b/sdk/services/futures.ts @@ -115,22 +115,27 @@ export default class FuturesService { if (!PerpsV2MarketData || !PerpsV2MarketSettings || !SystemStatus || !ExchangeRates) { throw new Error(UNSUPPORTED_NETWORK); } - const [markets, globals] = await this.sdk.context.multicallProvider.all([ + const futuresData: Array< + PerpsV2MarketData.MarketSummaryStructOutput[] | PerpsV2MarketData.FuturesGlobalsStructOutput + > = await this.sdk.context.multicallProvider.all([ PerpsV2MarketData.allProxiedMarketSummaries(), PerpsV2MarketData.globals(), ]); - const filteredMarkets = (markets as PerpsV2MarketData.MarketSummaryStructOutput[]).filter( - (m) => { - const marketKey = parseBytes32String(m.key) as FuturesMarketKey; - const market = enabledMarkets.find((market) => { - return marketKey === market.key; - }); - return !!market; - } - ); + const { markets, globals } = { + markets: futuresData[0] as PerpsV2MarketData.MarketSummaryStructOutput[], + globals: futuresData[1] as PerpsV2MarketData.FuturesGlobalsStructOutput, + }; + + const filteredMarkets = markets.filter((m) => { + const marketKey = parseBytes32String(m.key) as FuturesMarketKey; + const market = enabledMarkets.find((market) => { + return marketKey === market.key; + }); + return !!market; + }); - const marketKeys = filteredMarkets.map((m: any) => { + const marketKeys = filteredMarkets.map((m) => { return m.key; }); @@ -475,11 +480,18 @@ export default class FuturesService { const crossMarginBaseSettings = this.sdk.context.multicallContracts.CrossMarginBaseSettings; if (!crossMarginBaseSettings) throw new Error(UNSUPPORTED_NETWORK); - const [tradeFee, limitOrderFee, stopOrderFee] = await this.sdk.context.multicallProvider.all([ + const fees: Array = await this.sdk.context.multicallProvider.all([ crossMarginBaseSettings.tradeFee(), crossMarginBaseSettings.limitOrderFee(), crossMarginBaseSettings.stopOrderFee(), ]); + + const { tradeFee, limitOrderFee, stopOrderFee } = { + tradeFee: fees[0], + limitOrderFee: fees[1], + stopOrderFee: fees[2], + }; + return { tradeFee: tradeFee ? wei(tradeFee.toNumber() / BPS_CONVERSION) : wei(0), limitOrderFee: limitOrderFee ? wei(limitOrderFee.toNumber() / BPS_CONVERSION) : wei(0), diff --git a/sdk/services/kwentaToken.ts b/sdk/services/kwentaToken.ts index fcb2ed6f66..ba5adc200e 100644 --- a/sdk/services/kwentaToken.ts +++ b/sdk/services/kwentaToken.ts @@ -8,6 +8,7 @@ import KwentaSDK from 'sdk'; import { ETH_COINGECKO_ADDRESS, KWENTA_ADDRESS } from 'constants/currency'; import { DEFAULT_NUMBER_OF_FUTURES_FEE } from 'constants/defaults'; import { FLEEK_BASE_URL, FLEEK_STORAGE_BUCKET } from 'queries/files/constants'; +import { EPOCH_START, TRADING_REWARDS_CUTOFF_EPOCH, WEEK } from 'queries/staking/utils'; import { ContractName } from 'sdk/contracts'; import { formatTruncatedDuration } from 'utils/formatters/date'; import { zeroBN } from 'utils/formatters/number'; @@ -174,7 +175,6 @@ export default class KwentaTokenService { vKwentaBalance, vKwentaAllowance, kwentaAllowance, - epochPeriod, veKwentaBalance, veKwentaAllowance, ]: BigNumber[] = await this.sdk.context.multicallProvider.all([ @@ -188,7 +188,6 @@ export default class KwentaTokenService { vKwentaToken.balanceOf(walletAddress), vKwentaToken.allowance(walletAddress, vKwentaRedeemer.address), KwentaToken.allowance(walletAddress, KwentaStakingRewards.address), - MultipleMerkleDistributor.distributionEpoch(), veKwentaToken.balanceOf(walletAddress), veKwentaToken.allowance(walletAddress, veKwentaRedeemer.address), ]); @@ -204,7 +203,7 @@ export default class KwentaTokenService { vKwentaBalance: wei(vKwentaBalance), vKwentaAllowance: wei(vKwentaAllowance), kwentaAllowance: wei(kwentaAllowance), - epochPeriod: Number(epochPeriod) - 2, + epochPeriod: Math.floor((Math.floor(Date.now() / 1000) - EPOCH_START[10]) / WEEK), veKwentaBalance: wei(veKwentaBalance), veKwentaAllowance: wei(veKwentaAllowance), }; @@ -378,29 +377,39 @@ export default class KwentaTokenService { return this.performStakeAction('unstake', amount, { escrow: true }); } - public async getClaimableRewards(epochPeriod: number) { - const { MultipleMerkleDistributor } = this.sdk.context.multicallContracts; + public async getClaimableRewards(epochPeriod: number, isOldDistributor: boolean = true) { + const { + MultipleMerkleDistributor, + MultipleMerkleDistributorPerpsV2, + } = this.sdk.context.multicallContracts; const { walletAddress } = this.sdk.context; - if (!MultipleMerkleDistributor) { + if (!MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2) { throw new Error(sdkErrors.UNSUPPORTED_NETWORK); } - const periods = Array.from(new Array(Number(epochPeriod) + 1), (_, i) => i); + const periods = Array.from(new Array(Number(epochPeriod)), (_, i) => i); + const adjustedPeriods = isOldDistributor + ? periods.slice(0, TRADING_REWARDS_CUTOFF_EPOCH) + : periods.slice(TRADING_REWARDS_CUTOFF_EPOCH); - const fileNames = periods - .slice(0, -1) - .map( - (i) => - `trading-rewards-snapshots/${ - this.sdk.context.networkId === 420 ? `goerli-` : '' - }epoch-${i}.json` - ); + const fileNames = adjustedPeriods.map( + (i) => + `trading-rewards-snapshots/${ + this.sdk.context.networkId === 420 ? `goerli-` : '' + }epoch-${i}.json` + ); const responses: EpochData[] = await Promise.all( fileNames.map(async (fileName, index) => { const response = await client.get(fileName); - const period = index >= 5 ? (index >= 10 ? index + 2 : index + 1) : index; + const period = isOldDistributor + ? index >= 5 + ? index >= 10 + ? index + 2 + : index + 1 + : index + : index + TRADING_REWARDS_CUTOFF_EPOCH; return { ...response.data, period }; }) ); @@ -418,7 +427,11 @@ export default class KwentaTokenService { .filter((x): x is ClaimParams => !!x); const claimed: boolean[] = await this.sdk.context.multicallProvider.all( - rewards.map((reward) => MultipleMerkleDistributor.isClaimed(reward[0], reward[4])) + rewards.map((reward) => + isOldDistributor + ? MultipleMerkleDistributor.isClaimed(reward[0], reward[4]) + : MultipleMerkleDistributorPerpsV2.isClaimed(reward[0], reward[4]) + ) ); const { totalRewards, claimableRewards } = rewards.reduce( @@ -436,14 +449,19 @@ export default class KwentaTokenService { return { claimableRewards, totalRewards }; } - public async claimMultipleRewards(claimableRewards: ClaimParams[]) { - const { MultipleMerkleDistributor } = this.sdk.context.contracts; + public async claimMultipleRewards(claimableRewards: ClaimParams[][]) { + const { + BatchClaimer, + MultipleMerkleDistributor, + MultipleMerkleDistributorPerpsV2, + } = this.sdk.context.contracts; - if (!MultipleMerkleDistributor) { + if (!BatchClaimer || !MultipleMerkleDistributor || !MultipleMerkleDistributorPerpsV2) { throw new Error(sdkErrors.UNSUPPORTED_NETWORK); } - return this.sdk.transactions.createContractTxn(MultipleMerkleDistributor, 'claimMultiple', [ + return this.sdk.transactions.createContractTxn(BatchClaimer, 'claimMultiple', [ + [MultipleMerkleDistributor.address, MultipleMerkleDistributorPerpsV2.address], claimableRewards, ]); } diff --git a/sections/dashboard/Stake/TradingRewardsTab.tsx b/sections/dashboard/Stake/TradingRewardsTab.tsx index c8ac0966d6..e3e103ab8c 100644 --- a/sections/dashboard/Stake/TradingRewardsTab.tsx +++ b/sections/dashboard/Stake/TradingRewardsTab.tsx @@ -12,6 +12,7 @@ import { SplitContainer } from 'components/layout/grid'; import { MobileHiddenView, MobileOnlyView } from 'components/Media'; import { Body, LogoText } from 'components/Text'; import Tooltip from 'components/Tooltip/Tooltip'; +import { EXTERNAL_LINKS } from 'constants/links'; import Connector from 'containers/Connector'; import useGetFile from 'queries/files/useGetFile'; import useGetFuturesFee from 'queries/staking/useGetFuturesFee'; @@ -138,10 +139,21 @@ const TradingRewardsTab: FC = memo( {showEstimatedValue ? ( <> -
- {t('dashboard.stake.tabs.trading-rewards.estimated-rewards')} - {truncateNumbers(wei(estimatedReward), 4)} -
+ + + {t('dashboard.stake.tabs.trading-rewards.estimated-rewards')} + + + {truncateNumbers(wei(estimatedReward), 4)} + + + +
{t('dashboard.stake.tabs.trading-rewards.estimated-reward-share', { @@ -153,22 +165,25 @@ const TradingRewardsTab: FC<TradingRewardProps> = memo( </> ) : null} </CardGrid> - {showEstimatedValue ? ( - <FlexDivRow> - <PeriodLabel> - {t('dashboard.stake.tabs.trading-rewards.estimated-info')} - </PeriodLabel> - </FlexDivRow> - ) : null} + <Button + fullWidth + variant="flat" + size="sm" + onClick={() => + window.open(EXTERNAL_LINKS.Docs.TradingRewardsV2, '_blank', 'noopener noreferrer') + } + > + {t('dashboard.stake.tabs.trading-rewards.learn-more')} + </Button> </CardGridContainer> </MobileHiddenView> <MobileOnlyView> <CardGridContainer> <CardGrid> <CustomStyledTooltip - preset="bottom" width="260px" height="auto" + left="15px !important" content={t('dashboard.stake.tabs.trading-rewards.trading-rewards-tooltip')} > <WithCursor cursor="help"> @@ -185,10 +200,19 @@ const TradingRewardsTab: FC<TradingRewardProps> = memo( </div> {showEstimatedValue ? ( <> - <div> - <Title>{t('dashboard.stake.tabs.trading-rewards.estimated-rewards')} - {truncateNumbers(wei(estimatedReward), 4)} -
+ + + {t('dashboard.stake.tabs.trading-rewards.estimated-rewards')} + + {truncateNumbers(wei(estimatedReward), 4)} + + +
{t('dashboard.stake.tabs.trading-rewards.estimated-reward-share-mobile', { @@ -200,13 +224,16 @@ const TradingRewardsTab: FC<TradingRewardProps> = memo( </> ) : null} </CardGrid> - {showEstimatedValue ? ( - <FlexDivRow> - <PeriodLabel> - {t('dashboard.stake.tabs.trading-rewards.estimated-info')} - </PeriodLabel> - </FlexDivRow> - ) : null} + <Button + fullWidth + variant="flat" + size="sm" + onClick={() => + window.open(EXTERNAL_LINKS.Docs.TradingRewardsV2, '_blank', 'noopener noreferrer') + } + > + {t('dashboard.stake.tabs.trading-rewards.learn-more')} + </Button> </CardGridContainer> </MobileOnlyView> </SplitContainer> @@ -214,20 +241,10 @@ const TradingRewardsTab: FC<TradingRewardProps> = memo( } ); -const PeriodLabel = styled.div` - font-size: 13px; - line-height: 20px; - display: flex; - align-items: center; - font-family: ${(props) => props.theme.fonts.regular}; - color: ${(props) => props.theme.colors.selectedTheme.gray}; -`; - const CustomStyledTooltip = styled(Tooltip)` padding: 10px; ${media.lessThan('md')` width: 310px; - left: -5px; `} `; @@ -239,6 +256,7 @@ const CardGridContainer = styled(StakingCard)` display: flex; flex-direction: column; justify-content: space-between; + height: 240px; `; const Value = styled(Body).attrs({ variant: 'bold', mono: true })` diff --git a/sections/futures/FeeInfoBox/FeeInfoBox.tsx b/sections/futures/FeeInfoBox/FeeInfoBox.tsx index 040ee94405..945e010467 100644 --- a/sections/futures/FeeInfoBox/FeeInfoBox.tsx +++ b/sections/futures/FeeInfoBox/FeeInfoBox.tsx @@ -167,13 +167,13 @@ const FeeInfoBox: React.FC = () => { 'Trading Reward': { value: '', compactBox: true, - spaceBeneath: true, + spaceBeneath: false, keyNode: ( <CompactBox $isEligible={isRewardEligible} onClick={() => router.push(ROUTES.Dashboard.Stake)} > - <FlexDivRow style={{ marginBottom: '8px' }}> + <FlexDivRow style={{ marginBottom: '5px' }}> <div>{t('dashboard.stake.tabs.trading-rewards.trading-reward')}</div> {isRewardEligible ? ( <div className="badge badge-yellow"> @@ -255,13 +255,13 @@ const RewardCopy = styled(Body)` const CompactBox = styled.div<{ $isEligible: boolean }>` color: ${(props) => props.theme.colors.selectedTheme.text.value}; font-size: 13px; - padding-left: 8px; + padding-left: 10px; cursor: pointer; - margin-top: 16px; + margin-top: 10px; .badge { font-family: ${(props) => props.theme.fonts.black}; - padding: 0px 6px; + padding: 1px 5px; border-radius: 100px; font-variant: all-small-caps; } @@ -279,7 +279,7 @@ const CompactBox = styled.div<{ $isEligible: boolean }>` } ${(props) => - `border-left: 3px solid + `border-left: 2px solid ${ props.$isEligible ? props.theme.colors.selectedTheme.badge.yellow.background diff --git a/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileMenuModal.tsx b/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileMenuModal.tsx index 77d074fe68..500867204f 100644 --- a/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileMenuModal.tsx +++ b/sections/shared/Layout/AppLayout/Header/MobileUserMenu/MobileMenuModal.tsx @@ -5,7 +5,9 @@ import { useTranslation } from 'react-i18next'; import styled, { css } from 'styled-components'; import MobileMenuArrow from 'assets/svg/app/mobile-menu-arrow.svg'; +import KwentaYellowIcon from 'assets/svg/brand/logo-yellow.svg'; import FullScreenModal from 'components/FullScreenModal'; +import { FlexDivRowCentered } from 'components/layout/flex'; import ROUTES from 'constants/routes'; import Links from 'sections/dashboard/Links'; import Logo from 'sections/shared/Layout/Logo'; @@ -74,7 +76,12 @@ export const MobileMenuModal: FC<MobileMenuModalProps> = ({ onDismiss }) => { isActive={router.asPath.includes(link)} onClick={onDismiss} > - {t(i18nLabel)} + <FlexDivRowCentered> + {t(i18nLabel)} + {i18nLabel === 'header.nav.markets' ? ( + <KwentaYellowIcon height={18} width={18} style={{ marginLeft: 5 }} /> + ) : null} + </FlexDivRowCentered> <MobileMenuArrow /> </MenuButton> </Link> diff --git a/sections/shared/Layout/AppLayout/Header/Nav.tsx b/sections/shared/Layout/AppLayout/Header/Nav.tsx index bf4380fdf5..7c1661bc31 100644 --- a/sections/shared/Layout/AppLayout/Header/Nav.tsx +++ b/sections/shared/Layout/AppLayout/Header/Nav.tsx @@ -4,13 +4,17 @@ import { FC, FunctionComponent, memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; +import KwentaYellowIcon from 'assets/svg/brand/logo-yellow.svg'; import Badge from 'components/Badge'; +import { FlexDivRow } from 'components/layout/flex'; import LabelContainer from 'components/Nav/DropDownLabel'; import Select from 'components/Select'; import { DropdownIndicator, IndicatorSeparator } from 'components/Select/Select'; +import Tooltip from 'components/Tooltip/Tooltip'; import { selectMarketAsset } from 'state/futures/selectors'; import { useAppSelector } from 'state/hooks'; import { linkCSS } from 'styles/common'; +import media from 'styles/media'; import { DESKTOP_NAV_LINKS, Badge as BadgeType } from './constants'; @@ -42,7 +46,7 @@ const Nav: FC = memo(() => { link, isActive, }: ReactSelectOptionProps) => { - if (i18nLabel === 'header.nav.markets' || i18nLabel === 'header.nav.leaderboard') { + if (i18nLabel === 'header.nav.leaderboard') { return ( <MenuInside isDropDown isActive={isActive}> {t(i18nLabel)} @@ -77,7 +81,25 @@ const Nav: FC = memo(() => { if (!links) { return ( <Link key={url} href={url}> - <MenuInside isActive={isActive}>{t(i18nLabel)}</MenuInside> + <MenuInside isActive={isActive}> + {i18nLabel === 'header.nav.markets' ? ( + <CustomStyledTooltip + preset="bottom" + width="260px" + height="auto" + content={t('dashboard.stake.tabs.trading-rewards.trading-rewards-tooltip')} + > + <WithCursor cursor="pointer"> + <FlexDivRow> + {t(i18nLabel)} + <KwentaYellowIcon height={20} width={20} style={{ paddingLeft: 5 }} /> + </FlexDivRow> + </WithCursor> + </CustomStyledTooltip> + ) : ( + t(i18nLabel) + )} + </MenuInside> </Link> ); } @@ -101,8 +123,22 @@ const Nav: FC = memo(() => { ); }); +const CustomStyledTooltip = styled(Tooltip)` + padding: 10px; + text-align: left; + text-transform: none; + ${media.lessThan('md')` + width: 310px; + `} +`; + +const WithCursor = styled.div<{ cursor: 'help' | 'pointer' }>` + cursor: ${(props) => props.cursor}; +`; + const MenuLinks = styled.ul` display: flex; + padding-top: 2px; `; const NavLabel = styled.div` diff --git a/sections/shared/Layout/HomeLayout/Footer.tsx b/sections/shared/Layout/HomeLayout/Footer.tsx index d37b1df1e8..3afb81867d 100644 --- a/sections/shared/Layout/HomeLayout/Footer.tsx +++ b/sections/shared/Layout/HomeLayout/Footer.tsx @@ -49,25 +49,25 @@ const Footer = memo(() => { title: t('homepage.footer.use-kwenta.title'), links: [ { - key: 'how-to', - title: t('homepage.footer.use-kwenta.how-to'), - link: EXTERNAL_LINKS.Docs.DocsRoot, - }, - { - key: 'preps', + key: 'perps', title: t('homepage.footer.use-kwenta.perps'), - link: EXTERNAL_LINKS.Docs.Perpetuals, + link: EXTERNAL_LINKS.Trade.PerpsV2, }, { key: 'spot', title: t('homepage.footer.use-kwenta.spot'), - link: EXTERNAL_LINKS.Docs.Spot, + link: EXTERNAL_LINKS.Trade.Spot, }, { key: 'legacy', title: t('homepage.footer.use-kwenta.legacy'), link: EXTERNAL_LINKS.Trading.Legacy, }, + { + key: 'perps-v1', + title: t('homepage.footer.use-kwenta.perps-v1'), + link: EXTERNAL_LINKS.Trading.PerpsV1, + }, ], }, { diff --git a/sections/shared/Layout/HomeLayout/Header.tsx b/sections/shared/Layout/HomeLayout/Header.tsx index c13a2744e2..ede21237f2 100644 --- a/sections/shared/Layout/HomeLayout/Header.tsx +++ b/sections/shared/Layout/HomeLayout/Header.tsx @@ -8,7 +8,6 @@ import ArrowUpRightIcon from 'assets/svg/app/arrow-up-right-tg.svg'; import CaretDownGrayIcon from 'assets/svg/app/caret-down-gray-slim.svg'; import TwitterLogo from 'assets/svg/marketing/twitter-icon.svg'; import DiscordLogo from 'assets/svg/social/discord.svg'; -import MirrorLogo from 'assets/svg/social/mirror.svg'; import Button from 'components/Button'; import { FlexDivRow, FlexDivRowCentered } from 'components/layout/flex'; import { GridDivCenteredCol } from 'components/layout/grid'; @@ -89,12 +88,6 @@ const Header = memo(() => { onClick: () => window.open(EXTERNAL_LINKS.Social.Twitter, '_blank'), icon: <TwitterLogo />, }, - { - id: 'mirror', - label: t('homepage.nav.socials.mirror'), - onClick: () => window.open(EXTERNAL_LINKS.Social.Mirror, '_blank'), - icon: <MirrorLogo />, - }, ]; return ( diff --git a/state/exchange/reducer.ts b/state/exchange/reducer.ts index 1b9ad96065..d59000ab74 100644 --- a/state/exchange/reducer.ts +++ b/state/exchange/reducer.ts @@ -201,6 +201,8 @@ const exchangeSlice = createSlice({ state.txProvider = action.payload.txProvider; state.allowance = action.payload.allowance; state.oneInchQuoteError = false; + state.approvalStatus = FetchStatus.Idle; + state.isSubmitting = false; }); }, }); diff --git a/state/staking/actions.ts b/state/staking/actions.ts index 4e303c7467..f8d4a00b64 100644 --- a/state/staking/actions.ts +++ b/state/staking/actions.ts @@ -150,7 +150,7 @@ export const fetchClaimableRewards = createAsyncThunk< { claimableRewards: Awaited< ReturnType<KwentaSDK['kwentaToken']['getClaimableRewards']> - >['claimableRewards']; + >['claimableRewards'][]; totalRewards: string; }, void, @@ -160,9 +160,20 @@ export const fetchClaimableRewards = createAsyncThunk< staking: { epochPeriod }, } = getState(); - const { claimableRewards, totalRewards } = await sdk.kwentaToken.getClaimableRewards(epochPeriod); + const { + claimableRewards: claimableRewardsV1, + totalRewards: totalRewardsV1, + } = await sdk.kwentaToken.getClaimableRewards(epochPeriod, true); + + const { + claimableRewards: claimableRewardsV2, + totalRewards: totalRewardsV2, + } = await sdk.kwentaToken.getClaimableRewards(epochPeriod, false); - return { claimableRewards, totalRewards: totalRewards.toString() }; + return { + claimableRewards: [claimableRewardsV1, claimableRewardsV2], + totalRewards: totalRewardsV1.add(totalRewardsV2).toString(), + }; }); export const claimMultipleRewards = createAsyncThunk<void, void, ThunkConfig>( diff --git a/state/staking/types.ts b/state/staking/types.ts index 3a8ed1c49e..0914301161 100644 --- a/state/staking/types.ts +++ b/state/staking/types.ts @@ -19,7 +19,7 @@ export type StakingState = { totalVestable: string; escrowData: EscrowData<string>[]; totalRewards: string; - claimableRewards: ClaimParams[]; + claimableRewards: ClaimParams[][]; selectedEpoch?: number; stakeStatus: FetchStatus; unstakeStatus: FetchStatus; diff --git a/styles/common.tsx b/styles/common.tsx index a93674a042..557d84f719 100644 --- a/styles/common.tsx +++ b/styles/common.tsx @@ -192,7 +192,6 @@ export const SmallGoldenHeader = styled(Text.Body).attrs({ variant: 'bold' })` color: ${(props) => props.theme.colors.common.primaryYellow}; text-transform: uppercase; text-align: center; - letter-spacing: 0.65em; margin-bottom: 10px; cursor: default; ${media.lessThan('sm')` @@ -209,7 +208,6 @@ export const WhiteHeader = styled.div` font-variant: all-small-caps; text-transform: uppercase; text-align: center; - letter-spacing: 6px; cursor: default; ${media.lessThan('sm')` font-size: 32px; diff --git a/translations/en.json b/translations/en.json index 61943cf669..e48aa27b0e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -58,7 +58,6 @@ "socials": { "title": "Socials", "discord": "Discord", - "mirror": "Mirror", "twitter": "Twitter" }, "blog": "Blog", @@ -66,11 +65,11 @@ }, "hero": { "title": "TRADE SYNTHETIC PERPETUALS", - "copy": "Gain exposure to a variety of assets with up to <0>25x leverage</0> and <0>deep liquidity</0>." + "copy": "Gain exposure to a variety of assets with up to <0>25x leverage</0> and <0>deep liquidity</0>" }, "assets": { "title": "UNIQUE MARKETS", - "description": "Backed by Chainlink Oracles", + "description": "Backed by Chainlink and Pyth Oracles", "forex": "Forex", "crypto": "Cryptocurrencies", "commodity": "Commodities", @@ -263,7 +262,7 @@ "title": "Start trading today", "button": "View Exchange" }, - "copyright": "© 2022 kwenta, All Rights Reserved.", + "copyright": "© 2023 kwenta, All Rights Reserved.", "about-kwenta": { "title": "About Kwenta", "docs": "Docs", @@ -273,10 +272,10 @@ }, "use-kwenta": { "title": "Use KWENTA", - "how-to": "How to use Kwenta", - "perps": "Perpetuals", + "perps": "Perpetual Futures", "spot": "Spot Trading", - "legacy": "Legacy Exchange" + "legacy": "Legacy Exchange", + "perps-v1": "Legacy Perpetuals" }, "community": { "title": "Community", @@ -523,27 +522,28 @@ "title": "Trading Rewards", "mobile-title": "Rewards", "pending-for-rewards": "Pending...", - "trading-rewards-tooltip": "Only trades made using a Kwenta Cross Margin account are eligible for trading rewards.", + "trading-rewards-tooltip": "All trades on Perps V2 are eligible for trading rewards.", "spot-fee-paid": "Spot Fee Paid: Epoch {{EpochPeriod}}", "future-fee-paid": "Futures Fees Paid: Ep. {{EpochPeriod}}", "future-fee-paid-mobile": "Futures Fees Paid", "fees-paid": "Total Fees in Pool: Ep. {{EpochPeriod}}", "fees-paid-mobile": "Total Fees in Pool", - "estimated-rewards": "Estimated Reward*", - "estimated-reward-share": "Estimated Reward Share*", - "estimated-reward-share-mobile": "Est. Reward Share*", + "estimated-rewards": "Estimated Reward", + "estimated-reward-share": "Estimated Reward Share", + "estimated-reward-share-mobile": "Est. Reward Share", "trading-activity-reset": "Time until next epoch", "epoch": "Epoch {{EpochPeriod}}: {{EpochDate}}", "claimable-rewards-epoch": "Claimable Rewards: Epoch {{EpochPeriod}}", "claimable-rewards-all": "Claimable Trading Rewards", "claim-epoch": "Claim: Epoch {{EpochPeriod}}", "claim": "Claim", - "estimated-info": "* Estimated values do not reflect the final reward value and are subject to change as a result of future fees paid and staked amounts by other participants.", + "estimated-info": "Estimated values do not reflect the final reward value and are subject to change as a result of future fees paid and staked amounts by other participants.", "trading-reward": "Trading Reward:", "not-eligible": "Not Eligible", "eligible": "Eligible", "stake-to-earn": "Stake <0>$KWENTA</0> to earn more rewards", - "stake-to-start": "Stake <0>$KWENTA</0> to start earning rewards" + "stake-to-start": "Stake <0>$KWENTA</0> to start earning rewards", + "learn-more": "Learn More" }, "escrow": { "title": "Escrow",