Skip to content

Commit

Permalink
add swapAndRequestCCTPWithSolanaSwap
Browse files Browse the repository at this point in the history
  • Loading branch information
thal0x committed Jul 4, 2024
1 parent 7efd29f commit 78d05b3
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
96 changes: 96 additions & 0 deletions CCTPRelayer/src/CCTPRelayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,102 @@ contract CCTPRelayer is ICCTPRelayer, Initializable, UUPSUpgradeable, Ownable2St
emit PaymentForRelay(nonce, feeAmount);
}

function swapAndRequestCCTPWithSolanaSwap(
address inputToken,
uint256 inputAmount,
bytes memory swapCalldata,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
uint256 feeAmount,
bytes32 destinationCaller,
bytes memory solanaSwapPayload
) external payable nonReentrant {
if (inputAmount == 0) revert PaymentCannotBeZero();
if (feeAmount == 0) revert PaymentCannotBeZero();

uint256 outputAmount;
if (inputToken == address(0)) {
// Native Token
if (inputAmount != msg.value) revert InsufficientNativeToken();

// Get the contract's balances previous to the swap
uint256 preInputBalance = address(this).balance - inputAmount;
uint256 preOutputBalance = usdc.balanceOf(address(this));

// Call the swap router and perform the swap
(bool success,) = swapRouter.call{value: inputAmount}(swapCalldata);
if (!success) revert SwapFailed();

// Get the contract's balances after the swap
uint256 postInputBalance = address(this).balance;
uint256 postOutputBalance = usdc.balanceOf(address(this));

// Check that the contract's native token balance has increased
if (preOutputBalance >= postOutputBalance) revert InsufficientSwapOutput();
outputAmount = postOutputBalance - preOutputBalance;

// Refund the remaining ETH
uint256 dust = postInputBalance - preInputBalance;
if (dust != 0) {
(bool ethSuccess,) = msg.sender.call{value: dust}("");
if (!ethSuccess) revert ETHSendFailed();
}
} else {
IERC20 token = IERC20(inputToken);

// Get the contract's balances previous to the swap
uint256 preInputBalance = token.balanceOf(address(this));
uint256 preOutputBalance = usdc.balanceOf(address(this));

// Transfer input ERC20 tokens to the contract
token.transferFrom(msg.sender, address(this), inputAmount);

// Approve the swap router to spend the input tokens
token.approve(swapRouter, inputAmount);

// Call the swap router and perform the swap
(bool success,) = swapRouter.call(swapCalldata);
if (!success) revert SwapFailed();

// Get the contract's balances after the swap
uint256 postInputBalance = token.balanceOf(address(this));
uint256 postOutputBalance = usdc.balanceOf(address(this));

// Check that the contract's output token balance has increased
if (preOutputBalance >= postOutputBalance) revert InsufficientSwapOutput();
outputAmount = postOutputBalance - preOutputBalance;

// Refund the remaining amount
uint256 dust = postInputBalance - preInputBalance;
if (dust != 0) {
token.transfer(msg.sender, dust);

// Revoke Approval
token.approve(swapRouter, 0);
}
}

// Check that output amount is enough to cover the fee
if (outputAmount <= feeAmount) revert InsufficientSwapOutput();
uint256 transferAmount = outputAmount - feeAmount;

// Only give allowance of the transfer amount, as we want the fee amount to stay in the contract.
usdc.approve(address(messenger), transferAmount);

// Call deposit for burn and save the nonce.
uint64 nonce = messenger.depositForBurnWithCaller(
transferAmount, destinationDomain, mintRecipient, burnToken, destinationCaller
);

// As user already paid for the fee we emit the payment event.
emit PaymentForRelay(nonce, feeAmount);

transmitter.sendMessageWithCaller(
destinationDomain, mintRecipient, destinationCaller, abi.encodePacked(nonce, solanaSwapPayload)
);
}

