@dydxfoundation/governance
Version:
dYdX governance smart contracts
238 lines (237 loc) • 14.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const units_1 = require("@ethersproject/units");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const ethereum_multicall_1 = require("ethereum-multicall");
const ethers_1 = require("ethers");
const Multicall2_json_1 = __importDefault(require("../../../abi/contracts/dependencies/makerdao/multicall2.sol/Multicall2.json"));
const DydxToken_json_1 = __importDefault(require("../../../abi/contracts/governance/token/DydxToken.sol/DydxToken.json"));
const LiquidityStakingV1_json_1 = __importDefault(require("../../../abi/contracts/liquidity/v1/LiquidityStakingV1.sol/LiquidityStakingV1.json"));
const SafetyModuleV1_json_1 = __importDefault(require("../../../abi/contracts/safety/v1/SafetyModuleV1.sol/SafetyModuleV1.json"));
const types_1 = require("../../../types");
const config_1 = require("../config");
const types_2 = require("../types");
const BaseService_1 = __importDefault(require("./BaseService"));
class DydxTokenService extends BaseService_1.default {
constructor(config, erc20Service, safetyModule, liquidityModule, merkleDistributor, hardhatTokenAddresses, hardhatTreasuryAddresses, hardhatMulticallAddress) {
super(config, types_1.DydxToken__factory);
this.decimalsOf = () => {
return config_1.DYDX_TOKEN_DECIMALS;
};
this.totalSupply = async () => {
return this.erc20Service.totalSupply(this.tokenAddress);
};
this.balanceOf = async (user) => {
return this.erc20Service.balanceOf(this.tokenAddress, user);
};
this.distributedToday = async () => {
var _a, _b, _c, _d;
const contractCallContext = [
{
reference: 'token',
contractAddress: this.tokenAddress,
abi: DydxToken_json_1.default,
calls: [
{
reference: 'transferRestriction',
methodName: '_transfersRestrictedBefore',
methodParameters: [],
},
],
},
{
reference: 'safetyModule',
contractAddress: this.safetyModule.contract.address,
abi: SafetyModuleV1_json_1.default,
calls: [
{
reference: 'rewardsPerSecond',
methodName: 'getRewardsPerSecond',
methodParameters: [],
},
],
},
{
reference: 'liquidityModule',
contractAddress: this.liquidityModule.contract.address,
abi: LiquidityStakingV1_json_1.default,
calls: [
{
reference: 'rewardsPerSecond',
methodName: 'getRewardsPerSecond',
methodParameters: [],
},
],
},
{
reference: 'multicall',
contractAddress: this._multicallData.multicallAddress,
abi: Multicall2_json_1.default,
calls: [
{
reference: 'timeLatest',
methodName: 'getCurrentBlockTimestamp',
methodParameters: [],
},
],
},
];
const [multiRes, rootUpdatedMetadata,] = await Promise.all([
this._multicallData.multicall.call(contractCallContext),
this.merkleDistributor.getRootUpdatedMetadata(),
]);
const tokenReturnContext = multiRes.results.token.callsReturnContext;
const multicallReturnContext = multiRes.results.multicall.callsReturnContext;
const transferRestriction = ethers_1.BigNumber.from((_a = tokenReturnContext.find(c => c.reference === 'transferRestriction')) === null || _a === void 0 ? void 0 : _a.returnValues[0].hex);
const currentTimestamp = ethers_1.BigNumber.from((_b = multicallReturnContext.find(c => c.reference === 'timeLatest')) === null || _b === void 0 ? void 0 : _b.returnValues[0].hex);
if (currentTimestamp.lt(transferRestriction)) {
// Distributed today is 0 during transfer restriction
return '0.0';
}
const safetyModuleReturnContext = multiRes.results.safetyModule.callsReturnContext;
const liquidityModuleReturnContext = multiRes.results.liquidityModule.callsReturnContext;
const safetyModuleRewardsPerSecond = (0, units_1.formatEther)(ethers_1.BigNumber.from((_c = safetyModuleReturnContext.find(c => c.reference === 'rewardsPerSecond')) === null || _c === void 0 ? void 0 : _c.returnValues[0].hex));
const liquidityModuleRewardsPerSecond = (0, units_1.formatEther)(ethers_1.BigNumber.from((_d = liquidityModuleReturnContext.find(c => c.reference === 'rewardsPerSecond')) === null || _d === void 0 ? void 0 : _d.returnValues[0].hex));
// the earliest rewards can become liquid is after the transfer restriction period
const lastClaimableMerkleRewardsTimestamp = Math.max(transferRestriction.toNumber(), rootUpdatedMetadata.lastRootUpdatedTimestamp);
let dailyMerkleDistributorRewards = new bignumber_js_1.default(0);
const secondsSinceClaimableMerkleRewards = currentTimestamp.sub(lastClaimableMerkleRewardsTimestamp);
if (rootUpdatedMetadata.numRootUpdates > 0 && secondsSinceClaimableMerkleRewards.lte(config_1.ONE_DAY_SECONDS.toNumber())) {
dailyMerkleDistributorRewards = new bignumber_js_1.default(config_1.MERKLE_DISTRIBUTOR_REWARDS_PER_EPOCH);
if (rootUpdatedMetadata.numRootUpdates === 1) {
// The first root update includes retroactive rewards
dailyMerkleDistributorRewards = dailyMerkleDistributorRewards.plus(config_1.RETROACTIVE_MINING_REWARDS);
}
}
const dailySafetyRewards = new bignumber_js_1.default(safetyModuleRewardsPerSecond)
.multipliedBy(config_1.ONE_DAY_SECONDS.toNumber());
let dailyLiquidityRewards = new bignumber_js_1.default(liquidityModuleRewardsPerSecond)
.multipliedBy(config_1.ONE_DAY_SECONDS.toNumber());
const secondsSinceTransferRestrictionEnd = currentTimestamp.sub(transferRestriction);
if (secondsSinceTransferRestrictionEnd.lte(config_1.ONE_DAY_SECONDS.toNumber())) {
// within one day of transfer restriction end, previous 36 days of liquidity module rewards
// become liquid
const preTransferRestrictionLiquidityStakingRewards = new bignumber_js_1.default(liquidityModuleRewardsPerSecond)
.multipliedBy(config_1.ONE_DAY_SECONDS.toNumber())
.multipliedBy(36);
dailyLiquidityRewards = dailyLiquidityRewards.plus(preTransferRestrictionLiquidityStakingRewards);
}
const distributedToday = dailySafetyRewards
.plus(dailyLiquidityRewards)
.plus(dailyMerkleDistributorRewards)
.toFixed();
return distributedToday;
};
this.circulatingSupply = async () => {
var _a, _b, _c, _d, _e, _f, _g;
const contractCallContext = [
{
reference: 'token',
contractAddress: this.tokenAddress,
abi: DydxToken_json_1.default,
calls: [
{
reference: 'transferRestriction',
methodName: '_transfersRestrictedBefore',
methodParameters: [],
},
{
reference: 'totalSupply',
methodName: 'totalSupply',
methodParameters: [],
},
{
reference: 'rewardsTreasuryBalance',
methodName: 'balanceOf',
methodParameters: [this.treasuryAddresses.REWARDS_TREASURY_ADDRESS],
},
{
reference: 'rewardsTreasuryVesterBalance',
methodName: 'balanceOf',
methodParameters: [this.treasuryAddresses.REWARDS_TREASURY_VESTER_ADDRESS],
},
{
reference: 'communityTreasuryBalance',
methodName: 'balanceOf',
methodParameters: [this.treasuryAddresses.COMMUNITY_TREASURY_ADDRESS],
},
{
reference: 'communityTreasuryVesterBalance',
methodName: 'balanceOf',
methodParameters: [this.treasuryAddresses.COMMUNITY_TREASURY_VESTER_ADDRESS],
},
],
},
{
reference: 'multicall',
contractAddress: this._multicallData.multicallAddress,
abi: Multicall2_json_1.default,
calls: [
{
reference: 'timeLatest',
methodName: 'getCurrentBlockTimestamp',
methodParameters: [],
},
],
},
];
const multiRes = await this._multicallData.multicall.call(contractCallContext);
const tokenReturnContext = multiRes.results.token.callsReturnContext;
const multicallReturnContext = multiRes.results.multicall.callsReturnContext;
const transferRestriction = ethers_1.BigNumber.from((_a = tokenReturnContext.find(c => c.reference === 'transferRestriction')) === null || _a === void 0 ? void 0 : _a.returnValues[0].hex);
const totalSupply = ethers_1.BigNumber.from((_b = tokenReturnContext.find(c => c.reference === 'totalSupply')) === null || _b === void 0 ? void 0 : _b.returnValues[0].hex);
const rewardsTreasuryBalance = ethers_1.BigNumber.from((_c = tokenReturnContext.find(c => c.reference === 'rewardsTreasuryBalance')) === null || _c === void 0 ? void 0 : _c.returnValues[0].hex);
const rewardsTreasuryVesterBalance = ethers_1.BigNumber.from((_d = tokenReturnContext.find(c => c.reference === 'rewardsTreasuryVesterBalance')) === null || _d === void 0 ? void 0 : _d.returnValues[0].hex);
const communityTreasuryBalance = ethers_1.BigNumber.from((_e = tokenReturnContext.find(c => c.reference === 'communityTreasuryBalance')) === null || _e === void 0 ? void 0 : _e.returnValues[0].hex);
const communityTreasuryVesterBalance = ethers_1.BigNumber.from((_f = tokenReturnContext.find(c => c.reference === 'communityTreasuryVesterBalance')) === null || _f === void 0 ? void 0 : _f.returnValues[0].hex);
const currentTimestamp = ethers_1.BigNumber.from((_g = multicallReturnContext.find(c => c.reference === 'timeLatest')) === null || _g === void 0 ? void 0 : _g.returnValues[0].hex);
if (currentTimestamp.lt(transferRestriction)) {
// Circulating supply is 0 before transfer restriction end
return '0.0';
}
// circulatingSupply = total supply - illiquid supply
// We consider all tokens owned by the treasuries + treasury vesters to be illiquid
// supply since it would require a governance vote + sufficient time for vesting to move the funds.
const circulatingSupplyWei = totalSupply
.sub((0, units_1.parseEther)(config_1.LOCKED_ALLOCATION))
.sub(rewardsTreasuryBalance)
.sub(rewardsTreasuryVesterBalance)
.sub(communityTreasuryBalance)
.sub(communityTreasuryVesterBalance);
return (0, units_1.formatEther)(circulatingSupplyWei);
};
this.erc20Service = erc20Service;
const { network } = this.config;
const isHardhatNetwork = network === types_2.Network.hardhat;
if (isHardhatNetwork &&
(!hardhatTokenAddresses ||
!hardhatTreasuryAddresses ||
!hardhatMulticallAddress)) {
throw new Error('Must specify token, treasury, and multicall addresses when on hardhat network');
}
const tokenAddresses = isHardhatNetwork ? hardhatTokenAddresses : config_1.dydxTokenAddresses[network];
this.tokenAddress = tokenAddresses.TOKEN_ADDRESS;
this.treasuryAddresses = isHardhatNetwork ? hardhatTreasuryAddresses : config_1.dydxTreasuryAddresses[network];
this.safetyModule = safetyModule;
this.liquidityModule = liquidityModule;
this.merkleDistributor = merkleDistributor;
const multicallAddress = isHardhatNetwork
? hardhatMulticallAddress
: config_1.multicallAddresses[network];
const multicall = new ethereum_multicall_1.Multicall({
multicallCustomContractAddress: multicallAddress.MULTICALL_ADDRESS,
ethersProvider: this.config.provider,
});
this._multicallData = {
multicall,
multicallAddress: multicallAddress.MULTICALL_ADDRESS,
};
}
get contract() {
return this.getContractInstance(this.tokenAddress);
}
}
exports.default = DydxTokenService;