-
Notifications
You must be signed in to change notification settings - Fork 3
[VPD-314]: Add enterMarketBehalf support to Comptroller #640
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
Changes from all commits
8f59924
279ba2e
1786fb3
486a08c
fec05a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| import { TransactionResponse } from "@ethersproject/providers"; | ||
| import { expect } from "chai"; | ||
| import { Contract } from "ethers"; | ||
| import { parseEther } from "ethers/lib/utils"; | ||
| import { ethers } from "hardhat"; | ||
| import { NETWORK_ADDRESSES } from "src/networkAddresses"; | ||
| import { expectEvents, initMainnetUser } from "src/utils"; | ||
| import { forking, testVip } from "src/vip-framework"; | ||
|
|
||
| import vip573 from "../../vips/vip-573/bscmainnet"; | ||
| import COMPTROLLER_ABI from "./abi/Comptroller.json"; | ||
| import DIAMOND_ABI from "./abi/Diamond.json"; | ||
|
|
||
| const { bscmainnet } = NETWORK_ADDRESSES; | ||
|
|
||
| const OLD_MARKET_FACET = "0x7ec871BA4248CC443a994f2febeDFB96DAe444F1"; | ||
|
|
||
| const NEW_MARKET_FACET = "0x87FdF72FA2fB29Cb43f03aCa261A8DC2C613a860"; | ||
|
|
||
| // Function selector for enterMarketBehalf(address,address) | ||
| const ENTER_MARKET_BEHALF_SELECTOR = "0xd585c3c6"; | ||
|
|
||
| // Test accounts | ||
| const TEST_USER = "0x14A1c22EF6d2eF6cE33c0b018d8A34D02021e5c8"; | ||
| const TEST_DELEGATE = "0x9cc6f5f16498fceef4d00a350bd8f8921d304dc9"; | ||
| const VUSDT = "0xfD5840Cd36d94D7229439859C0112a4185BC0255"; | ||
|
|
||
| forking(69628476, async () => { | ||
| const provider = ethers.provider; | ||
| let unitroller: Contract; | ||
|
|
||
| let marketFacetFunctionSelectors: string[]; | ||
|
|
||
| before(async () => { | ||
| unitroller = new ethers.Contract(bscmainnet.UNITROLLER, COMPTROLLER_ABI, provider); | ||
|
|
||
| // Get current MarketFacet function selectors | ||
| marketFacetFunctionSelectors = await unitroller.facetFunctionSelectors(OLD_MARKET_FACET); | ||
| }); | ||
|
|
||
| describe("Pre-VIP behavior", async () => { | ||
| it("MarketFacet should have old implementation", async () => { | ||
| expect(await unitroller.facetFunctionSelectors(OLD_MARKET_FACET)).to.deep.equal(marketFacetFunctionSelectors); | ||
| expect(await unitroller.facetFunctionSelectors(NEW_MARKET_FACET)).to.deep.equal([]); | ||
| }); | ||
|
|
||
| it("enterMarketBehalf function should not exist", async () => { | ||
| expect(marketFacetFunctionSelectors).to.not.include(ENTER_MARKET_BEHALF_SELECTOR); | ||
| }); | ||
|
|
||
| it("unitroller should contain old MarketFacet address", async () => { | ||
| expect(await unitroller.facetAddresses()).to.include(OLD_MARKET_FACET); | ||
| expect(await unitroller.facetAddresses()).to.not.include(NEW_MARKET_FACET); | ||
| }); | ||
| }); | ||
|
|
||
| testVip("vip-573", await vip573(), { | ||
| callbackAfterExecution: async (txResponse: TransactionResponse) => { | ||
| await expectEvents(txResponse, [DIAMOND_ABI], ["DiamondCut"], [1]); | ||
| }, | ||
| }); | ||
|
|
||
| describe("Post-VIP behavior", async () => { | ||
| it("MarketFacet function selectors should be updated for new facet address", async () => { | ||
| const newMarketFacetFunctionSelectors = [ENTER_MARKET_BEHALF_SELECTOR]; | ||
|
|
||
| const expectSelectors = [...marketFacetFunctionSelectors, ...newMarketFacetFunctionSelectors].sort(); | ||
| const updatedSelectors = [...(await unitroller.facetFunctionSelectors(NEW_MARKET_FACET))].sort(); | ||
|
|
||
| expect(updatedSelectors).to.deep.equal(expectSelectors); | ||
| expect(await unitroller.facetFunctionSelectors(OLD_MARKET_FACET)).to.deep.equal([]); | ||
| }); | ||
|
|
||
| it("unitroller should contain new MarketFacet address", async () => { | ||
| expect(await unitroller.facetAddresses()).to.include(NEW_MARKET_FACET); | ||
| expect(await unitroller.facetAddresses()).to.not.include(OLD_MARKET_FACET); | ||
| }); | ||
|
|
||
| it("enterMarketBehalf function should exist in new facet", async () => { | ||
| const newFacetSelectors = await unitroller.facetFunctionSelectors(NEW_MARKET_FACET); | ||
| expect(newFacetSelectors).to.include(ENTER_MARKET_BEHALF_SELECTOR); | ||
| }); | ||
| it("should allow approved delegate to enter market on behalf of user", async () => { | ||
| const user = await initMainnetUser(TEST_USER, parseEther("10000")); | ||
|
|
||
| // First, exit the market if already in it | ||
| try { | ||
| await unitroller.connect(user).exitMarket(VUSDT); | ||
| } catch (e) { | ||
| // Market might not be entered, continue | ||
| } | ||
|
|
||
| // Approve delegate | ||
| await unitroller.connect(user).updateDelegate(TEST_DELEGATE, true); | ||
|
|
||
| // Switch to delegate | ||
| const delegate = await initMainnetUser(TEST_DELEGATE, parseEther("10000")); | ||
|
|
||
| // Check membership before | ||
| const membershipBefore = await unitroller.checkMembership(TEST_USER, VUSDT); | ||
| expect(membershipBefore).to.be.false; | ||
|
|
||
| await expect(await unitroller.connect(delegate).enterMarketBehalf(TEST_USER, VUSDT)) | ||
| .to.emit(unitroller, "MarketEntered") | ||
| .withArgs(VUSDT, TEST_USER); | ||
|
|
||
| // Check membership after | ||
| const membershipAfter = await unitroller.checkMembership(TEST_USER, VUSDT); | ||
| expect(membershipAfter).to.be.true; | ||
| }); | ||
|
|
||
| it("should revert when unapproved delegate tries to enter market", async () => { | ||
| const userSigner = await initMainnetUser(TEST_DELEGATE, parseEther("10000")); | ||
| const comptroller = unitroller.connect(userSigner); | ||
|
|
||
| try { | ||
| await comptroller.exitMarket(VUSDT); | ||
| } catch (e) { | ||
| // Market might not be entered, continue | ||
| } | ||
|
|
||
| const delegateSigner = await initMainnetUser(TEST_USER, parseEther("10000")); | ||
| const comptrollerDelegate = unitroller.connect(delegateSigner); | ||
|
|
||
| // Should revert with NotAnApprovedDelegate | ||
| await expect(comptrollerDelegate.enterMarketBehalf(TEST_DELEGATE, VUSDT)).to.be.revertedWithCustomError( | ||
| comptrollerDelegate, | ||
| "NotAnApprovedDelegate", | ||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| import { TransactionResponse } from "@ethersproject/providers"; | ||
| import { expect } from "chai"; | ||
| import { Contract } from "ethers"; | ||
| import { parseEther } from "ethers/lib/utils"; | ||
| import { ethers } from "hardhat"; | ||
| import { NETWORK_ADDRESSES } from "src/networkAddresses"; | ||
| import { expectEvents, initMainnetUser } from "src/utils"; | ||
| import { forking, testVip } from "src/vip-framework"; | ||
|
|
||
| import vip573 from "../../vips/vip-573/bsctestnet"; | ||
| import COMPTROLLER_ABI from "./abi/Comptroller.json"; | ||
| import DIAMOND_ABI from "./abi/Diamond.json"; | ||
|
|
||
| const { bsctestnet } = NETWORK_ADDRESSES; | ||
|
|
||
| const OLD_MARKET_FACET = "0x0A7A88aB6aB40417Bd6bF1EB3907EFF06D24C2FC"; | ||
|
|
||
| const NEW_MARKET_FACET = "0x8e0e15C99Ab0985cB39B2FE36532E5692730eBA9"; | ||
|
|
||
| // Function selector for enterMarketBehalf(address,address) | ||
| const ENTER_MARKET_BEHALF_SELECTOR = "0xd585c3c6"; | ||
|
|
||
| // Test accounts | ||
| const TEST_USER = "0x14A1c22EF6d2eF6cE33c0b018d8A34D02021e5c8"; | ||
| const TEST_DELEGATE = "0x9cc6f5f16498fceef4d00a350bd8f8921d304dc9"; | ||
| const VUSDT = "0xb7526572FFE56AB9D7489838Bf2E18e3323b441A"; | ||
|
|
||
| forking(73659707, async () => { | ||
| const provider = ethers.provider; | ||
| let unitroller: Contract; | ||
|
|
||
| let marketFacetFunctionSelectors: string[]; | ||
|
|
||
| before(async () => { | ||
| unitroller = new ethers.Contract(bsctestnet.UNITROLLER, COMPTROLLER_ABI, provider); | ||
|
|
||
| // Get current MarketFacet function selectors | ||
| marketFacetFunctionSelectors = await unitroller.facetFunctionSelectors(OLD_MARKET_FACET); | ||
| }); | ||
|
|
||
| describe("Pre-VIP behavior", async () => { | ||
| it("MarketFacet should have old implementation", async () => { | ||
| expect(await unitroller.facetFunctionSelectors(OLD_MARKET_FACET)).to.deep.equal(marketFacetFunctionSelectors); | ||
| expect(await unitroller.facetFunctionSelectors(NEW_MARKET_FACET)).to.deep.equal([]); | ||
| }); | ||
|
|
||
| it("enterMarketBehalf function should not exist", async () => { | ||
| expect(marketFacetFunctionSelectors).to.not.include(ENTER_MARKET_BEHALF_SELECTOR); | ||
| }); | ||
|
|
||
| it("unitroller should contain old MarketFacet address", async () => { | ||
| expect(await unitroller.facetAddresses()).to.include(OLD_MARKET_FACET); | ||
| expect(await unitroller.facetAddresses()).to.not.include(NEW_MARKET_FACET); | ||
| }); | ||
| }); | ||
|
|
||
| testVip("vip-573", await vip573(), { | ||
| callbackAfterExecution: async (txResponse: TransactionResponse) => { | ||
| await expectEvents(txResponse, [DIAMOND_ABI], ["DiamondCut"], [1]); | ||
| }, | ||
| }); | ||
|
|
||
| describe("Post-VIP behavior", async () => { | ||
| it("MarketFacet function selectors should be updated for new facet address", async () => { | ||
| const newMarketFacetFunctionSelectors = [ENTER_MARKET_BEHALF_SELECTOR]; | ||
|
|
||
| const expectSelectors = [...marketFacetFunctionSelectors, ...newMarketFacetFunctionSelectors].sort(); | ||
| const updatedSelectors = [...(await unitroller.facetFunctionSelectors(NEW_MARKET_FACET))].sort(); | ||
|
|
||
| expect(updatedSelectors).to.deep.equal(expectSelectors); | ||
| expect(await unitroller.facetFunctionSelectors(OLD_MARKET_FACET)).to.deep.equal([]); | ||
| }); | ||
|
|
||
| it("unitroller should contain new MarketFacet address", async () => { | ||
| expect(await unitroller.facetAddresses()).to.include(NEW_MARKET_FACET); | ||
| expect(await unitroller.facetAddresses()).to.not.include(OLD_MARKET_FACET); | ||
| }); | ||
|
|
||
| it("enterMarketBehalf function should exist in new facet", async () => { | ||
| const newFacetSelectors = await unitroller.facetFunctionSelectors(NEW_MARKET_FACET); | ||
| expect(newFacetSelectors).to.include(ENTER_MARKET_BEHALF_SELECTOR); | ||
| }); | ||
| it("should allow approved delegate to enter market on behalf of user", async () => { | ||
| const user = await initMainnetUser(TEST_USER, parseEther("10000")); | ||
|
|
||
| // First, exit the market if already in it | ||
| try { | ||
| await unitroller.connect(user).exitMarket(VUSDT); | ||
| } catch (e) { | ||
| // Market might not be entered, continue | ||
| } | ||
|
|
||
| // Approve delegate | ||
| await unitroller.connect(user).updateDelegate(TEST_DELEGATE, true); | ||
|
|
||
| // Switch to delegate | ||
| const delegate = await initMainnetUser(TEST_DELEGATE, parseEther("10000")); | ||
|
|
||
| // Check membership before | ||
| const membershipBefore = await unitroller.checkMembership(TEST_USER, VUSDT); | ||
| expect(membershipBefore).to.be.false; | ||
|
|
||
| await expect(await unitroller.connect(delegate).enterMarketBehalf(TEST_USER, VUSDT)) | ||
| .to.emit(unitroller, "MarketEntered") | ||
| .withArgs(VUSDT, TEST_USER); | ||
|
|
||
| // Check membership after | ||
| const membershipAfter = await unitroller.checkMembership(TEST_USER, VUSDT); | ||
| expect(membershipAfter).to.be.true; | ||
| }); | ||
|
|
||
| it("should revert when unapproved delegate tries to enter market", async () => { | ||
| const userSigner = await initMainnetUser(TEST_DELEGATE, parseEther("10000")); | ||
| const comptroller = unitroller.connect(userSigner); | ||
|
|
||
| try { | ||
| await comptroller.exitMarket(VUSDT); | ||
| } catch (e) { | ||
| // Market might not be entered, continue | ||
| } | ||
|
|
||
| const delegateSigner = await initMainnetUser(TEST_USER, parseEther("10000")); | ||
| const comptrollerDelegate = unitroller.connect(delegateSigner); | ||
|
|
||
| // Should revert with NotAnApprovedDelegate | ||
| await expect(comptrollerDelegate.enterMarketBehalf(TEST_DELEGATE, VUSDT)).to.be.revertedWithCustomError( | ||
| comptrollerDelegate, | ||
| "NotAnApprovedDelegate", | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "cutParams": [ | ||
| [ | ||
| "0x87FdF72FA2fB29Cb43f03aCa261A8DC2C613a860", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we get it verified @GitGuru7 |
||
| 1, | ||
| [ | ||
| "0xa76b3fda", | ||
| "0x929fe9a1", | ||
| "0xc2998238", | ||
| "0xede4edd0", | ||
| "0xb0772d0b", | ||
| "0xabfceffc", | ||
| "0x007e3dd2", | ||
| "0xc488847b", | ||
| "0xa78dc775", | ||
| "0x0686dab6", | ||
| "0xddbf54fd", | ||
| "0x3d98a1e5", | ||
| "0xcab4f84c", | ||
| "0xc5b4db55", | ||
| "0x89c13be0", | ||
| "0xd0d13036", | ||
| "0xf9682732", | ||
| "0x23617585", | ||
| "0xafd3783b", | ||
| "0x19ef3e8b", | ||
| "0xd686e9ee", | ||
| "0x7b86e42c", | ||
| "0x63e0d634", | ||
| "0xf02fdf97", | ||
| "0x0ef332ca", | ||
| "0x8e8f294b", | ||
| "0x3093c11e", | ||
| "0xd137f36e", | ||
| "0xd463654c", | ||
| "0x4d99c776" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. aditya, curious, is there a tool to generate the selector list of a facet
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, nvm, guess u r calling |
||
| ] | ||
| ], | ||
| ["0x87FdF72FA2fB29Cb43f03aCa261A8DC2C613a860", 0, ["0xd585c3c6"]] | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "cutParams": [ | ||
| [ | ||
| "0x8e0e15C99Ab0985cB39B2FE36532E5692730eBA9", | ||
| 1, | ||
| [ | ||
| "0xa76b3fda", | ||
| "0x929fe9a1", | ||
| "0xc2998238", | ||
| "0xede4edd0", | ||
| "0xb0772d0b", | ||
| "0xabfceffc", | ||
| "0x007e3dd2", | ||
| "0xc488847b", | ||
| "0xa78dc775", | ||
| "0x0686dab6", | ||
| "0xddbf54fd", | ||
| "0x3d98a1e5", | ||
| "0xcab4f84c", | ||
| "0xc5b4db55", | ||
| "0x89c13be0", | ||
| "0xd0d13036", | ||
| "0xf9682732", | ||
| "0x23617585", | ||
| "0xafd3783b", | ||
| "0x19ef3e8b", | ||
| "0xd686e9ee", | ||
| "0x7b86e42c", | ||
| "0x63e0d634", | ||
| "0xf02fdf97", | ||
| "0x0ef332ca", | ||
| "0x8e8f294b", | ||
| "0x3093c11e", | ||
| "0xd137f36e", | ||
| "0xd463654c", | ||
| "0x4d99c776" | ||
| ] | ||
| ], | ||
| ["0x8e0e15C99Ab0985cB39B2FE36532E5692730eBA9", 0, ["0xd585c3c6"]] | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import { NETWORK_ADDRESSES } from "src/networkAddresses"; | ||
| import { ProposalType } from "src/types"; | ||
| import { makeProposal } from "src/utils"; | ||
|
|
||
| import { cutParams as params } from "../../simulations/vip-573/utils/cut-params-bscmainnet.json"; | ||
|
|
||
| const { bscmainnet } = NETWORK_ADDRESSES; | ||
|
|
||
| export const cutParams = params; | ||
|
|
||
| export const vip573 = () => { | ||
| const meta = { | ||
| version: "v2", | ||
| title: "VIP-573 [BNB Chain] Add enterMarketBehalf support to Comptroller", | ||
| description: `#### Summary | ||
|
|
||
| If passed, this VIP will upgrade the MarketFacet of the core pool Comptroller on BNB Chain Mainnet to add support for the \`enterMarketBehalf\` function, allowing approved delegates to enter markets on behalf of users. | ||
|
|
||
| #### Description`, | ||
| forDescription: "Execute this proposal", | ||
| againstDescription: "Do not execute this proposal", | ||
| abstainDescription: "Indifferent to execution", | ||
| }; | ||
|
|
||
| return makeProposal( | ||
| [ | ||
| { | ||
| target: bscmainnet.UNITROLLER, | ||
| signature: "diamondCut((address,uint8,bytes4[])[])", | ||
| params: [cutParams], | ||
| }, | ||
| ], | ||
| meta, | ||
| ProposalType.REGULAR, | ||
| ); | ||
| }; | ||
|
|
||
| export default vip573; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cast sig "enterMarketBehalf(address,address)"
0xd585c3c6