Credits
The code implements a multi-adapter OFT, which allows an OFT to have multiple lock/unlock adapter pools.
Summary of operations
Disregarding fees, the effects of the possible operations are as follows:
Sending OFTs
Chain A:
decreases
dstEidcreditsincreases
localEidcreditsincreases
contractBalance
Chain B:
decreases
contractBalance
withdrawRemote
Chain A:
decreases
dstEidcredits
Chain B:
decreases
contractBalance
withdrawLocal
Chain A:
decreases
localEidcredits and decreasescontractBalance
depositLocal
Chain A:
increases
localEidcredits and increasescontractBalance
An invariant exists, on each chain:
contractBalance - localEid credits = sum(on all other chains, the credits allocated to that one chain)
Fees
In the following code, amountSent is the amount the user paid, and amountReceived is the amount after subtracting fees.
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {
// @dev Applies fees and determines the total amount to deduct from the sender, and the amount received
(uint256 amountSent, uint256 amountReceived) = _debitView(_sendParam.amountLD, _sendParam.minAmountLD);
// @dev Lock tokens by moving them into this contract from the caller.
innerToken.safeTransferFrom(msg.sender, address(this), amountSent);
// @dev Handle the credits
! _decreaseCredits(_sendParam.dstEid, amountReceived);
! _increaseCredits(LOCAL_EID, amountReceived);When a user sends an OFT, they are essentially telling the destination chain to transfer an amount to xyz.
It decreases dstEid credits, because those basically represent the question, "how much is my chain allowed to ask the destination chain to transfer?" It does not make sense to reduce this by the fee because the fee is paid for in the local token.
It increases localEid credits. Those basically represent the question, "how much of the local token on this chain are we allowed to transfer?" If the code also increased localEid credits by the fee, then it would be possible to withdrawLocal the fee too, for example.
The TON implementation has the same behavior regarding fees. Note that on TON, however, amountSent is actually the amount after subtracting fees — which is named amountReceived on EVM.
(int, int) _getAmountAndFee(cell $storage, int totalAmount) impure inline {
int feeBps = $storage.UsdtOFT::getFeeBps();
;; totalAmount is always at most 128-bits, feeBps is at most 10000 == 13 bits
;; so this multiplication can never overflow signed 257-bit
int fee = ((totalAmount * feeBps) / BPS_DENOMINATOR);
int amount = totalAmount - fee;
return (amount, fee);
}
tuple sendOFT(cell $OFTSend) impure inline {
(cell $storage, tuple actions) = preamble();
;; [...]
try {
(int amountSent, int fee) = _getAmountAndFee($storage, totalAmount);
} catch {
;; [...]
}
;; [...]
}