179 lines
7.2 KiB
Solidity
179 lines
7.2 KiB
Solidity
// SPDX-License-Identifier: Apache 2
|
|
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "./DeliveryProviderGovernance.sol";
|
|
import "./DeliveryProviderStructs.sol";
|
|
import "../../interfaces/relayer/IDeliveryProviderTyped.sol";
|
|
import "../../interfaces/relayer/TypedUnits.sol";
|
|
import "../../libraries/relayer/ExecutionParameters.sol";
|
|
import {IWormhole} from "../../interfaces/IWormhole.sol";
|
|
|
|
contract DeliveryProvider is DeliveryProviderGovernance, IDeliveryProvider {
|
|
using WeiLib for Wei;
|
|
using GasLib for Gas;
|
|
using GasPriceLib for GasPrice;
|
|
using WeiPriceLib for WeiPrice;
|
|
using TargetNativeLib for TargetNative;
|
|
using LocalNativeLib for LocalNative;
|
|
|
|
error CallerNotApproved(address msgSender);
|
|
|
|
function quoteEvmDeliveryPrice(
|
|
uint16 targetChain,
|
|
Gas gasLimit,
|
|
TargetNative receiverValue
|
|
)
|
|
public
|
|
view
|
|
returns (LocalNative nativePriceQuote, GasPrice targetChainRefundPerUnitGasUnused)
|
|
{
|
|
(uint16 buffer, uint16 denominator) = assetConversionBuffer(targetChain);
|
|
targetChainRefundPerUnitGasUnused = GasPrice.wrap(gasPrice(targetChain).unwrap() * (denominator) / (uint256(denominator) + buffer));
|
|
|
|
TargetNative targetCostIfNoGasLimitUsed = gasLimit.toWei(targetChainRefundPerUnitGasUnused).asTargetNative();
|
|
TargetNative targetCostIfFullGasLimitUsed = gasLimit.toWei(gasPrice(targetChain)).asTargetNative();
|
|
LocalNative costIfNoGasLimitUsed = quoteAssetCost(targetChain, targetCostIfNoGasLimitUsed);
|
|
LocalNative costIfFullGasLimitUsed = quoteGasCost(targetChain, gasLimit);
|
|
LocalNative receiverValueCost = quoteAssetCost(targetChain, receiverValue);
|
|
nativePriceQuote = quoteDeliveryOverhead(targetChain) + costIfFullGasLimitUsed + 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(
|
|
receiverValue.asNative() + targetCostIfFullGasLimitUsed.asNative() <= maximumBudget(targetChain).asNative(),
|
|
"Exceeds maximum budget"
|
|
);
|
|
}
|
|
|
|
function quoteDeliveryPrice(
|
|
uint16 targetChain,
|
|
TargetNative receiverValue,
|
|
bytes memory encodedExecutionParams
|
|
) external view returns (LocalNative nativePriceQuote, bytes memory encodedExecutionInfo) {
|
|
ExecutionParamsVersion version = decodeExecutionParamsVersion(encodedExecutionParams);
|
|
if (version == ExecutionParamsVersion.EVM_V1) {
|
|
EvmExecutionParamsV1 memory parsed = decodeEvmExecutionParamsV1(encodedExecutionParams);
|
|
GasPrice targetChainRefundPerUnitGasUnused;
|
|
(nativePriceQuote, targetChainRefundPerUnitGasUnused) =
|
|
quoteEvmDeliveryPrice(targetChain, parsed.gasLimit, receiverValue);
|
|
return (
|
|
nativePriceQuote,
|
|
encodeEvmExecutionInfoV1(
|
|
EvmExecutionInfoV1(parsed.gasLimit, targetChainRefundPerUnitGasUnused)
|
|
)
|
|
);
|
|
} else {
|
|
revert UnsupportedExecutionParamsVersion(uint8(version));
|
|
}
|
|
}
|
|
|
|
function quoteAssetConversion(
|
|
uint16 targetChain,
|
|
LocalNative currentChainAmount
|
|
) public view returns (TargetNative targetChainAmount) {
|
|
return quoteAssetConversion(chainId(), targetChain, currentChainAmount);
|
|
}
|
|
|
|
function quoteAssetConversion(
|
|
uint16 sourceChain,
|
|
uint16 targetChain,
|
|
LocalNative sourceChainAmount
|
|
) internal view returns (TargetNative targetChainAmount) {
|
|
(uint16 buffer, uint16 bufferDenominator) = assetConversionBuffer(targetChain);
|
|
return sourceChainAmount.asNative().convertAsset(
|
|
nativeCurrencyPrice(sourceChain),
|
|
nativeCurrencyPrice(targetChain),
|
|
(bufferDenominator),
|
|
(uint32(buffer) + bufferDenominator),
|
|
false // round down
|
|
).asTargetNative();
|
|
}
|
|
|
|
//Returns the address on this chain that rewards should be sent to
|
|
function getRewardAddress() public view override returns (address payable) {
|
|
return rewardAddress();
|
|
}
|
|
|
|
function isChainSupported(uint16 targetChain) public view override returns (bool supported) {
|
|
return _state.supportedChains[targetChain];
|
|
}
|
|
|
|
function getTargetChainAddress(uint16 targetChain)
|
|
public
|
|
view
|
|
override
|
|
returns (bytes32 deliveryProviderAddress)
|
|
{
|
|
return targetChainAddress(targetChain);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* HELPER METHODS
|
|
*
|
|
*/
|
|
|
|
//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) {
|
|
nativePriceQuote = quoteGasCost(targetChain, deliverGasOverhead(targetChain));
|
|
require(nativePriceQuote.unwrap() <= type(uint128).max, "Overflow");
|
|
}
|
|
|
|
//Returns the price of purchasing gasAmount units of gas on the target chain, denominated in this chain's wei.
|
|
function quoteGasCost(uint16 targetChain, Gas gasAmount) public view returns (LocalNative totalCost) {
|
|
Wei gasCostInSourceChainCurrency =
|
|
assetConversion(targetChain, gasAmount.toWei(gasPrice(targetChain)), chainId());
|
|
totalCost = LocalNative.wrap(gasCostInSourceChainCurrency.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)
|
|
function assetConversion(
|
|
uint16 sourceChain,
|
|
Wei sourceAmount,
|
|
uint16 targetChain
|
|
) internal view returns (Wei targetAmount) {
|
|
if(nativeCurrencyPrice(sourceChain).unwrap() == 0) {
|
|
revert PriceIsZero(sourceChain);
|
|
}
|
|
if(nativeCurrencyPrice(targetChain).unwrap() == 0) {
|
|
revert PriceIsZero(targetChain);
|
|
}
|
|
return sourceAmount.convertAsset(
|
|
nativeCurrencyPrice(sourceChain),
|
|
nativeCurrencyPrice(targetChain),
|
|
1,
|
|
1,
|
|
// round up
|
|
true
|
|
);
|
|
}
|
|
|
|
function quoteAssetCost(
|
|
uint16 targetChain,
|
|
TargetNative targetChainAmount
|
|
) internal view returns (LocalNative currentChainAmount) {
|
|
(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(
|
|
nativeCurrencyPrice(targetChain),
|
|
nativeCurrencyPrice(chainId()),
|
|
(uint32(buffer) + bufferDenominator),
|
|
(bufferDenominator),
|
|
// round up
|
|
true
|
|
).asLocalNative();
|
|
}
|
|
}
|