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
dstEid
creditsincreases
localEid
creditsincreases
contractBalance
Chain B:
decreases
contractBalance
withdrawRemote
Chain A:
decreases
dstEid
credits
Chain B:
decreases
contractBalance
withdrawLocal
Chain A:
decreases
localEid
credits and decreasescontractBalance
depositLocal
Chain A:
increases
localEid
credits 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 {
;; [...]
}
;; [...]
}