Skip to content

Commit 310c571

Browse files
committed
impl
Signed-off-by: Ihor Farion <ihor@umaproject.org>
1 parent c4dd51e commit 310c571

File tree

5 files changed

+74
-19
lines changed

5 files changed

+74
-19
lines changed

contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ import { BytesLib } from "../../../external/libraries/BytesLib.sol";
55
/// @notice Codec for params passed in OFT `composeMsg`.
66
library ComposeMsgCodec {
77
uint256 internal constant NONCE_OFFSET = 0;
8-
uint256 internal constant DEADLINE_OFFSET = 32;
9-
uint256 internal constant MAX_BPS_TO_SPONSOR_OFFSET = 64;
10-
uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 96;
11-
uint256 internal constant FINAL_RECIPIENT_OFFSET = 128;
12-
uint256 internal constant FINAL_TOKEN_OFFSET = 160;
13-
uint256 internal constant DESTINATION_DEX_OFFSET = 192;
14-
uint256 internal constant ACCOUNT_CREATION_MODE_OFFSET = 224;
15-
uint256 internal constant EXECUTION_MODE_OFFSET = 256;
16-
// Minimum length with empty actionData: 9 regular params (32 bytes each) and 1 dynamic byte array (minumum 64 bytes)
17-
uint256 internal constant MIN_COMPOSE_MSG_BYTE_LENGTH = 352;
8+
uint256 internal constant AMOUNT_LD_OFFSET = 32;
9+
uint256 internal constant DEADLINE_OFFSET = 64;
10+
uint256 internal constant MAX_BPS_TO_SPONSOR_OFFSET = 96;
11+
uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 128;
12+
uint256 internal constant FINAL_RECIPIENT_OFFSET = 160;
13+
uint256 internal constant FINAL_TOKEN_OFFSET = 192;
14+
uint256 internal constant DESTINATION_DEX_OFFSET = 224;
15+
uint256 internal constant ACCOUNT_CREATION_MODE_OFFSET = 256;
16+
uint256 internal constant EXECUTION_MODE_OFFSET = 288;
17+
// Minimum length with empty actionData: 10 regular params (32 bytes each) and 1 dynamic byte array (minumum 64 bytes)
18+
uint256 internal constant MIN_COMPOSE_MSG_BYTE_LENGTH = 384;
1819

1920
function _encode(
2021
bytes32 nonce,
22+
uint256 amountLD,
2123
uint256 deadline,
2224
uint256 maxBpsToSponsor,
2325
uint256 maxUserSlippageBps,
@@ -31,6 +33,7 @@ library ComposeMsgCodec {
3133
return
3234
abi.encode(
3335
nonce,
36+
amountLD,
3437
deadline,
3538
maxBpsToSponsor,
3639
maxUserSlippageBps,
@@ -47,6 +50,10 @@ library ComposeMsgCodec {
4750
return BytesLib.toBytes32(data, NONCE_OFFSET);
4851
}
4952

53+
function _getAmountLD(bytes memory data) internal pure returns (uint256 v) {
54+
return BytesLib.toUint256(data, AMOUNT_LD_OFFSET);
55+
}
56+
5057
function _getDeadline(bytes memory data) internal pure returns (uint256 v) {
5158
return BytesLib.toUint256(data, DEADLINE_OFFSET);
5259
}
@@ -83,9 +90,9 @@ library ComposeMsgCodec {
8390
}
8491

8592
function _getActionData(bytes memory data) internal pure returns (bytes memory v) {
86-
(, , , , , , , , , bytes memory actionData) = abi.decode(
93+
(, , , , , , , , , , bytes memory actionData) = abi.decode(
8794
data,
88-
(bytes32, uint256, uint256, uint256, bytes32, bytes32, uint32, uint8, uint8, bytes)
95+
(bytes32, uint256, uint256, uint256, uint256, bytes32, bytes32, uint32, uint8, uint8, bytes)
8996
);
9097
return actionData;
9198
}

contracts/periphery/mintburn/sponsored-oft/DstOFTHandler.sol

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ contract DstOFTHandler is BaseModuleHandler, ILayerZeroComposer, ArbitraryEVMFlo
2727
using AddressToBytes32 for address;
2828
using SafeERC20 for IERC20;
2929

30-
/// @notice We expect bridge amount that comes through to this Handler to be 1:1 with the src send amount, and we
31-
/// require our src handler to ensure that it is. We don't sponsor extra bridge fees in this handler
32-
uint256 public constant EXTRA_FEES_TO_SPONSOR = 0;
33-
3430
address public immutable OFT_ENDPOINT_ADDRESS;
3531
address public immutable IOFT_ADDRESS;
3632

@@ -162,6 +158,15 @@ contract DstOFTHandler is BaseModuleHandler, ILayerZeroComposer, ArbitraryEVMFlo
162158
$.usedNonces[quoteNonce] = true;
163159

164160
uint256 amountLD = OFTComposeMsgCodec.amountLD(_message);
161+
// We trust the src keyword to record the real send amount.
162+
// This is safe because the source contract validates the signature of the quote.
163+
uint256 amountSentLD = composeMsg._getAmountLD();
164+
165+
uint256 extraFeesIncurred = 0;
166+
if (amountSentLD > amountLD) {
167+
extraFeesIncurred = amountSentLD - amountLD;
168+
}
169+
165170
uint256 maxBpsToSponsor = composeMsg._getMaxBpsToSponsor();
166171
uint256 maxUserSlippageBps = composeMsg._getMaxUserSlippageBps();
167172
address finalRecipient = composeMsg._getFinalRecipient().toAddress();
@@ -178,7 +183,7 @@ contract DstOFTHandler is BaseModuleHandler, ILayerZeroComposer, ArbitraryEVMFlo
178183
finalToken: finalToken,
179184
destinationDex: destinationDex,
180185
maxBpsToSponsor: maxBpsToSponsor,
181-
extraFeesIncurred: EXTRA_FEES_TO_SPONSOR,
186+
extraFeesIncurred: extraFeesIncurred,
182187
accountCreationMode: AccountCreationMode(accountCreationMode)
183188
});
184189

contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ library QuoteSignLib {
3131
p.destinationDex,
3232
p.lzReceiveGasLimit,
3333
p.lzComposeGasLimit,
34+
p.maxOftFeeBps,
3435
p.accountCreationMode,
3536
p.executionMode,
3637
keccak256(p.actionData) // Hash the actionData to keep signature size reasonable

contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ contract SponsoredOFTSrcPeriphery is Ownable {
3030
/// @notice Source endpoint id
3131
uint32 public immutable SRC_EID;
3232

33+
mapping(uint32 dstEid => int8 decimalDiff) public dstEidToDecimalsDiff;
34+
3335
/// @custom:storage-location erc7201:SponsoredOFTSrcPeriphery.main
3436
struct MainStorage {
3537
/// @notice Signer public key to check the signed quote against
@@ -83,6 +85,8 @@ contract SponsoredOFTSrcPeriphery is Ownable {
8385
error NonceAlreadyUsed();
8486
/// @notice Thrown when provided msg.value is not sufficient to cover OFT bridging fee
8587
error InsufficientNativeFee();
88+
/// @notice Thrown when array lengths do not match
89+
error ArrayLengthMismatch();
8690

8791
constructor(address _token, address _oftMessenger, uint32 _srcEid, address _signer) {
8892
TOKEN = _token;
@@ -161,8 +165,14 @@ contract SponsoredOFTSrcPeriphery is Ownable {
161165
function _buildOftTransfer(
162166
Quote calldata quote
163167
) internal view returns (SendParam memory, MessagingFee memory, address) {
168+
uint256 amountDstLD = _applyDecimalDiff(
169+
quote.signedParams.amountLD,
170+
dstEidToDecimalsDiff[quote.signedParams.dstEid]
171+
);
172+
164173
bytes memory composeMsg = ComposeMsgCodec._encode(
165174
quote.signedParams.nonce,
175+
amountDstLD,
166176
quote.signedParams.deadline,
167177
quote.signedParams.maxBpsToSponsor,
168178
quote.unsignedParams.maxUserSlippageBps,
@@ -179,12 +189,14 @@ contract SponsoredOFTSrcPeriphery is Ownable {
179189
.addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0))
180190
.addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0));
181191

192+
// Use maxOftFeeBps to calculate minAmountLD based on expected destination amount
193+
uint256 minAmountLD = (amountDstLD * (10000 - quote.signedParams.maxOftFeeBps)) / 10000;
194+
182195
SendParam memory sendParam = SendParam(
183196
quote.signedParams.dstEid,
184197
quote.signedParams.destinationHandler,
185-
// Only support OFT sends that don't take fees in sent token. Set `minAmountLD = amountLD` to enforce this
186-
quote.signedParams.amountLD,
187198
quote.signedParams.amountLD,
199+
minAmountLD,
188200
extraOptions,
189201
composeMsg,
190202
// Only support empty OFT commands
@@ -196,6 +208,21 @@ contract SponsoredOFTSrcPeriphery is Ownable {
196208
return (sendParam, fee, quote.unsignedParams.refundRecipient);
197209
}
198210

211+
/**
212+
* @notice Applies decimal difference to the amount
213+
* @param amount The amount to adjust
214+
* @param diff The decimal difference (positive: multiply, negative: divide)
215+
* @return The adjusted amount
216+
*/
217+
function _applyDecimalDiff(uint256 amount, int8 diff) internal pure returns (uint256) {
218+
if (diff > 0) {
219+
return amount * (10 ** uint8(diff));
220+
} else if (diff < 0) {
221+
return amount / (10 ** uint8(-diff));
222+
}
223+
return amount;
224+
}
225+
199226
function _validateQuote(Quote calldata quote, bytes calldata signature) internal view {
200227
MainStorage storage $ = _getMainStorage();
201228
if (!QuoteSignLib.isSignatureValid($.signer, quote.signedParams, signature)) {
@@ -215,4 +242,18 @@ contract SponsoredOFTSrcPeriphery is Ownable {
215242
function setSigner(address _newSigner) external onlyOwner {
216243
_getMainStorage().signer = _newSigner;
217244
}
245+
246+
/**
247+
* @notice Sets the decimal difference for destination chains
248+
* @param dstEids Array of destination endpoint IDs
249+
* @param decimalDiffs Array of decimal differences (positive for Src < Dst, negative for Src > Dst)
250+
*/
251+
function setDstEidToDecimalsDiff(uint32[] calldata dstEids, int8[] calldata decimalDiffs) external onlyOwner {
252+
if (dstEids.length != decimalDiffs.length) {
253+
revert ArrayLengthMismatch();
254+
}
255+
for (uint256 i = 0; i < dstEids.length; ++i) {
256+
dstEidToDecimalsDiff[dstEids[i]] = decimalDiffs[i];
257+
}
258+
}
218259
}

contracts/periphery/mintburn/sponsored-oft/Structs.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct SignedQuoteParams {
3535
uint256 lzReceiveGasLimit; // gas limit for `lzReceive` call on destination side
3636
uint256 lzComposeGasLimit; // gas limit for `lzCompose` call on destination side
3737
// Execution mode and action data
38+
uint256 maxOftFeeBps; // max fee deducted by the OFT bridge
3839
uint8 accountCreationMode; // AccountCreationMode: Standard or FromUserFunds
3940
uint8 executionMode; // ExecutionMode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM
4041
bytes actionData; // Encoded action data for arbitrary execution. Empty for DirectToCore mode.

0 commit comments

Comments
 (0)