Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(govern) feat: migrate veOlas to govern #74

Merged
merged 2 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 30 additions & 16 deletions apps/govern/common-util/functions/balance.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import isNil from 'lodash/isNil';

/**
* Return formatted number with appropriate suffix
*/
export const formatWeiNumber = ({
value,
minimumFractionDigits = 0,
maximumFractionDigits = 2,
}: {
value: number | string | undefined;
minimumFractionDigits?: number;
maximumFractionDigits?: number;
}) => {
if (isNil(value) || Number(value) === 0) return '0';

return new Intl.NumberFormat('en', {
notation: 'compact',
minimumFractionDigits,
maximumFractionDigits,
}).format(Number(value));
};

/**
* @param {number} balanceInWei
* @returns formatted balance with appropriate suffix
* Converts a number to a comma separated format
* eg: 1000000 => 1,000,000, 12345.67 => 12,345.67
*/
export const formatWeiBalance = (balanceInWei: number | string) => {
const formatNumberWithSuffix = (number: number) => {
if (number >= 1e9) {
return `${Math.floor((number / 1e9) * 10) / 10}B`;
}
if (number >= 1e6) {
return `${Math.floor((number / 1e6) * 10) / 10}M`;
}
if (number >= 1e3) {
return `${Math.floor((number / 1e3) * 10) / 10}k`;
}
return Math.floor(number * 10) / 10;
};
export const getCommaSeparatedNumber = (value: number | string | undefined) => {
if (isNil(value) || Number(value) === 0) return '0';

return formatNumberWithSuffix(parseFloat(`${balanceInWei}`));
return new Intl.NumberFormat('en', {
maximumFractionDigits: 2,
}).format(Number(value));
};
1 change: 1 addition & 0 deletions apps/govern/common-util/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './frontend-library';
export * from './requests';
export * from './web3';
export * from './balance';
export * from './time';
179 changes: 177 additions & 2 deletions apps/govern/common-util/functions/requests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readContract, readContracts } from '@wagmi/core';
import { AbiFunction } from 'viem';
import { ethers } from 'ethers';
import { AbiFunction, TransactionReceipt } from 'viem';
import { Address } from 'viem';
import { mainnet } from 'viem/chains';

Expand All @@ -13,7 +14,7 @@ import { RPC_URLS } from 'common-util/constants/rpcs';

import { getAddressFromBytes32 } from './addresses';
import { getUnixNextWeekStartTimestamp } from './time';
import { getVoteWeightingContract } from './web3';
import { getOlasContract, getVeOlasContract, getVoteWeightingContract } from './web3';

