Compare commits

...

3 Commits

Author SHA1 Message Date
derpy-duck 07558de31c remove console logs 2023-06-09 21:46:06 +00:00
derpy-duck e5b5fbfc71 Fix Delivery Provider Pricing and add a test 2023-06-09 21:12:52 +00:00
derpy-duck 014dc79f49 remove unnecessary comments 2023-06-09 18:44:03 +00:00
3 changed files with 154 additions and 42 deletions

View File

@ -171,15 +171,9 @@ library WeiLib {
uint32 multiplierDenom, uint32 multiplierDenom,
bool roundUp bool roundUp
) internal pure returns (Wei) { ) internal pure returns (Wei) {
// console.log("heyo");
Dollar numerator = w.toDollars(fromPrice).mul(multiplierNum); Dollar numerator = w.toDollars(fromPrice).mul(multiplierNum);
// console.log("numerator", numerator.unwrap());
// console.log("multiplierDenom", multiplierDenom);
// console.log("toPrice", toPrice.unwrap());
WeiPrice denom = toPrice.mul(multiplierDenom); WeiPrice denom = toPrice.mul(multiplierDenom);
// console.log("denom", denom.unwrap());
Wei res = numerator.toWei(denom, roundUp); Wei res = numerator.toWei(denom, roundUp);
// console.log("res", res.unwrap());
return res; return res;
} }
} }

View File

