Skip to content

Commit

Permalink
base transfer & refund
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoch05 committed Dec 20, 2023
1 parent 4af6387 commit 0c458d3
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 17 deletions.
12 changes: 4 additions & 8 deletions helix-contract/contracts/mapping-token/v3/base/xTokenBacking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ contract xTokenBacking is xTokenBridgeBase {

// We use nonce to ensure that messages are not duplicated
// especially in reorg scenarios, the destination chain use nonce to filter out duplicate deliveries.
// nonce is user-defined, there is no requirement that it must not be repeated.
// But the transferId generated must not be repeated.
function lockAndRemoteIssuing(
uint256 _remoteChainId,
address _originalToken,
Expand Down Expand Up @@ -124,8 +126,7 @@ contract xTokenBacking is xTokenBridgeBase {
expendDailyLimit(_originalToken, _amount);

bytes32 transferId = getTransferId(_nonce, block.chainid, _originalToken, _originSender, _recipient, _amount);
require(filledTransfers[transferId] == TRANSFER_UNFILLED, "message has been accepted");
filledTransfers[transferId] = TRANSFER_DELIVERED;
_handleTransfer(transferId);

// native token do not use guard
if (address(0) == _originalToken) {
Expand Down Expand Up @@ -182,12 +183,7 @@ contract xTokenBacking is xTokenBridgeBase {
) external payable {
require(_originalSender == msg.sender || _recipient == msg.sender || dao == msg.sender, "invalid msgSender");
bytes32 transferId = getTransferId(_nonce, _remoteChainId, _originalToken, _originalSender, _recipient, _amount);
// must not exist in successful issue list
uint256 filledTransfer = filledTransfers[transferId];
require(filledTransfer != TRANSFER_DELIVERED, "success message can't refund for failed");
if (filledTransfer != TRANSFER_REFUNDED) {
filledTransfers[transferId] = TRANSFER_REFUNDED;
}
_requestRefund(transferId);
bytes memory unlockForFailed = encodeIssuingForUnlockFailureFromRemote(
_originalToken,
_originalSender,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,46 @@ contract xTokenBridgeBase is Initializable, Pausable, AccessController, DailyLim
messageId = IMessageId(service.sendService).latestSentMessageId();
}

// request a cross-chain transfer
// 1. lock and remote issue
// 2. burn and remote unlock
// save the transferId if not exist, else revert
function _requestTransfer(bytes32 _transferId) internal {
require(requestInfos[_transferId].isRequested == false, "request exist");
requestInfos[_transferId].isRequested = true;
}

// receive a cross-chain refund request
// 1. request must be exist
// 2. can't repeat
function _handleRefund(bytes32 _transferId) internal {
RequestInfo memory requestInfo = requestInfos[_transferId];
require(requestInfo.isRequested == true, "request not exist");
require(requestInfo.hasRefundForFailed == false, "request has been refund");
requestInfos[_transferId].hasRefundForFailed = true;
}

// receive a cross-chain request
// must not filled
// fill the transfer with delivered transfer type
function _handleTransfer(bytes32 _transferId) internal {
require(filledTransfers[_transferId] == TRANSFER_UNFILLED, "!conflict");
filledTransfers[_transferId] = TRANSFER_DELIVERED;
}

// request a cross-chain refund
// 1. can retry
// 2. can't be filled by delivery
function _requestRefund(bytes32 _transferId) internal {
uint256 filledTransfer = filledTransfers[_transferId];
// already fill by refund, retry request
if (filledTransfer == TRANSFER_REFUNDED) {
return;
}
require(filledTransfer == TRANSFER_UNFILLED, "!conflict");
filledTransfers[_transferId] = TRANSFER_REFUNDED;
}

function getTransferId(
uint256 _nonce,
uint256 _targetChainId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ contract xTokenIssuing is xTokenBridgeBase {
require(_amount > 0, "can not receive amount zero");
expendDailyLimit(xToken, _amount);

require(filledTransfers[transferId] == TRANSFER_UNFILLED, "message has been accepted");
filledTransfers[transferId] = TRANSFER_DELIVERED;
_handleTransfer(transferId);

address _guard = guard;
if (_guard != address(0)) {
Expand Down Expand Up @@ -182,11 +181,7 @@ contract xTokenIssuing is xTokenBridgeBase {
) external payable {
require(_originalSender == msg.sender || _recipient == msg.sender || dao == msg.sender, "invalid msgSender");
bytes32 transferId = getTransferId(_nonce, _originalChainId, _originalToken, _originalSender, _recipient, _amount);
uint256 filledTransfer = filledTransfers[transferId];
require(filledTransfer != TRANSFER_DELIVERED, "success message can't refund for failed");
if (filledTransfer != TRANSFER_REFUNDED) {
filledTransfers[transferId] = TRANSFER_REFUNDED;
}
_requestRefund(transferId);
bytes memory handleUnlockForFailed = encodeUnlockForIssuingFailureFromRemote(
_originalToken,
_originalSender,
Expand Down
4 changes: 2 additions & 2 deletions helix-contract/test/6_test_xtoken_v3.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,14 @@ describe("xtoken tests", () => {
nonce01,
"1.1",
true
)).to.be.revertedWith("success message can't refund for failed");
)).to.be.revertedWith("!conflict");
await expect(requestRemoteIssuingForUnlockFailure(
nativeTokenAddress,
100,
nonce02,
"1.1",
true
)).to.be.revertedWith("success message can't refund for failed");
)).to.be.revertedWith("!conflict");

// lock exceed daily limit
const nonce03 = await lockAndRemoteIssuing(
Expand Down

0 comments on commit 0c458d3

Please sign in to comment.