function batchReceiveMessage(ICCTPRelayer.ReceiveCall[] memory receiveCalls) external {
// Save gas by not retrieving the length on each loop.
uint256 length = receiveCalls.length;
Expand Down
78 changes: 78 additions & 0 deletions CCTPRelayer/test/CCTPRelayer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,20 @@ contract CCTPRelayerTest is Test {
_swapAndRequestCCTPTransferWithCaller(domain, address(wETH), 1 ether, swapCalldata);
}

function test_swapAndRequestCCTPWithSolanaSwap_Token() public {
uint32 domain = 7;
_switchFork(forks[0]);

uint256 minAmount = 2_000 * 1e6;
address[] memory path = new address[](2);
path[0] = address(wETH);
path[1] = address(usdc);
bytes memory swapCalldata =
abi.encodeWithSelector(bytes4(0x472b43f3), 1 ether, minAmount, path, address(relayer));

_swapAndRequestCCTPWithSolanaSwap(domain, address(wETH), 1 ether, swapCalldata);
}

function _swapAndRequestCCTPTransfer(
uint32 domain,
address inputToken,
Expand Down Expand Up @@ -477,6 +491,70 @@ contract CCTPRelayerTest is Test {
assertEq(usdc.balanceOf(ACTOR_1), 0, "Balance Remaining After Payment");
}

function _swapAndRequestCCTPWithSolanaSwap(
uint32 domain,
address inputToken,
uint256 inputAmount,
bytes memory swapCalldata
) internal {
uint256 feeAmount = 10 * 1e6;

bytes32 mintRecipent = bytes32(abi.encodePacked(ACTOR_1));

if (inputToken == address(0)) {
vm.deal(ACTOR_1, inputAmount);
uint256 preSwapUserBalance = ACTOR_1.balance;
uint256 preSwapContractBalance = address(relayer).balance;

vm.startPrank(ACTOR_1);
relayer.swapAndRequestCCTPWithSolanaSwap{value: inputAmount}(
inputToken,
inputAmount,
swapCalldata,
domain,
mintRecipent,
address(usdc),
feeAmount,
keccak256(abi.encodePacked("random caller")),
abi.encodePacked("solana calldata")
);
vm.stopPrank();

assertTrue(ACTOR_1.balance < preSwapUserBalance, "User balance increased");
assertEq(address(relayer).balance, preSwapContractBalance, "Funds leftover in contract");
} else {
deal(inputToken, ACTOR_1, inputAmount);

uint256 preSwapUserBalance = IERC20(inputToken).balanceOf(ACTOR_1);
uint256 preSwapContractBalance = IERC20(inputToken).balanceOf(address(relayer));

vm.startPrank(ACTOR_1);
IERC20(inputToken).approve(address(relayer), inputAmount);
relayer.swapAndRequestCCTPWithSolanaSwap(
inputToken,
inputAmount,
swapCalldata,
domain,
mintRecipent,
address(usdc),
feeAmount,
keccak256(abi.encodePacked("random caller")),
abi.encodePacked("solana calldata")
);
vm.stopPrank();

assertEq(IERC20(inputToken).allowance(address(relayer), relayer.swapRouter()), 0, "Left-over allowance");
assertTrue(IERC20(inputToken).balanceOf(ACTOR_1) < preSwapUserBalance, "User balance increased");
assertEq(
IERC20(inputToken).balanceOf(address(relayer)), preSwapContractBalance, "Funds leftover in contract"
);
}

assertEq(usdc.allowance(address(relayer), address(messenger)), 0, "Messenger Allowance Remaining After Payment");
assertEq(usdc.allowance(ACTOR_1, address(relayer)), 0, "Relayer Allowance Remaining After Payment");
assertEq(usdc.balanceOf(ACTOR_1), 0, "Balance Remaining After Payment");
}

fallback() external payable {}

receive() external payable {}
Expand Down

0 comments on commit 78d05b3

Please sign in to comment.