diff --git a/contracts/tokenManager/MysoTokenManagerArbitrum.sol b/contracts/tokenManager/MysoTokenManagerArbitrum.sol deleted file mode 100644 index df06686b..00000000 --- a/contracts/tokenManager/MysoTokenManagerArbitrum.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.19; - -import {MysoTokenManager} from "./MysoTokenManager.sol"; -import {DataTypesPeerToPeer} from "../peer-to-peer/DataTypesPeerToPeer.sol"; - -contract MysoTokenManagerArbitrum is MysoTokenManager { - address internal constant GDAI = 0xd85E038593d7A098614721EaE955EC2022B9B91B; - address internal constant GUSDC = - 0xd3443ee1e91aF28e5FB858Fbd0D72A63bA8046E0; - address internal constant GETH = 0x5977A9682D7AF81D347CFc338c61692163a2784C; - mapping(address => uint256) public gVolume; - mapping(address => uint256) public gVolumeCaps; - - event GVolumeSet(address gToken, uint256 gVolume); - event GVolumeCapSet(address gToken, uint256 gVolumeCap); - - error NoGToken(); - - constructor( - address _mysoIOOVault, - address _mysoToken, - address _stMysoToken, - uint256 _minMysoWeight, - address _signer, - uint256 _gDaiCap, - uint256 _gUsdcCap, - uint256 _gEthCap - ) - MysoTokenManager( - _mysoIOOVault, - _mysoToken, - _stMysoToken, - _minMysoWeight, - _signer - ) - { - gVolumeCaps[GDAI] = _gDaiCap; - gVolumeCaps[GUSDC] = _gUsdcCap; - gVolumeCaps[GETH] = _gEthCap; - } - - function setGVolume(address _gToken, uint256 _gVolume) external { - _checkOwner(); - gVolume[_gToken] = _gVolume; - emit GVolumeSet(_gToken, _gVolume); - } - - function setGVolumeCap(address gToken, uint256 _gVolumeCap) external { - _checkOwner(); - if (gToken != GDAI && gToken != GDAI && gToken != GDAI) { - revert NoGToken(); - } - gVolumeCaps[gToken] = _gVolumeCap; - emit GVolumeCapSet(gToken, _gVolumeCap); - } - - function _isAllowed( - bool isMysoIoo, - DataTypesPeerToPeer.BorrowTransferInstructions - calldata borrowInstructions, - DataTypesPeerToPeer.Loan calldata loan - ) internal override returns (bool isAllowed) { - isAllowed = super._isAllowed(isMysoIoo, borrowInstructions, loan); - if (!isAllowed) { - return isAllowed; - } - if (isMysoIoo) { - if ( - loan.collToken == GDAI || - loan.collToken == GUSDC || - loan.collToken == GETH - ) { - uint256 _newGVolume = gVolume[loan.collToken] + - loan.initCollAmount; - isAllowed = _newGVolume <= gVolumeCaps[loan.collToken]; - if (isAllowed) { - gVolume[loan.collToken] = _newGVolume; - } - } else { - isAllowed = true; - } - } else { - isAllowed = true; - } - } -} diff --git a/contracts/tokenManager/MysoTokenManagerWithCaps.sol b/contracts/tokenManager/MysoTokenManagerWithCaps.sol new file mode 100644 index 00000000..ae2e15de --- /dev/null +++ b/contracts/tokenManager/MysoTokenManagerWithCaps.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.19; + +import {MysoTokenManager} from "./MysoTokenManager.sol"; +import {DataTypesPeerToPeer} from "../peer-to-peer/DataTypesPeerToPeer.sol"; + +contract MysoTokenManagerWithCaps is MysoTokenManager { + struct CollatCapInfo { + address token; + uint128 maxPledge; + } + + mapping(address => uint256) public collatCaps; + mapping(address => uint256) public totalPledged; + + event CollatCapsSet(CollatCapInfo[] collatCaps); + + constructor( + address _mysoIOOVault, + address _mysoToken, + address _stMysoToken, + uint256 _minMysoWeight, + address _signer, + CollatCapInfo[] memory _collatCaps + ) + MysoTokenManager( + _mysoIOOVault, + _mysoToken, + _stMysoToken, + _minMysoWeight, + _signer + ) + { + for (uint256 i = 0; i < _collatCaps.length; i++) { + collatCaps[_collatCaps[i].token] = _collatCaps[i].maxPledge; + } + } + + function setCollatCaps(CollatCapInfo[] calldata _collatCaps) external { + _checkOwner(); + for (uint256 i = 0; i < _collatCaps.length; i++) { + collatCaps[_collatCaps[i].token] = _collatCaps[i].maxPledge; + } + emit CollatCapsSet(_collatCaps); + } + + function _isAllowed( + bool isMysoIoo, + DataTypesPeerToPeer.BorrowTransferInstructions + calldata borrowInstructions, + DataTypesPeerToPeer.Loan calldata loan + ) internal override returns (bool isAllowed) { + isAllowed = super._isAllowed(isMysoIoo, borrowInstructions, loan); + if (!isAllowed) { + return isAllowed; + } + if (isMysoIoo) { + uint256 _newPledged = totalPledged[loan.collToken] + + loan.initCollAmount; + isAllowed = _newPledged <= collatCaps[loan.collToken]; + if (isAllowed) { + totalPledged[loan.collToken] = _newPledged; + } + } else { + isAllowed = true; + } + } +} diff --git a/test/peer-to-peer/local-tests-tokenmanager.ts b/test/peer-to-peer/local-tests-tokenmanager.ts index 0350cadc..a7fb47c0 100644 --- a/test/peer-to-peer/local-tests-tokenmanager.ts +++ b/test/peer-to-peer/local-tests-tokenmanager.ts @@ -393,5 +393,80 @@ describe('Peer-to-Peer: Local Tests', function () { expect(preBalTokenManager.sub(postBalTokenManager)).to.be.equal(postBalDeployer.sub(preBalDeployer)) expect(postBalTokenManager).to.be.equal(0) }) + + it('Should process caps correctly', async function () { + const { + deployer, + borrower1, + borrower2, + addressRegistry, + borrowerGateway, + quoteHandler, + mysoTokenManager, + iooVault, + usdc, + myt, + stmyt, + iooOnChainQuote + } = await setupTest() + + // deploy token manager with pledge caps + const MysoTokenManagerWithCaps = await ethers.getContractFactory('MysoTokenManagerWithCaps') + const collatCaps = [{token: usdc.address, maxPledge: ONE_USDC.mul(10000)}] + const mysoTokenManagerWithCaps = await MysoTokenManagerWithCaps.connect(deployer).deploy(iooVault.address, myt.address, stmyt.address, 0, ethers.constants.AddressZero, collatCaps) + await mysoTokenManagerWithCaps.deployed() + + // whitelist new myso token manager + await addressRegistry.connect(deployer).setWhitelistState([mysoTokenManager.address], 0) + await addressRegistry.connect(deployer).setWhitelistState([mysoTokenManagerWithCaps.address], 9) + + // borrowers approves gateway + await usdc.connect(borrower1).approve(borrowerGateway.address, MAX_UINT256) + await usdc.connect(borrower2).approve(borrowerGateway.address, MAX_UINT256) + + // borrow initiation + const collSendAmount = ONE_USDC.mul(4000) + const expectedProtocolAndVaultTransferFee = 0 + const expectedCompartmentTransferFee = 0 + const quoteTupleIdx = 0 + const callbackAddr = ZERO_ADDRESS + const callbackData = ZERO_BYTES32 + const borrowInstructions = { + collSendAmount, + expectedProtocolAndVaultTransferFee, + expectedCompartmentTransferFee, + deadline: MAX_UINT256, + minLoanAmount: 0, + callbackAddr, + callbackData, + mysoTokenManagerData: ZERO_BYTES32 + } + + // check 2 borrows should work + borrowInstructions.mysoTokenManagerData = ZERO_BYTES32 + await borrowerGateway + .connect(borrower2) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx) + await borrowerGateway + .connect(borrower2) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx) + await expect(borrowerGateway + .connect(borrower2) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx)).to.be.revertedWithCustomError(mysoTokenManagerWithCaps, "NotAllowed") + + // set cap higher + const newCollatCaps = [{token: usdc.address, maxPledge: ONE_USDC.mul(12000)}] + await mysoTokenManagerWithCaps.setCollatCaps(newCollatCaps) + + // now borrow works again + await borrowerGateway + .connect(borrower2) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx) + + // next one fails again + await expect(borrowerGateway + .connect(borrower2) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx)).to.be.revertedWithCustomError(mysoTokenManagerWithCaps, "NotAllowed") + }) }) })