408 lines
16 KiB
Solidity
408 lines
16 KiB
Solidity
// SPDX-License-Identifier: Apache 2
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "../../contracts/interfaces/relayer/IDeliveryProviderTyped.sol";
|
|
import "../../contracts/relayer/deliveryProvider/DeliveryProvider.sol";
|
|
import "../../contracts/relayer/deliveryProvider/DeliveryProviderSetup.sol";
|
|
import "../../contracts/relayer/deliveryProvider/DeliveryProviderImplementation.sol";
|
|
import "../../contracts/relayer/deliveryProvider/DeliveryProviderProxy.sol";
|
|
import "../../contracts/relayer/deliveryProvider/DeliveryProviderStructs.sol";
|
|
import "../../contracts/interfaces/relayer/TypedUnits.sol";
|
|
import {MockWormhole} from "./MockWormhole.sol";
|
|
import {IWormhole} from "../../contracts/interfaces/IWormhole.sol";
|
|
import {WormholeSimulator, FakeWormholeSimulator} from "./WormholeSimulator.sol";
|
|
|
|
import "forge-std/Test.sol";
|
|
|
|
contract TestDeliveryProvider is Test {
|
|
using WeiLib for Wei;
|
|
using GasLib for Gas;
|
|
using WeiPriceLib for WeiPrice;
|
|
using GasPriceLib for GasPrice;
|
|
|
|
uint16 constant TEST_ORACLE_CHAIN_ID = 2;
|
|
|
|
DeliveryProvider internal deliveryProvider;
|
|
|
|
function initializeDeliveryProvider() internal {
|
|
DeliveryProviderSetup deliveryProviderSetup = new DeliveryProviderSetup();
|
|
DeliveryProviderImplementation deliveryProviderImplementation =
|
|
new DeliveryProviderImplementation();
|
|
DeliveryProviderProxy myDeliveryProvider = new DeliveryProviderProxy(
|
|
address(deliveryProviderSetup),
|
|
abi.encodeCall(
|
|
DeliveryProviderSetup.setup,
|
|
(
|
|
address(deliveryProviderImplementation),
|
|
TEST_ORACLE_CHAIN_ID
|
|
)
|
|
)
|
|
);
|
|
|
|
deliveryProvider = DeliveryProvider(address(myDeliveryProvider));
|
|
|
|
require(deliveryProvider.owner() == address(this), "owner() != expected");
|
|
require(deliveryProvider.chainId() == TEST_ORACLE_CHAIN_ID, "chainId() != expected");
|
|
}
|
|
|
|
function testCannotUpdatePriceWithChainIdZero(
|
|
GasPrice updateGasPrice,
|
|
WeiPrice updateNativeCurrencyPrice
|
|
) public {
|
|
vm.assume(updateGasPrice.unwrap() > 0);
|
|
vm.assume(updateNativeCurrencyPrice.unwrap() > 0);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// you shall not pass
|
|
vm.expectRevert(abi.encodeWithSignature("ChainIdIsZero()"));
|
|
deliveryProvider.updatePrice(
|
|
0, // updateChainId
|
|
updateGasPrice,
|
|
updateNativeCurrencyPrice
|
|
);
|
|
}
|
|
|
|
function testCannotUpdatePriceWithGasPriceZero(
|
|
uint16 updateChainId,
|
|
WeiPrice updateNativeCurrencyPrice
|
|
) public {
|
|
vm.assume(updateChainId > 0);
|
|
vm.assume(updateNativeCurrencyPrice.unwrap() > 0);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// you shall not pass
|
|
vm.expectRevert(abi.encodeWithSignature("GasPriceIsZero()"));
|
|
deliveryProvider.updatePrice(
|
|
updateChainId,
|
|
GasPrice.wrap(0), // updateGasPrice == 0
|
|
updateNativeCurrencyPrice
|
|
);
|
|
}
|
|
|
|
function testCannotUpdatePriceWithNativeCurrencyPriceZero(
|
|
uint16 updateChainId,
|
|
GasPrice updateGasPrice
|
|
) public {
|
|
vm.assume(updateChainId > 0);
|
|
vm.assume(updateGasPrice.unwrap() > 0);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// you shall not pass
|
|
vm.expectRevert(abi.encodeWithSignature("NativeCurrencyPriceIsZero()"));
|
|
deliveryProvider.updatePrice(
|
|
updateChainId,
|
|
updateGasPrice,
|
|
WeiPrice.wrap(0) // updateNativeCurrencyPrice == 0
|
|
);
|
|
}
|
|
|
|
function testCanUpdatePriceOnlyAsOwnerOrPriceWallet(
|
|
address pricingWallet,
|
|
address maliciousUser,
|
|
uint16 updateChainId,
|
|
GasPrice updateGasPrice,
|
|
WeiPrice updateNativeCurrencyPrice
|
|
) public {
|
|
vm.assume(maliciousUser != address(0));
|
|
vm.assume(pricingWallet != address(0));
|
|
vm.assume(maliciousUser != pricingWallet);
|
|
vm.assume(maliciousUser != address(this));
|
|
vm.assume(updateChainId > 0);
|
|
vm.assume(updateGasPrice.unwrap() > 0);
|
|
vm.assume(updateNativeCurrencyPrice.unwrap() > 0);
|
|
vm.assume(updateGasPrice.unwrap() < type(uint64).max);
|
|
vm.assume(updateNativeCurrencyPrice.unwrap() < type(uint128).max);
|
|
|
|
initializeDeliveryProvider();
|
|
deliveryProvider.updatePricingWallet(pricingWallet);
|
|
|
|
// you shall not pass
|
|
vm.prank(maliciousUser);
|
|
vm.expectRevert(abi.encodeWithSignature("CallerMustBeOwnerOrPricingWallet()"));
|
|
deliveryProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
|
|
|
|
// pricing wallet
|
|
vm.prank(pricingWallet);
|
|
deliveryProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
|
|
|
|
// owner
|
|
deliveryProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
|
|
}
|
|
|
|
|
|
function testCannotGetPriceBeforeUpdateSrcPrice(
|
|
uint16 dstChainId,
|
|
uint64 dstGasPrice,
|
|
WeiPrice dstNativeCurrencyPrice
|
|
)
|
|
public
|
|
{
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID);
|
|
vm.assume(dstGasPrice > 0);
|
|
vm.assume(dstNativeCurrencyPrice.unwrap() > 0);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// update the price with reasonable values
|
|
deliveryProvider.updatePrice(dstChainId, GasPrice.wrap(dstGasPrice), dstNativeCurrencyPrice);
|
|
|
|
// you shall not pass
|
|
vm.expectRevert(abi.encodeWithSignature("PriceIsZero(uint16)", TEST_ORACLE_CHAIN_ID));
|
|
deliveryProvider.quoteDeliveryOverhead(dstChainId);
|
|
}
|
|
|
|
function testCannotGetPriceBeforeUpdateDstPrice(
|
|
uint16 dstChainId,
|
|
uint64 srcGasPrice,
|
|
WeiPrice srcNativeCurrencyPrice
|
|
)
|
|
public
|
|
{
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID);
|
|
vm.assume(srcGasPrice > 0);
|
|
vm.assume(srcNativeCurrencyPrice.unwrap() > 0);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// update the price with reasonable values
|
|
//vm.prank(deliveryProvider.owner());
|
|
deliveryProvider.updatePrice(TEST_ORACLE_CHAIN_ID, GasPrice.wrap(srcGasPrice), srcNativeCurrencyPrice);
|
|
|
|
// you shall not pass
|
|
vm.expectRevert(abi.encodeWithSignature("PriceIsZero(uint16)", dstChainId));
|
|
deliveryProvider.quoteDeliveryOverhead(dstChainId);
|
|
}
|
|
|
|
|
|
function testUpdatePrice(
|
|
uint16 dstChainId,
|
|
uint64 dstGasPrice,
|
|
uint64 dstNativeCurrencyPrice,
|
|
uint64 srcGasPrice,
|
|
uint64 srcNativeCurrencyPrice
|
|
) public {
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID);
|
|
vm.assume(dstGasPrice > 0);
|
|
vm.assume(dstNativeCurrencyPrice > 0);
|
|
vm.assume(srcGasPrice > 0);
|
|
vm.assume(srcNativeCurrencyPrice > 0);
|
|
vm.assume(uint256(dstGasPrice) * srcNativeCurrencyPrice >= dstNativeCurrencyPrice);
|
|
vm.assume(dstGasPrice * uint256(dstNativeCurrencyPrice) / srcNativeCurrencyPrice < 2 ** 72);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
// update the prices with reasonable values
|
|
deliveryProvider.updatePrice(
|
|
dstChainId, GasPrice.wrap(dstGasPrice), WeiPrice.wrap(dstNativeCurrencyPrice)
|
|
);
|
|
deliveryProvider.updatePrice(
|
|
TEST_ORACLE_CHAIN_ID, GasPrice.wrap(srcGasPrice), WeiPrice.wrap(srcNativeCurrencyPrice)
|
|
);
|
|
|
|
// verify price
|
|
uint256 expected = (
|
|
uint256(dstNativeCurrencyPrice) * (uint256(dstGasPrice)) + (srcNativeCurrencyPrice - 1)
|
|
) / srcNativeCurrencyPrice;
|
|
GasPrice readValues = deliveryProvider.quoteGasPrice(dstChainId);
|
|
console.log(readValues.unwrap(), expected);
|
|
require(readValues.unwrap() == expected, "deliveryProvider.quotePrices != expected");
|
|
}
|
|
|
|
struct UpdatePrice {
|
|
uint16 chainId;
|
|
uint128 gasPrice;
|
|
uint128 nativeCurrencyPrice;
|
|
}
|
|
|
|
function testBulkUpdatePrices(
|
|
uint16 dstChainId,
|
|
uint64 dstGasPrice,
|
|
uint64 dstNativeCurrencyPrice,
|
|
uint64 srcGasPrice,
|
|
uint64 srcNativeCurrencyPrice
|
|
) public {
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID); // wormhole.chainId()
|
|
vm.assume(dstGasPrice > 0);
|
|
vm.assume(dstNativeCurrencyPrice > 0);
|
|
vm.assume(srcGasPrice > 0);
|
|
vm.assume(srcNativeCurrencyPrice > 0);
|
|
vm.assume(dstGasPrice >= dstNativeCurrencyPrice / srcNativeCurrencyPrice);
|
|
vm.assume(dstGasPrice * uint256(dstNativeCurrencyPrice) / srcNativeCurrencyPrice < 2 ** 72);
|
|
|
|
initializeDeliveryProvider();
|
|
|
|
DeliveryProviderStructs.UpdatePrice[] memory updates =
|
|
new DeliveryProviderStructs.UpdatePrice[](2);
|
|
updates[0] = DeliveryProviderStructs.UpdatePrice({
|
|
chainId: TEST_ORACLE_CHAIN_ID,
|
|
gasPrice: GasPrice.wrap(srcGasPrice),
|
|
nativeCurrencyPrice: WeiPrice.wrap(srcNativeCurrencyPrice)
|
|
});
|
|
updates[1] = DeliveryProviderStructs.UpdatePrice({
|
|
chainId: dstChainId,
|
|
gasPrice: GasPrice.wrap(dstGasPrice),
|
|
nativeCurrencyPrice: WeiPrice.wrap(dstNativeCurrencyPrice)
|
|
});
|
|
|
|
// update the prices with reasonable values
|
|
deliveryProvider.updatePrices(updates);
|
|
|
|
// verify price
|
|
uint256 expected = (
|
|
uint256(dstNativeCurrencyPrice) * (uint256(dstGasPrice)) + (srcNativeCurrencyPrice - 1)
|
|
) / srcNativeCurrencyPrice;
|
|
GasPrice readValues = deliveryProvider.quoteGasPrice(dstChainId);
|
|
require(readValues.unwrap() == expected, "deliveryProvider.quotePrices != expected");
|
|
}
|
|
|
|
function testUpdateTargetChainContracts(uint16 targetChain, bytes32 newAddress) public {
|
|
initializeDeliveryProvider();
|
|
|
|
deliveryProvider.updateTargetChainAddress(targetChain, newAddress);
|
|
bytes32 updated = deliveryProvider.getTargetChainAddress(targetChain);
|
|
|
|
assertTrue(newAddress == updated);
|
|
}
|
|
|
|
function testUpdateRewardAddress(address payable newAddress) public {
|
|
initializeDeliveryProvider();
|
|
|
|
deliveryProvider.updateRewardAddress(newAddress);
|
|
address payable updated = deliveryProvider.getRewardAddress();
|
|
|
|
assertTrue(newAddress == updated);
|
|
}
|
|
|
|
function testQuoteDeliveryOverhead(
|
|
uint16 dstChainId,
|
|
uint64 dstGasPrice,
|
|
uint64 dstNativeCurrencyPrice,
|
|
uint64 srcGasPrice,
|
|
uint64 srcNativeCurrencyPrice,
|
|
uint32 gasOverhead
|
|
) public {
|
|
initializeDeliveryProvider();
|
|
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID); // wormhole.chainId()
|
|
vm.assume(dstGasPrice > 0);
|
|
vm.assume(dstNativeCurrencyPrice > 0);
|
|
vm.assume(srcGasPrice > 0);
|
|
vm.assume(srcNativeCurrencyPrice > 0);
|
|
vm.assume(dstGasPrice >= dstNativeCurrencyPrice / srcNativeCurrencyPrice);
|
|
vm.assume(dstGasPrice * uint256(dstNativeCurrencyPrice) / srcNativeCurrencyPrice < 2 ** 72);
|
|
|
|
vm.assume(gasOverhead < uint256(2)**31);
|
|
|
|
|
|
|
|
// update the prices with reasonable values
|
|
deliveryProvider.updatePrice(
|
|
dstChainId, GasPrice.wrap(dstGasPrice), WeiPrice.wrap(dstNativeCurrencyPrice)
|
|
);
|
|
deliveryProvider.updatePrice(
|
|
TEST_ORACLE_CHAIN_ID, GasPrice.wrap(srcGasPrice), WeiPrice.wrap(srcNativeCurrencyPrice)
|
|
);
|
|
|
|
deliveryProvider.updateAssetConversionBuffer(dstChainId, 5, 100);
|
|
|
|
deliveryProvider.updateDeliverGasOverhead(dstChainId, Gas.wrap(gasOverhead));
|
|
|
|
deliveryProvider.updateMaximumBudget(dstChainId, Wei.wrap(uint256(2)**191));
|
|
|
|
// verify price
|
|
uint256 expectedOverhead = (
|
|
uint256(dstNativeCurrencyPrice) * (uint256(dstGasPrice) * gasOverhead) + (srcNativeCurrencyPrice - 1)
|
|
) / srcNativeCurrencyPrice;
|
|
|
|
LocalNative deliveryOverhead = deliveryProvider.quoteDeliveryOverhead(dstChainId);
|
|
|
|
require(expectedOverhead == LocalNative.unwrap(deliveryOverhead), "deliveryProvider overhead quote is not what is expected");
|
|
}
|
|
|
|
function testQuoteDeliveryPrice(
|
|
uint16 dstChainId,
|
|
uint64 dstGasPrice,
|
|
uint64 dstNativeCurrencyPrice,
|
|
uint64 srcGasPrice,
|
|
uint64 srcNativeCurrencyPrice,
|
|
uint32 gasLimit,
|
|
uint32 gasOverhead,
|
|
uint64 receiverValue
|
|
) public {
|
|
initializeDeliveryProvider();
|
|
|
|
vm.assume(dstChainId > 0);
|
|
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID); // wormhole.chainId()
|
|
vm.assume(dstGasPrice > 0);
|
|
vm.assume(dstNativeCurrencyPrice > 0);
|
|
vm.assume(srcGasPrice > 0);
|
|
vm.assume(srcNativeCurrencyPrice > 0);
|
|
vm.assume(dstGasPrice >= dstNativeCurrencyPrice / srcNativeCurrencyPrice);
|
|
vm.assume(dstGasPrice * uint256(dstNativeCurrencyPrice) / srcNativeCurrencyPrice < 2 ** 72);
|
|
|
|
vm.assume(gasLimit < uint256(2)**31);
|
|
vm.assume(gasOverhead < uint256(2)**31);
|
|
|
|
// update the prices with reasonable values
|
|
deliveryProvider.updatePrice(
|
|
dstChainId, GasPrice.wrap(dstGasPrice), WeiPrice.wrap(dstNativeCurrencyPrice)
|
|
);
|
|
deliveryProvider.updatePrice(
|
|
TEST_ORACLE_CHAIN_ID, GasPrice.wrap(srcGasPrice), WeiPrice.wrap(srcNativeCurrencyPrice)
|
|
);
|
|
|
|
deliveryProvider.updateAssetConversionBuffer(dstChainId, 5, 100);
|
|
|
|
deliveryProvider.updateDeliverGasOverhead(dstChainId, Gas.wrap(gasOverhead));
|
|
|
|
deliveryProvider.updateMaximumBudget(dstChainId, Wei.wrap(uint256(2)**191));
|
|
|
|
// verify price
|
|
uint256 expectedGasCost = (
|
|
uint256(dstNativeCurrencyPrice) * (uint256(dstGasPrice) * (gasLimit)) + (srcNativeCurrencyPrice - 1)
|
|
) / srcNativeCurrencyPrice;
|
|
|
|
uint256 expectedOverheadCost = (
|
|
uint256(dstNativeCurrencyPrice) * (uint256(dstGasPrice) * ( gasOverhead)) + (srcNativeCurrencyPrice - 1)
|
|
) / srcNativeCurrencyPrice;
|
|
|
|
uint256 expectedReceiverValueCost = (
|
|
uint256(dstNativeCurrencyPrice) * (receiverValue) * 105 + (srcNativeCurrencyPrice * uint256(100) - 1)
|
|
) / srcNativeCurrencyPrice / 100;
|
|
|
|
(LocalNative nativePriceQuote,) = deliveryProvider.quoteEvmDeliveryPrice(dstChainId, Gas.wrap(gasLimit), TargetNative.wrap(receiverValue));
|
|
|
|
require(expectedGasCost == LocalNative.unwrap(deliveryProvider.quoteGasCost(dstChainId, Gas.wrap(gasLimit))), "Gas cost is not what is expected");
|
|
require(expectedOverheadCost == LocalNative.unwrap(deliveryProvider.quoteGasCost(dstChainId, Gas.wrap(gasOverhead))), "Overhead cost is not what is expected");
|
|
|
|
require(expectedGasCost + expectedOverheadCost + expectedReceiverValueCost == LocalNative.unwrap(nativePriceQuote), "deliveryProvider price quote is not what is expected");
|
|
|
|
}
|
|
|
|
function testIsMessageKeyTypeSupported(uint8 keyType) public {
|
|
initializeDeliveryProvider();
|
|
|
|
assertFalse(deliveryProvider.isMessageKeyTypeSupported(keyType));
|
|
deliveryProvider.updateSupportedMessageKeyTypes(keyType, true);
|
|
assertTrue(deliveryProvider.isMessageKeyTypeSupported(keyType));
|
|
deliveryProvider.updateSupportedMessageKeyTypes(keyType, false);
|
|
assertFalse(deliveryProvider.isMessageKeyTypeSupported(keyType));
|
|
|
|
assertFalse(deliveryProvider.isMessageKeyTypeSupported(15));
|
|
deliveryProvider.updateSupportedMessageKeyTypes(15, true);
|
|
assertTrue(deliveryProvider.isMessageKeyTypeSupported(15));
|
|
deliveryProvider.updateSupportedMessageKeyTypes(15, false);
|
|
assertFalse(deliveryProvider.isMessageKeyTypeSupported(15));
|
|
assertFalse(deliveryProvider.isMessageKeyTypeSupported(15));
|
|
}
|
|
}
|