Skip to content

Commit

Permalink
feat: add deposit functionality to Gateway contract
Browse files Browse the repository at this point in the history
  • Loading branch information
OnahProsperity committed Sep 14, 2024
1 parent 8e9f59f commit 6792432
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 6 deletions.
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}. */
function getProviderDepositBalance(address _token, address _provider) external view returns (uint256) {
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:
* - 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.
*/


/**
* @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.
*/
function getProviderDepositBalance(address _asset, address _provider) external view returns (uint256);
}
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,
};

0 comments on commit 6792432

Please sign in to comment.