Skip to content

Instantly share code, notes, and snippets.

@BlockmanCodes
Created April 3, 2022 12:40
Show Gist options
  • Save BlockmanCodes/83d995ec9de0c4f5201c3556cba489e0 to your computer and use it in GitHub Desktop.
Save BlockmanCodes/83d995ec9de0c4f5201c3556cba489e0 to your computer and use it in GitHub Desktop.
Deposit and withdraw ERC20 tokens (USDT, SHIB, MATIC) from a Solidity contract (Hardhat, Ethers.js)
require("@nomiclabs/hardhat-waffle");
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.0",
};
{
"name": "erc20-bank",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.5",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.5.4",
"hardhat": "^2.9.1"
},
"dependencies": {
"@openzeppelin/contracts": "^4.5.0"
}
}
const { expect } = require("chai");
describe('Staking', function () {
beforeEach(async function() {
[owner, wallet1, wallet2] = await ethers.getSigners();
Staking = await ethers.getContractFactory('Staking', owner);
Wbtc = await ethers.getContractFactory('Wbtc', wallet1);
staking = await Staking.deploy();
wbtc = await Wbtc.deploy();
wbtc.connect(wallet1).transfer(wallet2.address, 1000);
await wbtc.connect(wallet1).approve(
staking.address,
4000
);
await wbtc.connect(wallet2).approve(
staking.address,
1000
);
WBTC = ethers.utils.formatBytes32String('Wbtc');
await staking.whitelistToken(
WBTC,
wbtc.address
);
});
describe('deployment', function () {
it('should mint tokens to wallet 1', async function () {
expect(await wbtc.balanceOf(wallet1.address)).to.equal(4000);
})
it('should transfer tokens to wallet 2', async function () {
expect(await wbtc.balanceOf(wallet2.address)).to.equal(1000);
})
it('should whitelist wbtc on the contract', async function () {
expect(
await staking.whitelistedTokens(WBTC)
).to.equal(wbtc.address);
})
})
describe('depositTokens', function () {
it('should deposit wbtc', async function () {
await staking.connect(wallet1).depositTokens(
100,
WBTC,
);
await staking.connect(wallet2).depositTokens(
50,
WBTC,
);
expect(await wbtc.balanceOf(wallet1.address)).to.equal(3900);
expect(await wbtc.balanceOf(wallet2.address)).to.equal(950);
expect(
await staking.accountBalances(wallet1.address, WBTC)
).to.equal(100);
expect(
await staking.accountBalances(wallet2.address, WBTC)
).to.equal(50);
});
})
describe('withdraw', function () {
it('should withdraw wbtc from the contract', async function () {
await staking.connect(wallet1).depositTokens(
600,
WBTC,
);
await staking.connect(wallet1).withdrawTokens(
100,
WBTC,
);
expect(await wbtc.balanceOf(wallet1.address)).to.equal(3500);
expect(
await staking.accountBalances(wallet1.address, WBTC)
).to.equal(500);
})
it('should not allow withdrawing more than has been deposited', async function () {
await expect(
staking.connect(wallet1).withdrawTokens(10000, WBTC)
).to.be.revertedWith("Insufficent funds")
})
})
})
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Staking {
address owner;
mapping(bytes32 => address) public whitelistedTokens;
mapping(address => mapping(bytes32 => uint256)) public accountBalances;
constructor() {
owner = msg.sender;
}
function whitelistToken(bytes32 symbol, address tokenAddress) external {
require(msg.sender == owner, 'This function is not public');
whitelistedTokens[symbol] = tokenAddress;
}
function getWhitelistedTokenAddresses(bytes32 token) external returns(address) {
return whitelistedTokens[token];
}
function depositTokens(uint256 amount, bytes32 symbol) external {
accountBalances[msg.sender][symbol] += amount;
ERC20(whitelistedTokens[symbol]).transferFrom(msg.sender, address(this), amount);
}
function withdrawTokens(uint256 amount, bytes32 symbol) external {
require(accountBalances[msg.sender][symbol] >= amount, 'Insufficent funds');
accountBalances[msg.sender][symbol] -= amount;
ERC20(whitelistedTokens[symbol]).transfer(msg.sender, amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Wbtc is ERC20 {
constructor() ERC20('WBTC', 'Wrapped BTC') {
_mint(msg.sender, 5000);
}
}
@wuliwong
Copy link

Thanks for the tutorial. Where does the address() method come from in line 27 ERC20(whitelistedTokens[symbol]).transferFrom(msg.sender, address(this), amount); in the Staking.sol file?

@BlockmanCodes
Copy link
Author

BlockmanCodes commented Apr 15, 2022

@wuliwong

Thanks for the tutorial. Where does the address() method come from in line 27 ERC20(whitelistedTokens[symbol]).transferFrom(msg.sender, address(this), amount); in the Staking.sol file?

It's core Solidity

@kingerpku
Copy link

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? Internal JSON-RPC error. { "code": 3, "message": "execution reverted: ERC20: transfer amount exceeds allowance", "data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002845524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365000000000000000000000000000000000000000000000000" }

I get the above error msg when run the depositTokens function of Staking.sol in remix, did i miss any step? thanks.

@BlockmanCodes
Copy link
Author

@kingerpku

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? Internal JSON-RPC error. { "code": 3, "message": "execution reverted: ERC20: transfer amount exceeds allowance", "data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002845524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e6365000000000000000000000000000000000000000000000000" }

I get the above error msg when run the depositTokens function of Staking.sol in remix, did i miss any step? thanks.

It sounds like you need to increase the amount you're approving.
Are you calling .approve?
If yes, try increasing it to a crazy high amount.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment