How can I implement a meta-transaction using ethers.js library or at least have another account paying a transaction, which was signed by different account?
Consider a transaction to transfer 100 ERC20-tokens using a ERC20-contract from 0xAccountA to 0xAccountB. The transaction will be signed by 0xAccountA but the gas would be payed by some other 0xAccountC.
Lets initiate a contract:
const abi = ['function transferFrom(address sender, address recipient, uint256 amount)'];
const payer = new ethers.Wallet(privKey, provider);
const contract = new ethers.Contract(contractAddress, abi, payer);
Currently, we create transactions like this;
const contractWithFromAsSigner = contract.connect(from);
const tx = await contractWithFromAsSigner.transferFrom(from, to, amount);
In this case, from has to sign the transaction and is also paying for the transaction.
What I'd like to achieve is that from is signing this transaction, but payer is paying and really broadcasting the transaction, which was signed by from, but paid by payer.
How can I do that?
You are roughly trying to describe a relayer manager. You can have it on-chain. Afaik there aren't many implementations but you can check these resources which have implemented it (EIP 1077) and modify it to your use case:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1077.md#similar-implementations
https://github.com/argentlabs/argent-contracts/blob/ea361609bbda15bf19bac35dbc2af80b720cb27b/contracts/modules/RelayerManager.sol
Your ethers.js would then call the execute fn of the relayer contract.
Related
I'm studying UniswapV2Pair.sol https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol and I have some question about the mint and burn function.
What I understand:
When user deposit the token pair, mint function mints the new liquidity token and send to the user
When user withdraw the token pair, burn function burns the new liquidity token and sends the deposited token pair back to user .
What I'm confused about:
I'm confused about the bold part of burn function I mentioned above. I think that mint and burn function is like mirror(opposite) function, but mint function doesn't include the feature which token pair are send to the exchange contract. However, burn function uses _safeTransfer which sends the token pair back to user.
I'm confused why did they designed assymetrically.
The mint() function calculates the amount of minted LP tokens from the difference of
recorded reserves of the underlying tokens (_reserve0 and _reserve1)
and the actual underlying token balance owned by the pair contract (balance0 and balance1)
So theoretically, if Alice just sent the underlying tokens to the pair contract without invoking the mint() function, that would make the accounting difference described above. And Bob would be able to invoke the mint() function and mint the LP tokens for himself profiting off Alice.
But that's not the usual process flow. Usually, the liquidity provider (Alice), invokes the addLiquidity() function of the router contract that performs both actions at once:
transfers the (approved) tokens from the user to the pair contract
invokes the mint() function on the pair contract calculating the difference created in this transaction
Which removes the possibility for Bob to intercept the Alice's minting process.
And having the mint() function executable by itself also allows anyone to claim unclaimed tokens that were sent to the pair contract by mistake for example.
However, if you want to transfer the underlying tokens out of the pair contract (i.e. burn() the LP tokens), there needs to be check already in the burn() function so that you can't claim more of the underlying tokens than you're eligible to.
No matter if you're invoking the pair burn() function directly or from the router removeLiquidity() (that's normally invoked from the Uniswap UI).
but mint function doesn't include the feature which token pair are
send to the exchange contract.
You showed the UniswapV2Pair.sol. mint function is used when we add liquidity which is defined in UniswapV2Router02.sol
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
liquidity = IUniswapV2Pair(pair).mint(to);
}
we are passing the amount of tokens that we want to add liquidity, since we cannot add an arbitrary amount, adding liquidity should not affect on the price, so the function itself is calculating the proper amounts and then those amounts are transferred to the UniswapV2Router02 contract. After those amounts are transferred, we are minting a new LP token and sending it to the to address
IUniswapV2Pair(pair).mint(to)
Inside mint function how much token will be minted is calculated and then _mint function called
_mint(to, liquidity);
Hi I'm creating a voting smart contract for a DAO and I have a security question. The voting system works like this:
You send your tokens to the smart contract then the smart contract registers how much tokens you have and assignes you "Power" which you use when you vote. Then the smart contract sends the funds back immidiately.
My question is if there is more secure way to do this. Without funds leaving usere's wallet.
Here is the code I have so far.
function getPower() payable public {
require(msg.value > 0, "The amount can't be 0");
require(election_state == ELECTION_STATE.OPEN);
require(votingPeriod > block.timestamp);
uint amountSent = msg.value;
// This function will take their money and assign power to the voter
// The power is equal to their deposit in eth * 10 so for each eth they get 10 power
voters[msg.sender].power = msg.value * 10;
payable(msg.sender).transfer(amountSent);
}
Thanks in advance.
Based on the provided code and question, I'm assuming you want to calculate the voting power based on the users' ETH balance - not based on their balance of the DAO token.
You can get the current ETH balance of an address, using the .balance member of an address type. So you could simplify your function as this:
function getPower() public {
require(election_state == ELECTION_STATE.OPEN);
require(votingPeriod > block.timestamp);
voters[msg.sender].power = msg.sender.balance * 10;
}
After performing the validations, it assigns the value based on the msg.sender ETH balance at the moment of the getPower() function being invoked. Without them needing to send ETH to the contract.
Note that this approach is not common and can be misused for example by users loaning a large amount of ETH just before executing the getPower() function. I'd recommend you to use a more common pattern of calculating the voting power based on their current holdings of the token representing their stake in the DAO.
We can transfer funds from address(this) to recipient. But is there any way to transfer funds directly msg.sender wallet to recipient? I can not set msg.value at the time of invoking payoutBonus call. Because I can get the amount only inside payoutBonus method.
function payoutBonus(address recipient) public payable returns (bool) {
// bonus = calculateBonus();
//transfer this bonus to recipient from msg.sender;
return true;
}
msg.value is a read-only property reflecting value of the incoming transaction. If you want to send an ETH amount, you can do it in one of two ways.
Notes:
These examples work on Solidity 0.8. Some previous versions also allow the send() method that is now deprecated, don't require the payable type, and have slightly different syntax for the call() method.
bonus is amount of wei (1 ETH is 10^18 wei)
The transfer() method
uint256 bonus = calculateBonus();
payable(msg.sender).transfer(bonus);
The transfer() method only allows consuming 2300 gas, which is enough for non-contract recipients. If the recipient is a contract requiring more than 2300 gas to receive the ETH (for instance it sets some local variable), the transaction reverts.
Low-level call()
uint256 bonus = calculateBonus();
msg.sender.call{value: bonus}("");
With the low-level call() method, you can also specify the gas limit, function to call and it's arguments. Use this method only if you know what you're doing.
I am writing a contract where I want to transfer money (present in the contract owners account and not the contract) to an account address passed to a function in the contract.
for some reason this code won't work
function payBill(uint value, address account) payable public {
account.transfer(value);
transactionCount += 1;
transactionAmount += value;
}
Your problem can be related to any frontend code, not only this.
You've got two options.
get that other user address from the contract and then run a direct transaction between two accounts (example here https://web3js.readthedocs.io/en/v1.2.6/web3-eth.html#id80)
send a value (see here https://web3js.readthedocs.io/en/v1.2.6/web3-eth-contract.html#id33) when you are calling the payBill method. If you don't do it, the default value is zero, and you don't see any transfer.
Also, please consider having a look at this https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now/
EDIT:
what you are trying to do here, is also not doable. This requires the contract to have the number of funds that you want to transfer. That is because, with the payable method, you are sending funds to the contract, but the funds will only be in the contract, once the transaction is accepted. On the other hand, you are trying to send that number of funds to another user, without having them on the contract. That is why you never see any balance.
I have contract, through which users can trade on DEX.
Like this:
// transfer asset A from msg.sender
ERC20(AToken).transferFrom(msg.sender, address(this), amount);
// do trade A to B
... trade logic here
// get balance of asset B after trade
// asset B after trade gets to this contract address
uint256 returnAmount = ERC20(BToken).balanceOf(address(this));
// transfer asset B to msg.sender
ERC20(BToken).transfer(msg.sender, returnAmount);
I wonder about returnAmount, is this logic safe?
Transactions in ethereum are performed either completely or not performed at all, and also in order of priority.
But I’m still wondering if there could be such a case when returnAmount shows incorrectly, for example, contract get the balance after the transaction of another user?
Transactions in ETH are executed one by one, other transactions can not interfere to execution of a transaction when it's running by EVM.