type VoteForNomineeWeightsParams = {
account: Address | undefined;
Expand Down Expand Up @@ -113,3 +114,177 @@ export const checkLockExpired = async (account: Address) => {

return result ? nextWeek >= (result as number) : false;
};

/**
* Approve amount of OLAS to be used
*/
export const approveOlasByOwner = ({ account, amount }: { account: Address; amount: bigint }) =>
Tanya-atatakai marked this conversation as resolved.
Show resolved Hide resolved
new Promise((resolve, reject) => {
const contract = getOlasContract();
const spender = (VE_OLAS.addresses as Record<number, string>)[mainnet.id];
const fn = contract.methods.approve(spender, amount).send({ from: account });

sendTransaction(fn, account, {
supportedChains: SUPPORTED_CHAINS,
rpcUrls: RPC_URLS,
})
.then((response) => {
resolve(response);
})
.catch((e) => {
window.console.log('Error occurred on approving OLAS by owner');
reject(e);
});
});

/**
* Check if `Approve` button can be clicked; `allowance` should be greater than or equal to the amount
*/
export const hasSufficientTokensRequest = ({
account,
amount,
}: {
account: Address;
amount: number;
}) =>
new Promise((resolve, reject) => {
const contract = getOlasContract();
const spender = (VE_OLAS.addresses as Record<number, string>)[mainnet.id];

contract.methods
.allowance(account, spender)
.call()
.then((response: bigint) => {
const responseInBg = ethers.toBigInt(response);

// Resolve false if the response amount is zero
if (responseInBg === ethers.toBigInt(0)) {
resolve(false);
return;
}

const amountBN = ethers.parseUnits(`${amount}`);
Tanya-atatakai marked this conversation as resolved.
Show resolved Hide resolved

// check if the allowance is greater than or equal to the amount input
resolve(responseInBg >= amountBN);
})
.catch((e: Error) => {
window.console.log('Error occurred on calling `allowance` method');
reject(e);
});
});

/**
* Create lock for veOLAS
*/
export const createLockRequest = async ({
account,
amount,
unlockTime,
}: {
account: Address;
amount: string;
unlockTime: number;
}) => {
const contract = getVeOlasContract();

try {
const createLockFn = contract.methods.createLock(amount, unlockTime);
const estimatedGas = await getEstimatedGasLimit(createLockFn, account);
const fn = createLockFn.send({ from: account, gasLimit: estimatedGas });

const response = await sendTransaction(fn, account, {
supportedChains: SUPPORTED_CHAINS,
rpcUrls: RPC_URLS,
});

return (response as TransactionReceipt)?.transactionHash;
} catch (error) {
window.console.log('Error occurred on creating lock for veOLAS');
throw error;
}
};

/**
* Increase Olas amount locked without modifying the lock time
*/
export const updateIncreaseAmount = async ({
account,
amount,
}: {
account: Address;
amount: string;
}) => {
const contract = getVeOlasContract();

try {
const increaseAmountFn = contract.methods.increaseAmount(amount);
const estimatedGas = await getEstimatedGasLimit(increaseAmountFn, account);
const fn = increaseAmountFn.send({ from: account, gasLimit: estimatedGas });

const response = await sendTransaction(fn, account, {
supportedChains: SUPPORTED_CHAINS,
rpcUrls: RPC_URLS,
});

return (response as TransactionReceipt)?.transactionHash;
} catch (e) {
window.console.log('Error occurred on increasing amount with estimated gas');
throw e;
}
};

/**
* Increase the unlock time without modifying the amount
*/
export const updateIncreaseUnlockTime = async ({
account,
time,
}: {
account: Address;
time: number;
}) => {
const contract = getVeOlasContract();

try {
const increaseUnlockTimeFn = contract.methods.increaseUnlockTime(time);
const estimatedGas = await getEstimatedGasLimit(increaseUnlockTimeFn, account);
const fn = increaseUnlockTimeFn.send({
from: account,
gasLimit: estimatedGas,
});

const response = await sendTransaction(fn, account, {
supportedChains: SUPPORTED_CHAINS,
rpcUrls: RPC_URLS,
});

return (response as TransactionReceipt)?.transactionHash;
} catch (error) {
window.console.log('Error occurred on increasing unlock time');
throw error;
}
};

/**
* Withdraw VeOlas
*/
export const withdrawVeolasRequest = async ({ account }: { account: Address }) => {
const contract = getVeOlasContract();

try {
const withdrawFn = contract.methods.withdraw();
const estimatedGas = await getEstimatedGasLimit(withdrawFn, account);
const fn = withdrawFn.send({ from: account, gasLimit: estimatedGas });

const response = await sendTransaction(fn, account, {
Tanya-atatakai marked this conversation as resolved.
Show resolved Hide resolved
supportedChains: SUPPORTED_CHAINS,
rpcUrls: RPC_URLS,
});

return (response as TransactionReceipt)?.transactionHash;
} catch (error) {
window.console.log('Error occurred on withdrawing veOlas');
throw error;
}
};
77 changes: 77 additions & 0 deletions apps/govern/common-util/functions/time.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NA } from 'libs/util-constants/src';