@ -29,17 +29,18 @@ contract DeliveryProvider is DeliveryProviderGovernance, IDeliveryProvider {
returns (LocalNative nativePriceQuote, GasPrice targetChainRefundPerUnitGasUnused) returns (LocalNative nativePriceQuote, GasPrice targetChainRefundPerUnitGasUnused)
{ {
(uint16 buffer, uint16 denominator) = assetConversionBuffer(targetChain); (uint16 buffer, uint16 denominator) = assetConversionBuffer(targetChain);
targetChainRefundPerUnitGasUnused = GasPrice.wrap(gasPrice(targetChain).unwrap() * (denominator - buffer) / denominator); targetChainRefundPerUnitGasUnused = GasPrice.wrap(gasPrice(targetChain).unwrap() * (denominator) / (uint256(denominator) + buffer));
TargetNative targetCostIfNoGasLimitUsed = gasLimit.toWei(targetChainRefundPerUnitGasUnused).asTargetNative(); TargetNative targetCostIfNoGasLimitUsed = gasLimit.toWei(targetChainRefundPerUnitGasUnused).asTargetNative();
TargetNative targetCostIfFullGasLimitUsed = gasLimit.toWei(gasPrice(targetChain)).asTargetNative(); TargetNative targetCostIfFullGasLimitUsed = gasLimit.toWei(gasPrice(targetChain)).asTargetNative();
LocalNative costIfNoGasLimitUsed = quoteAssetCost(targetChain, targetCostIfNoGasLimitUsed); LocalNative costIfNoGasLimitUsed = quoteAssetCost(targetChain, targetCostIfNoGasLimitUsed);
LocalNative costIfFullGasLimitUsed = gasLimit.toWei(quoteGasPrice(targetChain)).asLocalNative(); LocalNative costIfFullGasLimitUsed = quoteGasCost(targetChain, gasLimit);
LocalNative receiverValueCost = quoteAssetCost(targetChain, receiverValue); LocalNative receiverValueCost = quoteAssetCost(targetChain, receiverValue);
nativePriceQuote = quoteDeliveryOverhead(targetChain) + nativePriceQuote = quoteDeliveryOverhead(targetChain) + costIfFullGasLimitUsed + receiverValueCost;
costIfFullGasLimitUsed.asNative().max(costIfNoGasLimitUsed.asNative()).asLocalNative() + receiverValueCost; require(costIfNoGasLimitUsed.unwrap() <= costIfFullGasLimitUsed.unwrap(), "target chain refund per gas unused is too high");
require(targetCostIfNoGasLimitUsed.unwrap() <= targetCostIfFullGasLimitUsed.unwrap(), "target chain refund per gas unused is too high");
require( require(
receiverValue.asNative() + targetCostIfNoGasLimitUsed.asNative().max(targetCostIfFullGasLimitUsed.asNative()) <= maximumBudget(targetChain).asNative(), receiverValue.asNative() + targetCostIfFullGasLimitUsed.asNative() <= maximumBudget(targetChain).asNative(),
"Exceeds maximum budget" "Exceeds maximum budget"
); );
} }
@ -82,12 +83,10 @@ contract DeliveryProvider is DeliveryProviderGovernance, IDeliveryProvider {
return sourceChainAmount.asNative().convertAsset( return sourceChainAmount.asNative().convertAsset(
nativeCurrencyPrice(sourceChain), nativeCurrencyPrice(sourceChain),
nativeCurrencyPrice(targetChain), nativeCurrencyPrice(targetChain),
(buffer), (bufferDenominator),
(uint32(buffer) + bufferDenominator), (uint32(buffer) + bufferDenominator),
false false // round down
) ).asTargetNative();
// round down
.asTargetNative();
} }
//Returns the address on this chain that rewards should be sent to //Returns the address on this chain that rewards should be sent to
@ -116,27 +115,36 @@ contract DeliveryProvider is DeliveryProviderGovernance, IDeliveryProvider {
//Returns the delivery overhead fee required to deliver a message to the target chain, denominated in this chain's wei. //Returns the delivery overhead fee required to deliver a message to the target chain, denominated in this chain's wei.
function quoteDeliveryOverhead(uint16 targetChain) public view returns (LocalNative nativePriceQuote) { function quoteDeliveryOverhead(uint16 targetChain) public view returns (LocalNative nativePriceQuote) {
Gas overhead = deliverGasOverhead(targetChain); nativePriceQuote = quoteGasCost(targetChain, deliverGasOverhead(targetChain));
Wei targetFees = overhead.toWei(gasPrice(targetChain)); require(nativePriceQuote.unwrap() <= type(uint128).max, "Overflow");
Wei result = assetConversion(targetChain, targetFees, chainId());
require(result.unwrap() <= type(uint128).max, "Overflow");
return result.asLocalNative();
} }
//Returns the price of purchasing 1 unit of gas on the target chain, denominated in this chain's wei. //Returns the price of purchasing gasAmount units of gas on the target chain, denominated in this chain's wei.
function quoteGasPrice(uint16 targetChain) public view returns (GasPrice) { function quoteGasCost(uint16 targetChain, Gas gasAmount) public view returns (LocalNative totalCost) {
Wei gasPriceInSourceChainCurrency = Wei gasCostInSourceChainCurrency =
assetConversion(targetChain, gasPrice(targetChain).priceAsWei(), chainId()); assetConversion(targetChain, gasAmount.toWei(gasPrice(targetChain)), chainId());
require(gasPriceInSourceChainCurrency.unwrap() <= type(uint88).max, "Overflow"); totalCost = LocalNative.wrap(gasCostInSourceChainCurrency.unwrap());
return GasPrice.wrap(uint88(gasPriceInSourceChainCurrency.unwrap()));
} }
function quoteGasPrice(uint16 targetChain) public view returns (GasPrice price) {
price = GasPrice.wrap(quoteGasCost(targetChain, Gas.wrap(1)).unwrap());
require(price.unwrap() <= type(uint88).max, "Overflow");
}
error PriceIsZero(uint16 chain);
// relevant for chains that have dynamic execution pricing (e.g. Ethereum) // relevant for chains that have dynamic execution pricing (e.g. Ethereum)
function assetConversion( function assetConversion(
uint16 sourceChain, uint16 sourceChain,
Wei sourceAmount, Wei sourceAmount,
uint16 targetChain uint16 targetChain
) internal view returns (Wei targetAmount) { ) internal view returns (Wei targetAmount) {
if(nativeCurrencyPrice(sourceChain).unwrap() == 0) {
revert PriceIsZero(sourceChain);
}
if(nativeCurrencyPrice(targetChain).unwrap() == 0) {
revert PriceIsZero(targetChain);
}
return sourceAmount.convertAsset( return sourceAmount.convertAsset(
nativeCurrencyPrice(sourceChain), nativeCurrencyPrice(sourceChain),
nativeCurrencyPrice(targetChain), nativeCurrencyPrice(targetChain),
@ -152,11 +160,17 @@ contract DeliveryProvider is DeliveryProviderGovernance, IDeliveryProvider {
TargetNative targetChainAmount TargetNative targetChainAmount
) internal view returns (LocalNative currentChainAmount) { ) internal view returns (LocalNative currentChainAmount) {
(uint16 buffer, uint16 bufferDenominator) = assetConversionBuffer(targetChain); (uint16 buffer, uint16 bufferDenominator) = assetConversionBuffer(targetChain);
if(nativeCurrencyPrice(chainId()).unwrap() == 0) {
revert PriceIsZero(chainId());
}
if(nativeCurrencyPrice(targetChain).unwrap() == 0) {
revert PriceIsZero(targetChain);
}
return targetChainAmount.asNative().convertAsset( return targetChainAmount.asNative().convertAsset(
nativeCurrencyPrice(chainId()),
nativeCurrencyPrice(targetChain), nativeCurrencyPrice(targetChain),
nativeCurrencyPrice(chainId()),
(uint32(buffer) + bufferDenominator), (uint32(buffer) + bufferDenominator),
(buffer), (bufferDenominator),
// round up // round up
true true
).asLocalNative(); ).asLocalNative();

View File

@ -120,53 +120,52 @@ contract TestDeliveryProvider is Test {
deliveryProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice); deliveryProvider.updatePrice(updateChainId, updateGasPrice, updateNativeCurrencyPrice);
} }
/*
TODO: Uncomment these tests once revert messages are back in
function testCannotGetPriceBeforeUpdateSrcPrice( function testCannotGetPriceBeforeUpdateSrcPrice(
uint16 dstChainId, uint16 dstChainId,
uint128 dstGasPrice, uint64 dstGasPrice,
uint128 dstNativeCurrencyPrice WeiPrice dstNativeCurrencyPrice
) )
public public
{ {
vm.assume(dstChainId > 0); vm.assume(dstChainId > 0);
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID); vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID);
vm.assume(dstGasPrice > 0); vm.assume(dstGasPrice > 0);
vm.assume(dstNativeCurrencyPrice > 0); vm.assume(dstNativeCurrencyPrice.unwrap() > 0);
initializeDeliveryProvider(); initializeDeliveryProvider();
// update the price with reasonable values // update the price with reasonable values
deliveryProvider.updatePrice(dstChainId, dstGasPrice, dstNativeCurrencyPrice); deliveryProvider.updatePrice(dstChainId, GasPrice.wrap(dstGasPrice), dstNativeCurrencyPrice);
// you shall not pass // you shall not pass
vm.expectRevert("srcNativeCurrencyPrice == 0"); vm.expectRevert(abi.encodeWithSignature("PriceIsZero(uint16)", TEST_ORACLE_CHAIN_ID));
deliveryProvider.quoteDeliveryOverhead(dstChainId); deliveryProvider.quoteDeliveryOverhead(dstChainId);
} }
function testCannotGetPriceBeforeUpdateDstPrice( function testCannotGetPriceBeforeUpdateDstPrice(
uint16 dstChainId, uint16 dstChainId,
uint128 srcGasPrice, uint64 srcGasPrice,
uint128 srcNativeCurrencyPrice WeiPrice srcNativeCurrencyPrice
) )
public public
{ {
vm.assume(dstChainId > 0); vm.assume(dstChainId > 0);
vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID); vm.assume(dstChainId != TEST_ORACLE_CHAIN_ID);
vm.assume(srcGasPrice > 0); vm.assume(srcGasPrice > 0);
vm.assume(srcNativeCurrencyPrice > 0); vm.assume(srcNativeCurrencyPrice.unwrap() > 0);
initializeDeliveryProvider(); initializeDeliveryProvider();
// update the price with reasonable values // update the price with reasonable values
//vm.prank(deliveryProvider.owner()); //vm.prank(deliveryProvider.owner());
deliveryProvider.updatePrice(TEST_ORACLE_CHAIN_ID, srcGasPrice, srcNativeCurrencyPrice); deliveryProvider.updatePrice(TEST_ORACLE_CHAIN_ID, GasPrice.wrap(srcGasPrice), srcNativeCurrencyPrice);
// you shall not pass // you shall not pass
vm.expectRevert("dstNativeCurrencyPrice == 0"); vm.expectRevert(abi.encodeWithSignature("PriceIsZero(uint16)", dstChainId));
deliveryProvider.quoteDeliveryOverhead(dstChainId); deliveryProvider.quoteDeliveryOverhead(dstChainId);
} }
*/
function testUpdatePrice( function testUpdatePrice(
uint16 dstChainId, uint16 dstChainId,
@ -268,4 +267,109 @@ contract TestDeliveryProvider is Test {
assertTrue(newAddress == updated); 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");
}
} }