UNPKG

@dydxfoundation/governance

Version:
147 lines (146 loc) 8.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = require("chai"); const util_1 = require("../../src/lib/util"); const impersonate_account_1 = require("../../src/migrations/helpers/impersonate-account"); const types_1 = require("../../src/types"); const describe_contract_1 = require("../helpers/describe-contract"); const evm_1 = require("../helpers/evm"); const get_address_with_role_1 = require("../helpers/get-address-with-role"); const staking_helper_1 = require("../helpers/staking-helper"); // Snapshots const borrowerHasBorrowed = 'BorrowerHasBorrowed'; const borrowerRestrictedSnapshot = 'BorrowerRestrictedSnapshot'; const stakerInitialBalance = 1000000; // Contracts. let deployer; let liquidityStaking; let mockStakedToken; let mockStarkPerpetual; let shortTimelockSigner; // Users. let stakers; let borrowerStarkProxies; let contract; async function init(ctx) { ({ liquidityStaking, deployer, } = ctx); mockStakedToken = ctx.dydxCollateralToken; mockStarkPerpetual = ctx.starkPerpetual; // Users. stakers = ctx.users.slice(1, 3); // 2 stakers const borrowers = await Promise.all(ctx.starkProxies.map(async (b) => { const ownerAddress = await (0, get_address_with_role_1.findAddressWithRole)(b, types_1.Role.OWNER_ROLE); return (0, impersonate_account_1.impersonateAndFundAccount)(ownerAddress); })); borrowerStarkProxies = borrowers.map((b, i) => ctx.starkProxies[i].connect(b)); // Grant roles. const deployerAddress = await deployer.getAddress(); await Promise.all(borrowerStarkProxies.map(async (b) => { await b.grantRole((0, util_1.getRole)(types_1.Role.EXCHANGE_OPERATOR_ROLE), deployerAddress); await b.grantRole((0, util_1.getRole)(types_1.Role.BORROWER_ROLE), deployerAddress); })); shortTimelockSigner = await (0, impersonate_account_1.impersonateAndFundAccount)(ctx.shortTimelock.address); // Use helper class to automatically check contract invariants after every update. contract = new staking_helper_1.StakingHelper(ctx, liquidityStaking, mockStakedToken, ctx.rewardsTreasury.address, ctx.deployer, shortTimelockSigner, stakers.concat(borrowers), false); // Mint staked tokens and set allowances. await Promise.all(stakers.map((s) => contract.mintAndApprove(s, stakerInitialBalance))); // Initial stake of 1M. await contract.stake(stakers[0], stakerInitialBalance / 4); await contract.stake(stakers[1], (stakerInitialBalance / 4) * 3); // Allocations: [40%, 60%], remaining borrowers 0% await contract.setBorrowerAllocations({ [borrowerStarkProxies[0].address]: 0.4, [borrowerStarkProxies[1].address]: 0.6, [borrowerStarkProxies[2].address]: 0.0, [borrowerStarkProxies[3].address]: 0.0, [borrowerStarkProxies[4].address]: 0.0, }); // Borrow full amount. await contract.elapseEpoch(); await contract.fullBorrowViaProxy(borrowerStarkProxies[0], stakerInitialBalance * 0.4); (0, chai_1.expect)(await mockStakedToken.balanceOf(borrowerStarkProxies[0].address)).to.equal(stakerInitialBalance * 0.4); contract.saveSnapshot(borrowerHasBorrowed); } (0, describe_contract_1.describeContractHardhatRevertBefore)('SP2Exchange', init, () => { describe('interacting with the exchange', () => { const starkKey = 123; let postInitSnapshotId; before(async () => { postInitSnapshotId = await (0, evm_1.evmSnapshot)(); }); beforeEach(async () => { contract.loadSnapshot(borrowerHasBorrowed); await (0, evm_1.evmReset)(postInitSnapshotId); postInitSnapshotId = await (0, evm_1.evmSnapshot)(); }); after(async () => { await (0, evm_1.evmReset)(postInitSnapshotId); }); it('can add a STARK key that is registered to the StarkProxy contract', async () => { await mockStarkPerpetual.registerUser(borrowerStarkProxies[0].address, starkKey, []); await borrowerStarkProxies[0].allowStarkKey(starkKey); }); it('cannot add a STARK key that is not registered', async () => { await (0, chai_1.expect)(borrowerStarkProxies[0].allowStarkKey(starkKey)).to.be.revertedWith('USER_UNREGISTERED'); }); it('cannot add a STARK key that is registered to another address', async () => { await mockStarkPerpetual.registerUser(borrowerStarkProxies[1].address, starkKey, []); await (0, chai_1.expect)(borrowerStarkProxies[0].allowStarkKey(starkKey)).to.be.revertedWith('SP2Owner: STARK key not registered to this contract'); }); it('cannot deposit to a STARK key that has not been allowed', async () => { await mockStarkPerpetual.registerUser(borrowerStarkProxies[0].address, starkKey, []); await (0, chai_1.expect)(borrowerStarkProxies[0].depositToExchange(starkKey, 0, 0, 0)).to.be.revertedWith('SP1Storage: STARK key is not on the allowlist'); }); it('cannot deposit to a STARK key that has been disallowed', async () => { await mockStarkPerpetual.registerUser(borrowerStarkProxies[0].address, starkKey, []); await borrowerStarkProxies[0].allowStarkKey(starkKey); await borrowerStarkProxies[0].disallowStarkKey(starkKey); await (0, chai_1.expect)(borrowerStarkProxies[0].depositToExchange(starkKey, 0, 0, 0)).to.be.revertedWith('SP1Storage: STARK key is not on the allowlist'); }); it('can deposit and withdraw, and then repay', async () => { await mockStarkPerpetual.registerUser(borrowerStarkProxies[0].address, starkKey, []); await borrowerStarkProxies[0].allowStarkKey(starkKey); await borrowerStarkProxies[0].depositToExchange(starkKey, 456, 789, stakerInitialBalance * 0.4); await borrowerStarkProxies[0].withdrawFromExchange(starkKey, 456); await contract.repayBorrowViaProxy(borrowerStarkProxies[0], stakerInitialBalance * 0.4); }); }); describe('after restricted by guardian', () => { const starkKey = 123; let guardianSnapshot; before(async () => { // Restrict borrowing. await (0, chai_1.expect)(borrowerStarkProxies[0].connect(shortTimelockSigner).guardianSetBorrowingRestriction(true)) .to.emit(borrowerStarkProxies[0], 'BorrowingRestrictionChanged') .withArgs(true); // Register with the exchange. await mockStarkPerpetual.registerUser(borrowerStarkProxies[0].address, starkKey, []); contract.saveSnapshot(borrowerRestrictedSnapshot); guardianSnapshot = await (0, evm_1.evmSnapshot)(); }); beforeEach(async () => { contract.loadSnapshot(borrowerRestrictedSnapshot); await (0, evm_1.evmReset)(guardianSnapshot); guardianSnapshot = await (0, evm_1.evmSnapshot)(); }); it('cannot deposit borrowed funds to the exchange', async () => { await borrowerStarkProxies[0].allowStarkKey(starkKey); await (0, chai_1.expect)(borrowerStarkProxies[0].depositToExchange(starkKey, 456, 789, stakerInitialBalance * 0.4)).to.be.revertedWith('SP2Exchange: Cannot deposit borrowed funds to the exchange while Restricted'); }); it('can still deposit own funds to the exchange', async () => { await borrowerStarkProxies[0].allowStarkKey(starkKey); // Transfer some funds directly to the StarkProxy contract. const ownFundsAmount = 12340; await mockStakedToken .connect(stakers[0]) .transfer(borrowerStarkProxies[0].address, ownFundsAmount); // Deposit own funds to the exchange. await borrowerStarkProxies[0].depositToExchange(starkKey, 456, 789, ownFundsAmount); // Cannot deposit any more. await (0, chai_1.expect)(borrowerStarkProxies[0].depositToExchange(starkKey, 456, 789, 1)).to.be.revertedWith('SP2Exchange: Cannot deposit borrowed funds to the exchange while Restricted'); }); }); });