Skip to content

Commit ddfd4b5

Browse files
committed
add test cases
1 parent 7872e29 commit ddfd4b5

File tree

6 files changed

+233
-30
lines changed

6 files changed

+233
-30
lines changed

script/demoScripts/CallPolymerCCTP.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ set -e
77
GREEN='\033[0;32m'
88
BLUE='\033[0;34m'
99
RED='\033[0;31m'
10-
YELLOW='\033[1;33m'
1110
NC='\033[0m' # No Color
1211

1312
# Function to display usage

script/demoScripts/PolymerCCTP.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ contract CallPolymerCCTPFacet is Script {
4242
sendingAssetId: usdcAddress,
4343
receiver: receiver,
4444
minAmount: amount,
45-
destinationChainId: uint256(destinationDomain), // Using domain as chain ID for simplicity
45+
destinationChainId: uint256(destinationDomain), // Using domain as chain ID to avoid SLOAD to read from a mapping - this seems like it can just directly be passed via calldata.
4646
hasSourceSwaps: false,
4747
hasDestinationCall: false
4848
});

script/demoScripts/demoPolymer.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ if [ -z "$TO_CHAIN_ID" ]; then
3636
exit 1
3737
fi
3838

39-
echo "From Chain ID: $FROM_CHAIN_ID"
40-
echo "To Chain ID: $TO_CHAIN_ID"
39+
echo "From Chain ID: $FROM_CHAIN_ID" || { echo "Error: cast chain-id failed for TO_RPC_URL"; exit 1; }
40+
echo "To Chain ID: $TO_CHAIN_ID" || { echo "Error: cast chain-id failed for FROM_RPC_URL"; exit 1; }
4141
echo ""
4242

4343
# Extract USDC addresses from the addresses file using jq
@@ -56,7 +56,8 @@ if [ "$TO_TOKEN" = "null" ] || [ -z "$TO_TOKEN" ]; then
5656
fi
5757

5858
# Derive wallet address from private key
59-
USER_ADDRESS=$(cast wallet address "$PRIVATE_KEY")
59+
USER_ADDRESS=$(cast wallet address "$PRIVATE_KEY") || { echo "Error: Invalid private key or cast failed"; exit 1; }
60+
[ -z "$USER_ADDRESS" ] && { echo "Error: Failed to derive wallet address"; exit 1; }
6061
echo "Wallet Address: $USER_ADDRESS"
6162
echo ""
6263

