If you use Uniswap platform to swap a token, you are going to have 2 steps. You are going to approve the token, in this step metamask will pop-up and you are going to confirm it. Then Uniswap will do the actual swap, it takes the tokens out of your wallet and does the exchange for you.
This is the swapExactETHForTokens
function
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
virtual
override
payable
ensure(deadline)
returns (uint[] memory amounts)
{
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
IWETH(WETH).deposit{value: amounts[0]}();
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
_swap(amounts, path, to);
}
last function _swap
calls the swap
function:
From documentation
It is also important to ensure that your contract controls enough
ETH/tokens to make the swap, and has granted approval to the router to
withdraw this many tokens.
Imagine you want to swap 50 DAI for as much ETH as possible from your
smart contract.
transferFrom
Before swapping, our smart contracts needs to be in control of 50 DAI.
The easiest way to accomplish this is by calling transferFrom on DAI
with the owner set to msg.sender:
uint amountIn = 50 * 10 ** DAI.decimals();
require(DAI.transferFrom(msg.sender, address(this), amountIn), 'transferFrom failed.');
Eventually Uniswap will transferFrom
, but before your token has to approve the transaction, it has to add uniswap address
to its allowance
mapping.
mapping(address=>mapping(address=>uint)) public allowance;
// token address is allowign uniswap address for this much token
You cannot test the current implementation of your contract unless you have a swap token set and your swap token has to call approve
.
If you had front end app, when you call your contract's swap function, metamask would pop up and you would confirm it. However in a test environment you need the actual ERC20 contract, you deploy it and you call the approve
. In front end you would have two functions swapToken
and approve
. You would call them in this order?
const startSwap = async () => {
await approve()
await swapToken()
}
In test suite:
const MyContract = artifacts.require("MyContract");
const Dai = artifacts.require("Dai");
// ganache provides an array of accounts
contract("Uniswap", (ganachProvidedAccounts) => {
let myContract,dai;
// intialize the contracts before each test
before(async () => {
myContract = await myContract.new();
dai = await Dai.new();
})
describe("Swapping", async () => {
it("swap tokens", async () => {
let result;
// first ask for approval of 100 token transfer
await dai.approve(myContract.address, tokens("100"), {
from:ganachProvidedAccounts[0] ,
});
// // check staking for customer
await myContract.swapExactETHForTokens("100"), { from: ganachProvidedAccounts[0] });
// make your assetion
})})