add L1Receiver logic to unwrapper

This commit is contained in:
Drygin 2022-02-14 15:24:33 +03:00
parent abef8ad760
commit 0530f00af5
2 changed files with 133 additions and 1 deletions

View File

@ -21,6 +21,13 @@ import { BytesHelper } from "../libraries/Bytes.sol";
contract L1Unwrapper is WETHOmnibridgeRouter {
using SafeMath for uint256;
// If this address sets to not zero it receives L1_fee.
// It can be changed by the multisig.
// And should implement fee sharing logic:
// - some part to tx.origin - based on block base fee and can be subsidized
// - store surplus of ETH for future subsidizions
address payable public l1FeeReceiver;
event PublicKey(address indexed owner, bytes key);
struct Account {
@ -88,6 +95,22 @@ contract L1Unwrapper is WETHOmnibridgeRouter {
uint256 l1Fee = BytesHelper.sliceToUint(_data, 20);
AddressHelper.safeSendValue(payable(BytesHelper.bytesToAddress(_data)), _value.sub(l1Fee));
AddressHelper.safeSendValue(payable(tx.origin), l1Fee);
address payable l1FeeTo;
if (l1FeeReceiver != payable(address(0))) {
l1FeeTo = l1FeeReceiver;
} else {
l1FeeTo = payable(tx.origin);
}
AddressHelper.safeSendValue(l1FeeTo, l1Fee);
}
/**
* @dev Sets l1FeeReceiver address.
* Only contract owner can call this method.
* @param _receiver address of new L1FeeReceiver, address(0) for native tx.origin receiver.
*/
function setL1FeeReceiver(address payable _receiver) external onlyOwner {
l1FeeReceiver = _receiver;
}
}

View File

@ -48,6 +48,7 @@ describe('TornadoPool', function () {
let customConfig = Object.assign({}, config)
customConfig.omniBridge = omniBridge.address
customConfig.weth = l1Token.address
customConfig.multisig = multisig.address
const contracts = await generate(customConfig)
await singletonFactory.deploy(contracts.unwrapperContract.bytecode, config.salt)
const l1Unwrapper = await ethers.getContractAt('L1Unwrapper', contracts.unwrapperContract.address)
@ -368,6 +369,114 @@ describe('TornadoPool', function () {
expect(await ethers.provider.getBalance(recipient)).to.be.equal(aliceWithdrawAmount)
})
it('should set L1FeeReceiver on L1Unwrapper contract', async function () {
const { tornadoPool, token, omniBridge, l1Unwrapper, sender, l1Token, multisig } = await loadFixture(
fixture,
)
// check init l1FeeReceiver
expect(await l1Unwrapper.l1FeeReceiver()).to.be.equal(ethers.constants.AddressZero)
// should not set from not multisig
await expect(l1Unwrapper.connect(sender).setL1FeeReceiver(multisig.address)).to.be.reverted
expect(await l1Unwrapper.l1FeeReceiver()).to.be.equal(ethers.constants.AddressZero)
// should set from multisig
await l1Unwrapper.connect(multisig).setL1FeeReceiver(multisig.address)
expect(await l1Unwrapper.l1FeeReceiver()).to.be.equal(multisig.address)
// ------------------------------------------------------------------------
// check withdraw with L1 fee ---------------------------------------------
const aliceKeypair = new Keypair() // contains private and public keys
// regular L1 deposit -------------------------------------------
const aliceDepositAmount = utils.parseEther('0.07')
const aliceDepositUtxo = new Utxo({ amount: aliceDepositAmount, keypair: aliceKeypair })
const { args, extData } = await prepareTransaction({
tornadoPool,
outputs: [aliceDepositUtxo],
})
let onTokenBridgedData = encodeDataForBridge({
proof: args,
extData,
})
let onTokenBridgedTx = await tornadoPool.populateTransaction.onTokenBridged(
token.address,
aliceDepositUtxo.amount,
onTokenBridgedData,
)
// emulating bridge. first it sends tokens to omnibridge mock then it sends to the pool
await token.transfer(omniBridge.address, aliceDepositAmount)
let transferTx = await token.populateTransaction.transfer(tornadoPool.address, aliceDepositAmount)
await omniBridge.execute([
{ who: token.address, callData: transferTx.data }, // send tokens to pool
{ who: tornadoPool.address, callData: onTokenBridgedTx.data }, // call onTokenBridgedTx
])
// withdrawal with L1 fee ---------------------------------------
// withdraws a part of his funds from the shielded pool
const aliceWithdrawAmount = utils.parseEther('0.06')
const l1Fee = utils.parseEther('0.01')
// sum of desired withdraw amount and L1 fee are stored in extAmount
const extAmount = aliceWithdrawAmount.add(l1Fee)
const recipient = '0xDeaD00000000000000000000000000000000BEEf'
const aliceChangeUtxo = new Utxo({
amount: aliceDepositAmount.sub(extAmount),
keypair: aliceKeypair,
})
await transaction({
tornadoPool,
inputs: [aliceDepositUtxo],
outputs: [aliceChangeUtxo],
recipient: recipient,
isL1Withdrawal: true,
l1Fee: l1Fee,
})
const filter = omniBridge.filters.OnTokenTransfer()
const fromBlock = await ethers.provider.getBlock()
const events = await omniBridge.queryFilter(filter, fromBlock.number)
onTokenBridgedData = events[0].args.data
const hexL1Fee = '0x' + events[0].args.data.toString().slice(42)
expect(ethers.BigNumber.from(hexL1Fee)).to.be.equal(l1Fee)
const recipientBalance = await token.balanceOf(recipient)
expect(recipientBalance).to.be.equal(0)
const omniBridgeBalance = await token.balanceOf(omniBridge.address)
expect(omniBridgeBalance).to.be.equal(extAmount)
// L1 transactions:
onTokenBridgedTx = await l1Unwrapper.populateTransaction.onTokenBridged(
l1Token.address,
extAmount,
onTokenBridgedData,
)
// emulating bridge. first it sends tokens to omniBridge mock then it sends to the recipient
await l1Token.transfer(omniBridge.address, extAmount)
transferTx = await l1Token.populateTransaction.transfer(l1Unwrapper.address, extAmount)
const senderBalanceBefore = await ethers.provider.getBalance(sender.address)
const multisigBalanceBefore = await ethers.provider.getBalance(multisig.address)
let tx = await omniBridge.execute([
{ who: l1Token.address, callData: transferTx.data }, // send tokens to L1Unwrapper
{ who: l1Unwrapper.address, callData: onTokenBridgedTx.data }, // call onTokenBridged on L1Unwrapper
])
let receipt = await tx.wait()
let txFee = receipt.cumulativeGasUsed.mul(receipt.effectiveGasPrice)
expect(await ethers.provider.getBalance(sender.address)).to.be.equal(senderBalanceBefore.sub(txFee))
expect(await ethers.provider.getBalance(multisig.address)).to.be.equal(multisigBalanceBefore.add(l1Fee))
expect(await ethers.provider.getBalance(recipient)).to.be.equal(aliceWithdrawAmount)
})
it('should transfer funds to multisig in case of L1 deposit fail', async function () {
const { tornadoPool, token, omniBridge, multisig } = await loadFixture(fixture)
const aliceKeypair = new Keypair() // contains private and public keys