src/Facets/PolymerCCTPFacet.sol

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,32 +49,24 @@ contract PolymerCCTPFacet is IPolymerCCTPFacet, ILiFi, ReentrancyGuard, SwapperV
4949
doesNotContainDestinationCalls(_bridgeData)
5050
{
5151
// TODO - is it worth validating the integrator and bridge from the bridgeData here?
52-
if(_bridgeData.minAmount == 0){
53-
revert InvalidBridgeAmount();
52+
if (_bridgeData.sendingAssetId != usdc) {
53+
revert InvalidSendingAsset(_bridgeData.sendingAssetId, usdc);
5454
}
55-
if (_bridgeData.receiver == address(0)){
56-
revert InvalidBridgeReceiver();
5755

58-
}
59-
if(_bridgeData.sendingAssetId != usdc){
60-
revert InvalidSendingAsset(_bridgeData.sendingAssetId , usdc);
61-
}
62-
63-
if(_polymerData.polymerTokenFee >= _bridgeData.minAmount){
56+
if (_polymerData.polymerTokenFee >= _bridgeData.minAmount) {
6457
revert FeeCannotBeLessThanAmount();
6558
}
6659

6760
// TODO: Do we need this check if it's always going to be usdc?
68-
LibAsset.depositAsset(_bridgeData.sendingAssetId, _bridgeData.minAmount );
69-
LibAsset.transferERC20( usdc, polymerFeeReceiver, _polymerData.polymerTokenFee );
70-
61+
LibAsset.depositAsset(_bridgeData.sendingAssetId, _bridgeData.minAmount);
62+
LibAsset.transferERC20(usdc, polymerFeeReceiver, _polymerData.polymerTokenFee);
7163

7264
// TODO we don't need to use safe approve here?
73-
IERC20(usdc).approve(address(tokenMessenger), _bridgeData.minAmount - _polymerData.polymerTokenFee );
65+
IERC20(usdc).approve(address(tokenMessenger), _bridgeData.minAmount - _polymerData.polymerTokenFee);
7466

7567
// Need tocheck: can we just use destinationChainID as the normal chain id? and can we just mpass in min Amount as the amountT?
7668
tokenMessenger.depositForBurn(
77-
_bridgeData.minAmount - _polymerData.polymerTokenFee ,
69+
_bridgeData.minAmount - _polymerData.polymerTokenFee,
7870
uint32(_bridgeData.destinationChainId),
7971
_bridgeData.receiver == NON_EVM_ADDRESS
8072
? _polymerData.nonEvmAddress
@@ -85,12 +77,11 @@ contract PolymerCCTPFacet is IPolymerCCTPFacet, ILiFi, ReentrancyGuard, SwapperV
8577
_polymerData.minFinalityThreshold // minFinalityThreshold - use default
8678
);
8779

88-
emit PolymerCCTPFeeSent( _bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
80+
emit PolymerCCTPFeeSent(_bridgeData.minAmount, _polymerData.polymerTokenFee, _polymerData.minFinalityThreshold);
8981

9082
// Emit Li.Fi standard event
91-
// TODO: Check - do we need to emit this event?
92-
emit LiFiTransferStarted(
93-
BridgeData(
83+
// TODO: Check - do we need to emit this event?
84+
emit LiFiTransferStarted(BridgeData(
9485
_bridgeData.transactionId,
9586
_bridgeData.bridge,
9687
_bridgeData.integrator,
@@ -101,7 +92,6 @@ contract PolymerCCTPFacet is IPolymerCCTPFacet, ILiFi, ReentrancyGuard, SwapperV
10192
_bridgeData.destinationChainId,
10293
_bridgeData.hasSourceSwaps,
10394
_bridgeData.hasDestinationCall
104-
)
105-
);
95+
));
10696
}
10797
}

src/Interfaces/IPolymerCCTP.sol

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ struct PolymerCCTPData {
1212

1313
interface IPolymerCCTPFacet {
1414
error InvalidAddress();
15-
error InvalidBridgeAmount();
16-
error InvalidBridgeReceiver();
17-
error InvalidSendingAsset( address actual, address expected);
15+
error InvalidSendingAsset(address actual, address expected);
1816
error FeeCannotBeLessThanAmount();
1917

20-
event PolymerCCTPFeeSent( uint256 bridgeAmount, uint256 polymerFee, uint32 minFinalityThreshold);
18+
event PolymerCCTPFeeSent(uint256 bridgeAmount, uint256 polymerFee, uint32 minFinalityThreshold);
2119

2220
function startBridgeTokensViaPolymerCCTP(ILiFi.BridgeData memory _bridgeData, PolymerCCTPData calldata _polymerData)
2321
external
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// SPDX-License-Identifier: Unlicense
2+
pragma solidity ^0.8.17;
3+
4+
import {Test} from "forge-std/Test.sol";
5+
import {PolymerCCTPFacet} from "lifi/Facets/PolymerCCTPFacet.sol";
6+
import {IPolymerCCTPFacet} from "lifi/Interfaces/IPolymerCCTP.sol";
7+
import {PolymerCCTPData} from "lifi/Interfaces/IPolymerCCTP.sol";
8+
import {ILiFi} from "lifi/Interfaces/ILiFi.sol";
9+
import {ERC20} from "solmate/tokens/ERC20.sol";
10+
import {InvalidReceiver, InvalidAmount, InvalidSendingToken} from "src/Errors/GenericErrors.sol";
11+
12+
// Mock TokenMessenger
13+
contract MockTokenMessenger {
14+
event DepositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken);
15+
16+
function depositForBurn(
17+
uint256 amount,
18+
uint32 destinationDomain,
19+
bytes32 mintRecipient,
20+
address burnToken,
21+
bytes32,
22+
uint256,
23+
uint32
24+
) external {
25+
// Transfer tokens from caller
26+
ERC20(burnToken).transferFrom(msg.sender, address(this), amount);
27+
emit DepositForBurn(amount, destinationDomain, mintRecipient, burnToken);
28+
}
29+
}
30+
31+
// Mock USDC
32+
contract MockUSDC is ERC20 {
33+
constructor() ERC20("USD Coin", "USDC", 6) {}
34+
35+
function mint(address to, uint256 amount) external {
36+
_mint(to, amount);
37+
}
38+
}
39+
40+
contract PolymerCCTPFacetTest is Test {
41+
PolymerCCTPFacet public facet;
42+
MockTokenMessenger public tokenMessenger;
43+
MockUSDC public usdc;
44+
45+
address public feeReceiver = address(0x123);
46+
address public user = address(0x456);
47+
address public receiver = address(0x789);
48+
49+
function setUp() public {
50+
// Deploy mocks
51+
usdc = new MockUSDC();
52+
tokenMessenger = new MockTokenMessenger();
53+
54+
// Deploy facet
55+
facet = new PolymerCCTPFacet(address(tokenMessenger), address(usdc), feeReceiver);
56+
57+
// Mint USDC to user
58+
usdc.mint(user, 1_000_000e6); // 1M USDC
59+
}
60+
61+
function test_CanBridgeUSDCViaPolymerCCTP() public {
62+
uint256 bridgeAmount = 100_000e6; // 100k USDC
63+
uint256 polymerFee = 100e6; // 100 USDC fee
64+
uint256 amountAfterFee = bridgeAmount - polymerFee;
65+
66+
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
67+
transactionId: bytes32(uint256(1)),
68+
bridge: "polymercctp",
69+
integrator: "test",
70+
referrer: address(0),
71+
sendingAssetId: address(usdc),
72+
receiver: receiver,
73+
minAmount: bridgeAmount,
74+
destinationChainId: 81457, // Blast
75+
hasSourceSwaps: false,
76+
hasDestinationCall: false
77+
});
78+
79+
PolymerCCTPData memory polymerData = PolymerCCTPData({
80+
polymerTokenFee: polymerFee, maxCCTPFee: 0, nonEvmAddress: bytes32(0), minFinalityThreshold: 0
81+
});
82+
83+
vm.startPrank(user);
84+
85+
// Approve facet to spend USDC
86+
usdc.approve(address(facet), bridgeAmount);
87+
88+
// Execute bridge
89+
facet.startBridgeTokensViaPolymerCCTP(bridgeData, polymerData);
90+
91+
vm.stopPrank();
92+
93+
// Verify fee receiver got the fee
94+
assertEq(usdc.balanceOf(feeReceiver), polymerFee);
95+
96+
// Verify token messenger got the bridge amount minus fee
97+
assertEq(usdc.balanceOf(address(tokenMessenger)), amountAfterFee);
98+
99+
// Verify user's balance decreased
100+
assertEq(usdc.balanceOf(user), 1_000_000e6 - bridgeAmount);
101+
}
102+
103+
function test_Revert_FeeGreaterThanAmount() public {
104+
uint256 bridgeAmount = 100e6;
105+
uint256 polymerFee = 100e6; // Fee equals amount
106+
107+
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
108+
transactionId: bytes32(uint256(1)),
109+
bridge: "polymercctp",
110+
integrator: "test",
111+
referrer: address(0),
112+
sendingAssetId: address(usdc),
113+
receiver: receiver,
114+
minAmount: bridgeAmount,
115+
destinationChainId: 81457,
116+
hasSourceSwaps: false,
117+
hasDestinationCall: false
118+
});
119+
120+
PolymerCCTPData memory polymerData = PolymerCCTPData({
121+
polymerTokenFee: polymerFee, maxCCTPFee: 0, nonEvmAddress: bytes32(0), minFinalityThreshold: 0
122+
});
123+
124+
vm.startPrank(user);
125+
usdc.approve(address(facet), bridgeAmount);
126+
127+
vm.expectRevert(IPolymerCCTPFacet.FeeCannotBeLessThanAmount.selector);
128+
facet.startBridgeTokensViaPolymerCCTP(bridgeData, polymerData);
129+
130+
vm.stopPrank();
131+
}
132+
133+
function test_Revert_ZeroAmount() public {
134+
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
135+
transactionId: bytes32(uint256(1)),
136+
bridge: "polymercctp",
137+
integrator: "test",
138+
referrer: address(0),
139+
sendingAssetId: address(usdc),
140+
receiver: receiver,
141+
minAmount: 0,
142+
destinationChainId: 81457,
143+
hasSourceSwaps: false,
144+
hasDestinationCall: false
145+
});
146+
147+
PolymerCCTPData memory polymerData = PolymerCCTPData({
148+
polymerTokenFee: 100e6, maxCCTPFee: 0, nonEvmAddress: bytes32(0), minFinalityThreshold: 0
149+
});
150+
151+
vm.startPrank(user);
152+
153+
vm.expectRevert(InvalidAmount.selector);
154+
facet.startBridgeTokensViaPolymerCCTP(bridgeData, polymerData);
155+
156+
vm.stopPrank();
157+
}
158+
159+
function test_Revert_ZeroReceiver() public {
160+
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
161+
transactionId: bytes32(uint256(1)),
162+
bridge: "polymercctp",
163+
integrator: "test",
164+
referrer: address(0),
165+
sendingAssetId: address(usdc),
166+
receiver: address(0),
167+
minAmount: 100_000e6,
168+
destinationChainId: 81457,
169+
hasSourceSwaps: false,
170+
hasDestinationCall: false
171+
});
172+
173+
PolymerCCTPData memory polymerData = PolymerCCTPData({
174+
polymerTokenFee: 100e6, maxCCTPFee: 0, nonEvmAddress: bytes32(0), minFinalityThreshold: 0
175+
});
176+
177+
vm.startPrank(user);
178+
usdc.approve(address(facet), bridgeData.minAmount);
179+
180+
vm.expectRevert(InvalidReceiver.selector);
181+
facet.startBridgeTokensViaPolymerCCTP(bridgeData, polymerData);
182+
183+
vm.stopPrank();
184+
}
185+
186+
function test_Revert_InvalidSendingAsset() public {
187+
MockUSDC wrongToken = new MockUSDC();
188+
wrongToken.mint(user, 1_000_000e6);
189+
190+
ILiFi.BridgeData memory bridgeData = ILiFi.BridgeData({
191+
transactionId: bytes32(uint256(1)),
192+
bridge: "polymercctp",
193+
integrator: "test",
194+
referrer: address(0),
195+
sendingAssetId: address(wrongToken),
196+
receiver: receiver,
197+
minAmount: 100_000e6,
198+
destinationChainId: 81457,
199+
hasSourceSwaps: false,
200+
hasDestinationCall: false
201+
});
202+
203+
PolymerCCTPData memory polymerData = PolymerCCTPData({
204+
polymerTokenFee: 100e6, maxCCTPFee: 0, nonEvmAddress: bytes32(0), minFinalityThreshold: 0
205+
});
206+
207+
vm.startPrank(user);
208+
wrongToken.approve(address(facet), bridgeData.minAmount);
209+
210+
vm.expectRevert(InvalidSendingToken.selector);
211+
facet.startBridgeTokensViaPolymerCCTP(bridgeData, polymerData);
212+
213+
vm.stopPrank();
214+
}
215+
}

0 commit comments

Comments
 (0)