Skip to content

Commit

Permalink
add: allow to restore reserve in batches
Browse files Browse the repository at this point in the history
  • Loading branch information
sirpy committed Nov 6, 2024
1 parent f506b1c commit 715b6d4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 33 deletions.
10 changes: 3 additions & 7 deletions contracts/reserve/DistributionHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ contract DistributionHelper is
0x467719aD09025FcC6cF6F8311755809d45a5E5f3;

enum TransferType {
FuseBridge,
DEPRECATED_FuseBridge,
LayerZeroBridge,
AxelarBridge,
Contract
Expand Down Expand Up @@ -235,12 +235,8 @@ contract DistributionHelper is
DistributionRecipient storage _recipient,
uint256 _amount
) internal {
if (_recipient.transferType == TransferType.FuseBridge) {
nativeToken().transferAndCall(
fuseBridge,
_amount,
abi.encodePacked(_recipient.addr)
);
if (_recipient.transferType == TransferType.DEPRECATED_FuseBridge) {
revert("DEPRECATED");
} else if (_recipient.transferType == TransferType.LayerZeroBridge) {
nativeToken().approve(address(mpbBridge), _amount);
(uint256 lzFee, ) = ILayerZeroFeeEstimator(address(mpbBridge))
Expand Down
73 changes: 67 additions & 6 deletions contracts/utils/ReserveRestore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,84 @@ contract ReserveRestore {
NameService ns;
uint256 public constant LOCKED_HACKED_FUNDS = 971921364208;
bool public executed;
address owner;

constructor(NameService _ns) {
ns = _ns;
owner = msg.sender;
}

function upgrade(address daiFrom) external {
function end() external {
require(msg.sender == owner, "not owner");
Controller ctrl = Controller(ns.getAddress("CONTROLLER"));
address avatar = ns.dao().avatar();

// prevent executing again}
require(ctrl.unregisterSelf(avatar), "unregistering failed");
}

function donate(address daiFrom, uint256 amount) external {
require(msg.sender == owner, "not owner");
address avatar = ns.dao().avatar();

GoodReserveCDai reserve = GoodReserveCDai(ns.getAddress("RESERVE"));
ERC20(ns.getAddress("DAI")).transferFrom(daiFrom, address(this), amount);
uint256 daiBalance = ERC20(ns.getAddress("DAI")).balanceOf(address(this));
cERC20 cdai = cERC20(ns.getAddress("CDAI"));
ERC20 dai = ERC20(ns.getAddress("DAI"));

dai.approve(address(cdai), daiBalance);
//Mint cDAIs
uint256 cDaiResult = cdai.mint(daiBalance);
require(cDaiResult == 0, "Minting cDai failed");
uint256 cdaiBalance = cdai.balanceOf(address(this));
cdai.transfer(address(reserve), cdaiBalance);
cdaiBalance = cdai.balanceOf(address(reserve));

uint256 gdSupply = ERC20(ns.getAddress("GOODDOLLAR")).totalSupply() -
LOCKED_HACKED_FUNDS;
console.log("supply: %s", gdSupply);
// get 0.0001 dai price in cdai
uint256 currentPrice = reserve.currentPrice();
console.log("currentPrice: %s", currentPrice);

console.log("cdaiBalance: %s", cdaiBalance);

// given price calculate the reserve ratio
uint32 reserveRatio = uint32(
(cdaiBalance * 1e2 * 1e6) / (currentPrice * gdSupply)
); // mul by 1e2 to cover gd precision, cdaibalance precision=initialprice, mul by 1e6 to receive result in the precision of reserveRatio(1e6)
console.log("reserveRatio: %s", reserveRatio);
Controller ctrl = Controller(ns.getAddress("CONTROLLER"));
// function initializeToken(
// ERC20 _token,
// uint256 _gdSupply,
// uint256 _tokenSupply,
// uint32 _reserveRatio,
// uint256 _lastExpansion
// )
(bool ok, ) = ctrl.genericCall(
address(reserve.getMarketMaker()),
abi.encodeCall(
GoodMarketMaker.initializeToken,
(cdai, gdSupply, cdaiBalance, reserveRatio, block.timestamp)
),
address(avatar),
0
);
require(ok, "initializeToken failed");
}

function upgrade(address daiFrom, uint256 amount) external {
require(msg.sender == owner, "not owner");
require(executed == false, "already upgraded");
executed = true;
address avatar = ns.dao().avatar();

GoodReserveCDai reserve = GoodReserveCDai(ns.getAddress("RESERVE"));
ERC20(ns.getAddress("DAI")).transferFrom(daiFrom, address(this), 200000e18);
ERC20(ns.getAddress("DAI")).transferFrom(daiFrom, address(this), amount);
uint256 daiBalance = ERC20(ns.getAddress("DAI")).balanceOf(address(this));
require(daiBalance >= 200000e18, "not enough reserve");
require(daiBalance >= 50000e18, "not enough reserve");
cERC20 cdai = cERC20(ns.getAddress("CDAI"));
ERC20 dai = ERC20(ns.getAddress("DAI"));

Expand Down Expand Up @@ -89,8 +153,5 @@ contract ReserveRestore {
);

require(ok, "setContributionRatio failed");

// prevent executing again
require(ctrl.unregisterSelf(avatar), "unregistering failed");
}
}
45 changes: 35 additions & 10 deletions scripts/proposals/reserveRestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* - upgrade governance
* - unpause reserve
* - unpause goodfundmanager
* - switch fuse distribution to use lz bridge insted of deprecated fuse bridge
*
*
* Fuse:
Expand All @@ -48,14 +49,17 @@ import { executeViaGuardian, executeViaSafe, verifyProductionSigner } from "../m
import ProtocolSettings from "../../releases/deploy-settings.json";

import dao from "../../releases/deployment.json";
import { ExchangeHelper, FuseOldBridgeKill, GoodFundManager, GoodMarketMaker, GoodReserveCDai, IGoodDollar } from "../../types";
import { ExchangeHelper, FuseOldBridgeKill, GoodFundManager, GoodMarketMaker, GoodReserveCDai, IGoodDollar, ReserveRestore } from "../../types";
let { name: networkName } = network;


const isSimulation = network.name === "hardhat" || network.name === "fork" || network.name === "localhost";

// hacker and hacked multichain bridge accounts
const LOCKED_ACCOUNTS = ["0xeC577447D314cf1e443e9f4488216651450DBE7c", "0xD17652350Cfd2A37bA2f947C910987a3B1A1c60d", "0x6738fA889fF31F82d9Fe8862ec025dbE318f3Fde"]
const INITIAL_DAI = ethers.utils.parseEther("100000") // 100k
// reserve funder (goodlabs safe)
const funder = "0xF0652a820dd39EC956659E0018Da022132f2f40a"

export const upgradeMainnet = async network => {
const isProduction = networkName.includes("production");
Expand Down Expand Up @@ -102,12 +106,11 @@ export const upgradeMainnet = async network => {
const govImpl = await ethers.deployContract("CompoundVotingMachine");
const distHelperImplt = await ethers.deployContract("DistributionHelper");
const marketMakerImpl = await ethers.deployContract("GoodMarketMaker");
const upgradeImpl = await ethers.deployContract("ReserveRestore", [release.NameService]);
const upgradeImpl = await ethers.deployContract("ReserveRestore", [release.NameService]) as ReserveRestore;

const gd = (await ethers.getContractAt("IGoodDollar", release.GoodDollar)) as IGoodDollar;

// reserve funder (goodlabs safe)
const funder = "0xF0652a820dd39EC956659E0018Da022132f2f40a"

// test blacklisting to prevent burn by hacker
if (isSimulation) {

Expand Down Expand Up @@ -155,9 +158,10 @@ export const upgradeMainnet = async network => {
release.StakersDistribution, //upgrade stakers dist
release.GoodMarketMaker, //upgrade mm
release.CompoundVotingMachine, // upgrade gov
release.DistributionHelper, // switch to lz bridge for fuse
release.ExchangeHelper, // activate upgrade changes
release.Controller,
upgradeImpl.address,
// upgradeImpl.address,
release.GuardiansSafe + "_" + release.GoodReserveCDai
];

Expand All @@ -180,9 +184,10 @@ export const upgradeMainnet = async network => {
"upgradeTo(address)",
"upgradeTo(address)",
"upgradeTo(address)",
"addOrUpdateRecipient((uint32,uint32,address,uint8))",
"setAddresses()",
"registerScheme(address,bytes32,bytes4,address)", // give upgrade contract permissions
"upgrade(address)",
// "upgrade(address, uint256)",
"unpause()"
];

Expand All @@ -203,6 +208,10 @@ export const upgradeMainnet = async network => {
ethers.utils.defaultAbiCoder.encode(["address"], [stakersDistImpl.address]),
ethers.utils.defaultAbiCoder.encode(["address"], [marketMakerImpl.address]),
ethers.utils.defaultAbiCoder.encode(["address"], [govImpl.address]),
ethers.utils.defaultAbiCoder.encode(
["uint32", "uint32", "address", "uint8"],
[1000, 122, dao["production"].UBIScheme, 1] //10% chainId 122 ubischeme 1-lz bridge
),
"0x", //setAddresses
ethers.utils.defaultAbiCoder.encode(
["address", "bytes32", "bytes4", "address"],
Expand All @@ -213,7 +222,6 @@ export const upgradeMainnet = async network => {
release.Avatar
]
),
ethers.utils.defaultAbiCoder.encode(["address"], [funder]),
"0x"
];

Expand All @@ -239,17 +247,21 @@ export const upgradeMainnet = async network => {
}

if (isSimulation) {
await mainnetPostChecks()
await mainnetPostChecks(upgradeImpl)
}
};

const mainnetPostChecks = async () => {
const mainnetPostChecks = async (upgradeImpl: ReserveRestore) => {
networkName = "production-mainnet";
let release: { [key: string]: any } = dao[networkName];

let [root, ...signers] = await ethers.getSigners();
const gd = await ethers.getContractAt("IGoodDollar", release.GoodDollar);

//execute the reserve initialization
(await upgradeImpl.upgrade(funder, INITIAL_DAI)).wait()


const locked = await ethers.getImpersonatedSigner(LOCKED_ACCOUNTS[0]);
const tx = await gd
.connect(locked)
Expand Down Expand Up @@ -299,12 +311,25 @@ const mainnetPostChecks = async () => {
console.log("ubiEvents after collect interest:", ubiEvents)
// check expansion after some time
await time.increase(365 * 60 * 60 * 24)
const gdSupplyBeforeExpansion = await gd.totalSupply();
const reserveStateBeforeYearExpansion = await mm.reserveTokens(release.cDAI)

const expansionTX = await (await gfm.collectInterest([], false)).wait()
const ubiExpansionEvents = last(await reserve.queryFilter(reserve.filters.UBIMinted(), -1))
console.log("gfm events after 1 year expansion:", expansionTX.events?.filter(_ => _.event === 'FundsTransferred'))
console.log("ubiEvents after 1 year expansion:", ubiExpansionEvents)
const reserveStateAfterYearExpansion = await mm.reserveTokens(release.cDAI)
console.log({ reserveStateAfterYearExpansion })
const gdSupplyAfterExpansion = await gd.totalSupply();
console.log({ reserveStateAfterYearExpansion, gdSupplyAfterExpansion, gdSupplyBeforeExpansion, reserveStateBeforeYearExpansion })

//execute the reserve initialization
await (await upgradeImpl.donate(funder, INITIAL_DAI)).wait()
const [cdaiPriceAfterDonation, daiPriceAfterDonation] = await (await Promise.all([reserve.currentPrice(), reserve.currentPriceDAI()])).map(_ => _.toNumber())
console.log("price after dai donation:", { cdaiPriceAfterDonation, daiPriceAfterDonation })
const reserveStateAfterDonation = await mm.reserveTokens(release.cDAI)
console.log({ reserveStateAfterDonation })

await (await upgradeImpl.end()).wait()
}
export const upgradeFuse = async network => {
let [root] = await ethers.getSigners();
Expand Down
20 changes: 10 additions & 10 deletions test/reserve/DistributionHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe("DistributionHelper", () => {
expect(dr.transferType).to.equal(2);
});

it("should distribute via fuse bridge", async () => {
it("should not distribute via fuse bridge", async () => {
const { distHelper, bridge } = await loadFixture(fixture);

const recipient = signers[0];
Expand All @@ -215,15 +215,15 @@ describe("DistributionHelper", () => {
await genericCall(distHelper.address, encodedCall, avatar.address, 0);

await goodDollar.mint(distHelper.address, "100000000000");
await distHelper.onDistribution("100000000000");
expect(await goodDollar.balanceOf(bridge.address)).to.equal(
(100000000000 * 2000) / 10000
);

const events = await bridge.queryFilter(bridge.filters.OnToken());
expect(events[0].args.sender).to.equal(distHelper.address);
expect(events[0].args.amount).to.equal((100000000000 * 2000) / 10000);
expect(events[0].args.data).to.equal(recipient.address.toLowerCase());
await expect(distHelper.onDistribution("100000000000")).revertedWith("DEPRECATED");
// expect(await goodDollar.balanceOf(bridge.address)).to.equal(
// (100000000000 * 2000) / 10000
// );

// const events = await bridge.queryFilter(bridge.filters.OnToken());
// expect(events[0].args.sender).to.equal(distHelper.address);
// expect(events[0].args.amount).to.equal((100000000000 * 2000) / 10000);
// expect(events[0].args.data).to.equal(recipient.address.toLowerCase());
});

it("should distribute via layerzero bridge", async () => {
Expand Down

0 comments on commit 715b6d4

Please sign in to comment.