-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
// The Licensed Work is (c) 2022 Sygma | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
const TruffleAssert = require("truffle-assertions"); | ||
const Ethers = require("ethers"); | ||
|
||
const Helpers = require("../helpers"); | ||
|
||
const DefaultMessageReceiverContract = artifacts.require("DefaultMessageReceiver"); | ||
const NativeTokenHandlerContract = artifacts.require("NativeTokenHandler"); | ||
const NativeTokenAdapterContract = artifacts.require("NativeTokenAdapter"); | ||
const BasicFeeHandlerContract = artifacts.require("BasicFeeHandler"); | ||
const FeeHandlerRouterContract = artifacts.require("FeeHandlerRouter"); | ||
const ERC20MintableContract = artifacts.require("ERC20PresetMinterPauser"); | ||
|
||
contract("Bridge - [execute proposal - native token]", async (accounts) => { | ||
const originDomainID = 1; | ||
const destinationDomainID = 2; | ||
const adminAddress = accounts[0]; | ||
const depositorAddress = accounts[1]; | ||
const evmRecipientAddress = accounts[2]; | ||
const relayer1Address = accounts[3]; | ||
|
||
const expectedDepositNonce = 1; | ||
const emptySetResourceData = "0x"; | ||
const resourceID = "0x0000000000000000000000000000000000000000000000000000000000000650"; | ||
const depositAmount = Ethers.utils.parseEther("1"); | ||
const fee = Ethers.utils.parseEther("0.1"); | ||
const transferredAmount = depositAmount.sub(fee); | ||
const transactionId = "0x0000000000000000000000000000000000000000000000000000000000000001"; | ||
const executionGasAmount = 30000000; | ||
|
||
let BridgeInstance; | ||
let DefaultMessageReceiverInstance; | ||
let NativeTokenHandlerInstance; | ||
let BasicFeeHandlerInstance; | ||
let FeeHandlerRouterInstance; | ||
let NativeTokenAdapterInstance; | ||
let ERC20MintableInstance; | ||
let proposal; | ||
let depositProposalData; | ||
let message; | ||
|
||
beforeEach(async () => { | ||
await Promise.all([ | ||
(BridgeInstance = await Helpers.deployBridge( | ||
destinationDomainID, | ||
adminAddress | ||
)), | ||
]); | ||
|
||
|
||
FeeHandlerRouterInstance = await FeeHandlerRouterContract.new( | ||
BridgeInstance.address | ||
); | ||
BasicFeeHandlerInstance = await BasicFeeHandlerContract.new( | ||
BridgeInstance.address, | ||
FeeHandlerRouterInstance.address | ||
); | ||
NativeTokenAdapterInstance = await NativeTokenAdapterContract.new( | ||
BridgeInstance.address, | ||
resourceID | ||
); | ||
DefaultMessageReceiverInstance = await DefaultMessageReceiverContract.new([], 100000); | ||
NativeTokenHandlerInstance = await NativeTokenHandlerContract.new( | ||
BridgeInstance.address, | ||
NativeTokenAdapterInstance.address, | ||
DefaultMessageReceiverInstance.address, | ||
); | ||
await DefaultMessageReceiverInstance.grantRole( | ||
await DefaultMessageReceiverInstance.SYGMA_HANDLER_ROLE(), | ||
NativeTokenHandlerInstance.address | ||
); | ||
ERC20MintableInstance = await ERC20MintableContract.new( | ||
"token", | ||
"TOK" | ||
); | ||
|
||
await BridgeInstance.adminSetResource( | ||
NativeTokenHandlerInstance.address, | ||
resourceID, | ||
Ethers.constants.AddressZero, | ||
emptySetResourceData | ||
); | ||
await BasicFeeHandlerInstance.changeFee(destinationDomainID, resourceID, fee); | ||
await BridgeInstance.adminChangeFeeHandler(FeeHandlerRouterInstance.address), | ||
await FeeHandlerRouterInstance.adminSetResourceHandler( | ||
originDomainID, | ||
resourceID, | ||
BasicFeeHandlerInstance.address | ||
), | ||
|
||
await ERC20MintableInstance.grantRole( | ||
await ERC20MintableInstance.MINTER_ROLE(), | ||
DefaultMessageReceiverInstance.address | ||
); | ||
|
||
const mintableERC20Iface = new Ethers.utils.Interface(["function mint(address to, uint256 amount)"]); | ||
const actions = [{ | ||
nativeValue: 0, | ||
callTo: ERC20MintableInstance.address, | ||
approveTo: Ethers.constants.AddressZero, | ||
tokenSend: Ethers.constants.AddressZero, | ||
tokenReceive: Ethers.constants.AddressZero, | ||
data: mintableERC20Iface.encodeFunctionData("mint", [evmRecipientAddress, "20"]), | ||
}] | ||
message = Helpers.createMessageCallData( | ||
transactionId, | ||
actions, | ||
evmRecipientAddress | ||
); | ||
|
||
depositProposalData = Helpers.createOptionalContractCallDepositData( | ||
transferredAmount, | ||
Ethers.constants.AddressZero, | ||
executionGasAmount, | ||
message | ||
); | ||
|
||
proposal = { | ||
originDomainID: originDomainID, | ||
depositNonce: expectedDepositNonce, | ||
resourceID: resourceID, | ||
data: depositProposalData | ||
}; | ||
|
||
// set MPC address to unpause the Bridge | ||
await BridgeInstance.endKeygen(Helpers.mpcAddress); | ||
}); | ||
|
||
it("should revert if handler does not have SYGMA_HANDLER_ROLE", async () => { | ||
await DefaultMessageReceiverInstance.revokeRole( | ||
await DefaultMessageReceiverInstance.SYGMA_HANDLER_ROLE(), | ||
NativeTokenHandlerInstance.address | ||
); | ||
const proposalSignedData = await Helpers.signTypedProposal( | ||
BridgeInstance.address, | ||
[proposal] | ||
); | ||
|
||
// depositorAddress makes initial deposit of depositAmount | ||
assert.isFalse(await BridgeInstance.paused()); | ||
await TruffleAssert.passes( | ||
NativeTokenAdapterInstance.depositToEVMWithMessage( | ||
originDomainID, | ||
Ethers.constants.AddressZero, | ||
executionGasAmount, | ||
message, | ||
{ | ||
from: depositorAddress, | ||
value: depositAmount | ||
}) | ||
); | ||
|
||
const executeTx = await BridgeInstance.executeProposal( | ||
proposal, | ||
proposalSignedData, | ||
{ | ||
from: relayer1Address, | ||
gas: executionGasAmount | ||
} | ||
); | ||
|
||
TruffleAssert.eventEmitted(executeTx, "FailedHandlerExecution", (event) => { | ||
return ( | ||
event.originDomainID.toNumber() === originDomainID && | ||
event.depositNonce.toNumber() === expectedDepositNonce && | ||
event.lowLevelData === "0xdeda9030" // InsufficientPermission() | ||
); | ||
}); | ||
}); | ||
|
||
it("should revert if insufficient gas limit left for executing action", async () => { | ||
const insufficientExecutionGasAmount = 100000; | ||
const proposalSignedData = await Helpers.signTypedProposal( | ||
BridgeInstance.address, | ||
[proposal] | ||
); | ||
|
||
// depositorAddress makes initial deposit of depositAmount | ||
assert.isFalse(await BridgeInstance.paused()); | ||
await TruffleAssert.passes( | ||
NativeTokenAdapterInstance.depositToEVMWithMessage( | ||
originDomainID, | ||
Ethers.constants.AddressZero, | ||
insufficientExecutionGasAmount, | ||
message, | ||
{ | ||
from: depositorAddress, | ||
value: depositAmount | ||
}) | ||
); | ||
|
||
const executeTx = await BridgeInstance.executeProposal( | ||
proposal, | ||
proposalSignedData, | ||
{ | ||
from: relayer1Address, | ||
gas: insufficientExecutionGasAmount | ||
} | ||
); | ||
|
||
TruffleAssert.eventEmitted(executeTx, "FailedHandlerExecution", (event) => { | ||
return ( | ||
event.originDomainID.toNumber() === originDomainID && | ||
event.depositNonce.toNumber() === expectedDepositNonce && | ||
event.lowLevelData === "0x60ee1247" // InsufficientGasLimit() | ||
); | ||
}); | ||
}); | ||
|
||
it("should fail to transfer funds if invalid message is provided", async () => { | ||
const mintableERC20Iface = new Ethers.utils.Interface(["function mint(address to, uint256 amount)"]); | ||
const actions = [{ | ||
nativeValue: 0, | ||
callTo: Ethers.constants.AddressZero, | ||
approveTo: Ethers.constants.AddressZero, | ||
tokenSend: Ethers.constants.AddressZero, | ||
tokenReceive: Ethers.constants.AddressZero, | ||
data: mintableERC20Iface.encodeFunctionData("mint", [evmRecipientAddress, "20"]), | ||
}] | ||
const message = Helpers.createMessageCallData( | ||
transactionId, | ||
actions, | ||
evmRecipientAddress | ||
); | ||
|
||
const depositProposalData = Helpers.createOptionalContractCallDepositData( | ||
transferredAmount, | ||
Ethers.constants.AddressZero, | ||
executionGasAmount, | ||
message | ||
); | ||
|
||
const proposal = { | ||
originDomainID: originDomainID, | ||
depositNonce: expectedDepositNonce, | ||
resourceID: resourceID, | ||
data: depositProposalData | ||
}; | ||
const proposalSignedData = await Helpers.signTypedProposal( | ||
BridgeInstance.address, | ||
[proposal] | ||
); | ||
|
||
const executeTx = await BridgeInstance.executeProposal( | ||
proposal, | ||
proposalSignedData, | ||
{ | ||
from: relayer1Address, | ||
gas: executionGasAmount | ||
} | ||
); | ||
|
||
TruffleAssert.eventEmitted(executeTx, "FailedHandlerExecution", (event) => { | ||
return ( | ||
event.originDomainID.toNumber() === originDomainID && | ||
event.depositNonce.toNumber() === expectedDepositNonce && | ||
event.lowLevelData === "0x2ed7fc0e" // FailedFundsTransfer() | ||
); | ||
}); | ||
}); | ||
}); |