Skip to content

Commit

Permalink
remove completeTokenPurchase
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaryash90 committed Sep 4, 2024
1 parent 5071642 commit 1c5644e
Show file tree
Hide file tree
Showing 4 changed files with 3 additions and 136 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ This is a forwarder contract that forwards the swap providers transaction (LiFi,
- Data Logging - this is essential for attribution and linking on-chain and off-chain data
- Fee Splitting - this allows us to split the fees in-flight and flexibility to change fees on a per client basis
- Data validation - this provides high-security as only thirdweb originated swaps with untampered data can use this contract
- exit point for contract calls - for LiFi, they can only guarantee toAmount for contract calls. This allows use to add a contract call to transferEnd that forwards the end funds to the user
- Stateless - this will be deployed on many different chains. We don’t want to have to call addClient, changeFee, addSwapProvider, etc on every single chain for every change. Therefore, this should not rely on data held in the state of the contract, but rather data passed in

[PayGateway Reference](img/gateway.png)

[PayGateway With Transfer End](img/gateway-transfer-end.png)

## Features

- Event Logging
- TokenPurchaseInitiated logs the necessary events attribution and link off-chain and on-chain through clientId and transactionId. We use bytes32 instead of string for clientId and transactionId (uuid in database) because this allows recovering indexed pre-image
- TokenPurchaseCompleted logs the transfer end in case of a contract call and can be used for indexing bridge transactions by just listening to our Thirdweb PayGateway deployments
- FeePayout logs the fees distributed among the payees
- Fee Splitting
- supports many parties for fee payouts (we only expect us and client). It also allows for flexible fees on a per client basis
Expand Down
Binary file removed img/gateway-transfer-end.png
Binary file not shown.
60 changes: 3 additions & 57 deletions src/PayGatewayModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,6 @@ contract PayGatewayModule is EIP712, ModularModule, ReentrancyGuard {
uint256 tokenAmount
);

event TokenPurchaseCompleted(
bytes32 indexed clientId,
address indexed receiver,
bytes32 transactionId,
address tokenAddress,
uint256 tokenAmount
);

event FeePayout(
bytes32 indexed clientId,
address indexed sender,
Expand All @@ -128,7 +120,7 @@ contract PayGatewayModule is EIP712, ModularModule, ReentrancyGuard {

/// @notice Returns all implemented callback and fallback functions.
function getModuleConfig() external pure override returns (ModuleConfig memory config) {
config.fallbackFunctions = new FallbackFunction[](6);
config.fallbackFunctions = new FallbackFunction[](5);

config.fallbackFunctions[0] = FallbackFunction({
selector: this.withdrawTo.selector,
Expand All @@ -142,12 +134,8 @@ contract PayGatewayModule is EIP712, ModularModule, ReentrancyGuard {
selector: this.initiateTokenPurchase.selector,
permissionBits: 0
});
config.fallbackFunctions[3] = FallbackFunction({
selector: this.completeTokenPurchase.selector,
permissionBits: 0
});
config.fallbackFunctions[4] = FallbackFunction({ selector: this.eip712Domain.selector, permissionBits: 0 });
config.fallbackFunctions[5] = FallbackFunction({ selector: this.isProcessed.selector, permissionBits: 0 });
config.fallbackFunctions[3] = FallbackFunction({ selector: this.eip712Domain.selector, permissionBits: 0 });
config.fallbackFunctions[4] = FallbackFunction({ selector: this.isProcessed.selector, permissionBits: 0 });
}

/*///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -264,48 +252,6 @@ contract PayGatewayModule is EIP712, ModularModule, ReentrancyGuard {
emit TokenPurchaseInitiated(req.clientId, msg.sender, req.transactionId, req.tokenAddress, req.tokenAmount);
}

/**
@notice
The purpose of completeTokenPurchase is to provide a forwarding contract call
on the destination chain. For some swap providers, they can only guarantee the toAmount
if we use a contract call. This allows us to call the endTransfer function and forward the
funds to the end user.
Requirements:
1. Log the transfer end
2. forward the user funds
*/
function completeTokenPurchase(
bytes32 clientId,
bytes32 transactionId,
address tokenAddress,
uint256 tokenAmount,
address payable receiverAddress
) external payable nonReentrant {
if (tokenAmount == 0) {
revert PayGatewayInvalidAmount(tokenAmount);
}

if (_isTokenNative(tokenAddress)) {
if (msg.value != tokenAmount) {
revert PayGatewayMismatchedValue(tokenAmount, msg.value);
}
}

// pull user funds
if (_isTokenERC20(tokenAddress)) {
if (msg.value != 0) {
revert PayGatewayMsgValueNotZero();
}

SafeTransferLib.safeTransferFrom(tokenAddress, msg.sender, receiverAddress, tokenAmount);
} else {
SafeTransferLib.safeTransferETH(receiverAddress, tokenAmount);
}

emit TokenPurchaseCompleted(clientId, receiverAddress, transactionId, tokenAddress, tokenAmount);
}

/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
Expand Down
75 changes: 0 additions & 75 deletions test/PayGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ contract PayGatewayTest is Test {
uint256 tokenAmount
);

event TokenPurchaseCompleted(
bytes32 indexed clientId,
address indexed receiver,
bytes32 transactionId,
address tokenAddress,
uint256 tokenAmount
);

event FeePayout(
bytes32 indexed clientId,
address indexed sender,
Expand Down Expand Up @@ -467,71 +459,4 @@ contract PayGatewayTest is Test {
);
gateway.initiateTokenPurchase(req, _signature);
}

// /*///////////////////////////////////////////////////////////////
// Test `completeTokenPurchase`
// //////////////////////////////////////////////////////////////*/

function test_completeTokenPurchase_erc20() public {
uint256 sendValue = 1 ether;

// approve amount to gateway contract
vm.prank(sender);
mockERC20.approve(address(gateway), sendValue);

// state/balances before sending transaction
uint256 ownerBalanceBefore = mockERC20.balanceOf(owner);
uint256 senderBalanceBefore = mockERC20.balanceOf(sender);
uint256 receiverBalanceBefore = mockERC20.balanceOf(receiver);

// send transaction
bytes32 _transactionId = keccak256("transaction ID");
vm.prank(sender);
gateway.completeTokenPurchase(clientId, _transactionId, address(mockERC20), sendValue, receiver);

// check balances after transaction
assertEq(mockERC20.balanceOf(owner), ownerBalanceBefore);
assertEq(mockERC20.balanceOf(sender), senderBalanceBefore - sendValue);
assertEq(mockERC20.balanceOf(receiver), receiverBalanceBefore + sendValue);
}

function test_completeTokenPurchase_nativeToken() public {
uint256 sendValue = 1 ether;

// state/balances before sending transaction
uint256 ownerBalanceBefore = owner.balance;
uint256 senderBalanceBefore = sender.balance;
uint256 receiverBalanceBefore = receiver.balance;

// send transaction
bytes32 _transactionId = keccak256("transaction ID");
vm.prank(sender);
gateway.completeTokenPurchase{ value: sendValue }(
clientId,
_transactionId,
address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE),
sendValue,
receiver
);

// check balances after transaction
assertEq(owner.balance, ownerBalanceBefore);
assertEq(sender.balance, senderBalanceBefore - sendValue);
assertEq(receiver.balance, receiverBalanceBefore + sendValue);
}

function test_completeTokenPurchase_events() public {
uint256 sendValue = 1 ether;

// approve amount to gateway contract
vm.prank(sender);
mockERC20.approve(address(gateway), sendValue);

// send transaction
bytes32 _transactionId = keccak256("transaction ID");
vm.prank(sender);
vm.expectEmit(true, true, false, true);
emit TokenPurchaseCompleted(clientId, receiver, _transactionId, address(mockERC20), sendValue);
gateway.completeTokenPurchase(clientId, _transactionId, address(mockERC20), sendValue, receiver);
}
}

0 comments on commit 1c5644e

Please sign in to comment.