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 {
|
contract L1Unwrapper is WETHOmnibridgeRouter {
|
||||||
using SafeMath for uint256;
|
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);
|
event PublicKey(address indexed owner, bytes key);
|
||||||
|
|
||||||
struct Account {
|
struct Account {
|
||||||
@ -88,6 +95,22 @@ contract L1Unwrapper is WETHOmnibridgeRouter {
|
|||||||
uint256 l1Fee = BytesHelper.sliceToUint(_data, 20);
|
uint256 l1Fee = BytesHelper.sliceToUint(_data, 20);
|
||||||
|
|
||||||
AddressHelper.safeSendValue(payable(BytesHelper.bytesToAddress(_data)), _value.sub(l1Fee));
|
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)
|
let customConfig = Object.assign({}, config)
|
||||||
customConfig.omniBridge = omniBridge.address
|
customConfig.omniBridge = omniBridge.address
|
||||||
customConfig.weth = l1Token.address
|
customConfig.weth = l1Token.address
|
||||||
|
customConfig.multisig = multisig.address
|
||||||
const contracts = await generate(customConfig)
|
const contracts = await generate(customConfig)
|
||||||
await singletonFactory.deploy(contracts.unwrapperContract.bytecode, config.salt)
|
await singletonFactory.deploy(contracts.unwrapperContract.bytecode, config.salt)
|
||||||
const l1Unwrapper = await ethers.getContractAt('L1Unwrapper', contracts.unwrapperContract.address)
|
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)
|
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 () {
|
it('should transfer funds to multisig in case of L1 deposit fail', async function () {
|
||||||
const { tornadoPool, token, omniBridge, multisig } = await loadFixture(fixture)
|
const { tornadoPool, token, omniBridge, multisig } = await loadFixture(fixture)
|
||||||
const aliceKeypair = new Keypair() // contains private and public keys
|
const aliceKeypair = new Keypair() // contains private and public keys
|
||||||
|
Loading…
Reference in New Issue
Block a user