// Returns the closest Thursday in the future
// which is the start of the next week by Unix time
export const getUnixNextWeekStartTimestamp = () => {
Expand Down Expand Up @@ -25,3 +27,78 @@ export const getUnixWeekStartTimestamp = () => {

return result.getTime() / 1000;
};

const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

/**
* Get formatted date from milliseconds
* example, 1678320000000 => Mar 09 '23
*/
export function getFormattedDate(ms: number): string {
if (ms == 0) return NA;

const date = new Date(ms);
const month = MONTHS[date.getMonth()];
const day = date.getUTCDate();
const year = date.getUTCFullYear() % 100; // Get last two digits of year

return (
month +
' ' +
(day < 10 ? '0' : '') +
day.toString() +
" '" +
(year < 10 ? '0' : '') +
year.toString()
);
}

/**
* Get formatted date from milliseconds including time
* example, 1678320000000 => Mar 09 '2023 16:00
*/
export function getFullFormattedDate(ms: number): string {
if (ms == 0) return NA;
Tanya-atatakai marked this conversation as resolved.
Show resolved Hide resolved

const date = new Date(ms);
const month = MONTHS[date.getMonth()];
const day = date.getUTCDate();
const year = date.getUTCFullYear();
const hours = date.getUTCHours();
const minutes = date.getUTCMinutes();

return (
month +
' ' +
(day < 10 ? '0' : '') +
day.toString() +
' ' +
year.toString() +
', ' +
(hours < 10 ? '0' : '') +
hours.toString() +
':' +
(minutes < 10 ? '0' : '') +
minutes.toString()
);
}

export const dateInMs = (time: number) => {
if (!time) return 0;
return Math.round(new Date(time).getTime());
};

/*
* Returns the remaining time in seconds between unlockTime and the current time.
* Steps:
* 1. Convert unlockTime to a timestamp
* 2. Get the current time as a timestamp
* 3. Calculate the difference between the future timestamp and the current timestamp, convert to seconds.
*/
export const getRemainingTimeInSeconds = (unlockTime?: number) => {
if (!unlockTime) return 0;

const futureDateInTimeStamp = new Date(unlockTime).getTime();
const todayDateInTimeStamp = new Date().getTime();
return Math.round((futureDateInTimeStamp - todayDateInTimeStamp) / 1000);
};
20 changes: 17 additions & 3 deletions apps/govern/common-util/functions/web3.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { mainnet } from 'viem/chains';
import Web3 from 'web3';
import { AbiItem } from 'web3-utils';

import { VOTE_WEIGHTING } from 'libs/util-contracts/src/lib/abiAndAddresses';
import { OLAS, VE_OLAS, VOTE_WEIGHTING } from 'libs/util-contracts/src/lib/abiAndAddresses';

import { getChainId, getProvider } from 'common-util/functions/frontend-library';

Expand All @@ -26,9 +27,22 @@ const getContract = (abi: AbiItem[], contractAddress: string, chainId?: number)
};

export const getVoteWeightingContract = () => {
const { chainId } = getWeb3Details();
const abi = VOTE_WEIGHTING.abi as AbiItem[];
const address = (VOTE_WEIGHTING.addresses as Record<number, string>)[chainId as number];
const address = (VOTE_WEIGHTING.addresses as Record<number, string>)[mainnet.id];
const contract = getContract(abi, address);
return contract;
};

export const getOlasContract = () => {
const abi = OLAS.abi as AbiItem[];
const address = (OLAS.addresses as Record<number, string>)[mainnet.id];
const contract = getContract(abi, address);
return contract;
};

export const getVeOlasContract = () => {
const abi = VE_OLAS.abi as AbiItem[];
const address = (VE_OLAS.addresses as Record<number, string>)[mainnet.id];
const contract = getContract(abi, address);
return contract;
};
4 changes: 2 additions & 2 deletions apps/govern/components/Contracts/ContractsList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ describe('<ContractsList />', () => {

// current weight column
expect(screen.getByText(/10.12%/)).toBeInTheDocument();
expect(screen.getByText(/298.8k veOLAS/)).toBeInTheDocument();
expect(screen.getByText(/298.89K veOLAS/)).toBeInTheDocument();

// next weight column
expect(screen.getByText(/25.56%/)).toBeInTheDocument();
expect(screen.getByText(/297.4k veOLAS/)).toBeInTheDocument();
expect(screen.getByText(/297.43K veOLAS/)).toBeInTheDocument();
});

describe('Already voted', () => {
Expand Down
Loading
Loading