Skip to content

Commit

Permalink
(govern) feat: migrate veOlas to govern
Browse files Browse the repository at this point in the history
  • Loading branch information
Atatakai authored and Atatakai committed Aug 8, 2024
1 parent 392c02b commit 0e15424
Show file tree
Hide file tree
Showing 33 changed files with 2,031 additions and 31 deletions.
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 }) =>
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}`);

// 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, {
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;

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

0 comments on commit 0e15424

Please sign in to comment.