mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
add L1Receiver logic to unwrapper
This commit is contained in:
parent
abef8ad760
commit
0530f00af5
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user