Skip to content
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

feat: add deposit functionality to Gateway contract #47

Merged
merged 5 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions contracts/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
mapping(bytes32 => Order) private order;
mapping(address => uint256) private _nonce;
uint256[50] private __gap;
mapping(address => mapping(address => uint256)) private depositedBalances;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -42,6 +43,20 @@ contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
_;
}

/**
* @dev Modifier that checks if the token is supported.
* @param _token The address of the token to be checked.
*/
modifier isTokenApproved(address _token) {
require(_isTokenSupported[_token] == 1, 'TokenNotSupported');
_;
}

modifier isValidAmount(uint256 _amount) {
require(_amount != 0, 'AmountIsZero');
_;
}

/* ##################################################################
OWNER FUNCTIONS
################################################################## */
Expand Down Expand Up @@ -128,9 +143,7 @@ contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
address _refundAddress,
address _senderFeeRecipient,
uint256 _senderFee
) internal view {
require(_isTokenSupported[_token] == 1, 'TokenNotSupported');
require(_amount != 0, 'AmountIsZero');
) internal isTokenApproved(_token) isValidAmount(_amount) view {
require(_refundAddress != address(0), 'ThrowZeroAddress');

if (_senderFee != 0) {
Expand Down Expand Up @@ -225,6 +238,15 @@ contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
return true;
}

/** @dev See {deposit-IGateway}. */
function deposit(address _token, uint256 _amount) external isTokenApproved(_token) isValidAmount(_amount) returns (bool) {
address sender = _msgSender();
IERC20(_token).transferFrom(sender, address(this), _amount);
depositedBalances[_token][sender] += _amount;
emit Deposit(sender, _token, _amount);
return true;
}

/* ##################################################################
VIEW CALLS
################################################################## */
Expand All @@ -243,4 +265,9 @@ contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
function getFeeDetails() external view returns (uint64, uint256) {
return (protocolFeePercent, MAX_BPS);
}

/** @dev See {getProviderStakedBalance-IGateway}. */
chibie marked this conversation as resolved.
Show resolved Hide resolved
function getProviderDepositBalance(address _token, address _provider) external view returns (uint256) {
chibie marked this conversation as resolved.
Show resolved Hide resolved
return depositedBalances[_token][_provider];
}
}
40 changes: 40 additions & 0 deletions contracts/interfaces/IGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ interface IGateway {
*/
event SenderFeeTransferred(address indexed sender, uint256 indexed amount);

/**
* @dev Emitted when a deposit is made by a provider.
* @param sender The address of the sender.
* @param token The address of the deposited token.
* @param amount The amount of the deposit.
*/
event Deposit(address indexed sender, address indexed token, uint256 indexed amount);

/* ##################################################################
STRUCTS
################################################################## */
Expand Down Expand Up @@ -141,6 +149,30 @@ interface IGateway {
*/
function refund(uint256 _fee, bytes32 _orderId) external returns (bool);

/**
* @notice Allow Provider to deposit assets.
* @dev Reqirements:
chibie marked this conversation as resolved.
Show resolved Hide resolved
* - The amount must be greater than minimum.
* - The asset must be supported.
* - The provider must approve Gateway contract on `_token` of at least `_amount` before function call
* @param _token The address of the asset.
* @param _amount The amount to be deposited.
* @return bool the deposit is successful.
*/
function deposit(address _token, uint256 _amount) external returns (bool);

/**
* @notice Escrowed assets from provider to the sender.
* @param _orderId The ID of the transaction.
* @param _signature The signature of the provider.
* @param _provider The address of the provider.
* @param _senderAddress The address of the sender.
* @param _token The address of the asset.
* @param _amount The amount to be transferred.
* @return bool the withdrawal is successful.
*/


chibie marked this conversation as resolved.
Show resolved Hide resolved
/**
* @notice Checks if a token is supported by Gateway.
* @param _token The address of the token to check.
Expand All @@ -161,4 +193,12 @@ interface IGateway {
* @return max_bps The maximum basis points.
*/
function getFeeDetails() external view returns (uint64 protocolReward, uint256 max_bps);

/**
* @notice Gets provider staked balance.
* @param _provider The address of the provider.
* @param _asset The address of the asset.
* @return uint256 The staked balance of the provider.
*/
chibie marked this conversation as resolved.
Show resolved Hide resolved
function getProviderDepositBalance(address _asset, address _provider) external view returns (uint256);
chibie marked this conversation as resolved.
Show resolved Hide resolved
}
146 changes: 146 additions & 0 deletions test/gateway/gateway.deposit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
const { ethers } = require("hardhat");
const { BigNumber } = require("@ethersproject/bignumber");

const { gatewayFixture } = require("../fixtures/gateway.js");

const {
deployContract,
ZERO_AMOUNT,
Errors,
Events,
mockMintDeposit,
assertBalance,
assertDepositBalance,
} = require("../utils/utils.manager.js");
const { expect } = require("chai");

describe("Gateway Provider deposit", function () {
beforeEach(async function () {
[
this.deployer,
this.treasuryAddress,
this.alice,
this.bob,
this.Eve,
this.hacker,
...this.accounts
] = await ethers.getSigners();

({ gateway, mockUSDT } = await gatewayFixture());

this.mockDAI = await deployContract("MockUSDT");

this.mockUSDT = mockUSDT;
this.gateway = gateway;

this.depositAmount = ethers.utils.parseEther("1000000");

await mockMintDeposit(gateway, this.alice, mockUSDT, this.depositAmount);
await mockMintDeposit(gateway, this.bob, mockUSDT, this.depositAmount);
await mockMintDeposit(gateway, this.Eve, mockUSDT, this.depositAmount);
await mockMintDeposit(
gateway,
this.alice,
this.mockDAI,
this.depositAmount
);
await mockMintDeposit(gateway, this.bob, this.mockDAI, this.depositAmount);
await mockMintDeposit(gateway, this.Eve, this.mockDAI, this.depositAmount);

await assertBalance(
this.mockUSDT,
this.mockDAI,
this.alice.address,
this.depositAmount
);
await assertBalance(
this.mockUSDT,
this.mockDAI,
this.bob.address,
this.depositAmount
);
await assertBalance(
this.mockUSDT,
this.mockDAI,
this.Eve.address,
this.depositAmount
);

const token = ethers.utils.formatBytes32String("token");

await expect(
this.gateway
.connect(this.deployer)
.settingManagerBool(token, this.mockUSDT.address, BigNumber.from(1))
)
.to.emit(this.gateway, Events.Gateway.SettingManagerBool)
.withArgs(token, this.mockUSDT.address, BigNumber.from(1));
});

it("Should be able to deposit and update user balance", async function () {
await assertDepositBalance(
this.gateway,
this.mockUSDT.address,
this.alice.address,
ZERO_AMOUNT
);
await expect(
this.gateway
.connect(this.alice)
.deposit(this.mockUSDT.address, this.depositAmount)
)
.to.emit(this.gateway, Events.Gateway.Deposit)
.withArgs(
this.alice.address,
this.mockUSDT.address,
BigNumber.from(this.depositAmount)
);

await assertDepositBalance(
this.gateway,
this.mockUSDT.address,
this.alice.address,
this.depositAmount
);
});

it("SHould fail when amount deposited is zero", async function () {
await assertDepositBalance(
this.gateway,
this.mockUSDT.address,
this.bob.address,
ZERO_AMOUNT
);
await expect(
this.gateway.connect(this.bob).deposit(this.mockUSDT.address, ZERO_AMOUNT)
).to.be.revertedWith(Errors.Gateway.AmountIsZero);

await assertDepositBalance(
this.gateway,
this.mockUSDT.address,
this.bob.address,
ZERO_AMOUNT
);
});

it("Should fail when token is not supported", async function () {
await assertDepositBalance(
this.gateway,
this.mockDAI.address,
this.hacker.address,
ZERO_AMOUNT
);
await expect(
this.gateway
.connect(this.hacker)
.deposit(this.mockDAI.address, this.depositAmount)
).to.be.revertedWith(Errors.Gateway.TokenNotSupported);

await assertDepositBalance(
this.gateway,
this.mockDAI.address,
this.hacker.address,
ZERO_AMOUNT
);
});
});
19 changes: 16 additions & 3 deletions test/utils/utils.manager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { ethers } = require("hardhat");
const { BigNumber } = require("@ethersproject/bignumber");
const { expect } = require("chai");

const ZERO_AMOUNT = BigNumber.from("0");
const ZERO_ADDRESS = ethers.constants.AddressZero;
Expand Down Expand Up @@ -33,6 +34,7 @@ const Events = {
SettingManagerBool: "SettingManagerBool",
ProtocolFeeUpdated: "ProtocolFeeUpdated",
ProtocolAddressUpdated: "ProtocolAddressUpdated",
Deposit: "Deposit",
},
};

Expand Down Expand Up @@ -66,9 +68,18 @@ async function getSupportedInstitutions() {
};
}

async function mockMintDeposit(gateway, account, usdc, amount) {
await usdc.connect(account).mint(amount);
await usdc.connect(account).approve(gateway.address, amount);
async function mockMintDeposit(gateway, account, token, amount) {
await token.connect(account).mint(amount);
await token.connect(account).approve(gateway.address, amount);
}

async function assertBalance(mockUSDT, mockDAI, account, depositAmount) {
expect(await mockDAI.balanceOf(account)).to.eq(depositAmount);
expect(await mockUSDT.balanceOf(account)).to.eq(depositAmount);
}

async function assertDepositBalance(gateway, token, account, amount) {
expect(await gateway.getProviderDepositBalance(token, account)).to.eq(amount);
}

module.exports = {
Expand All @@ -80,5 +91,7 @@ module.exports = {
Events,
deployContract,
mockMintDeposit,
assertBalance,
assertDepositBalance,
getSupportedInstitutions,
};
Loading