Skip to content

Commit 7037bad

Browse files
feat: add polymer contracts for ERC-7683
1 parent 0f1863d commit 7037bad

File tree

6 files changed

+3052
-687
lines changed

6 files changed

+3052
-687
lines changed

.gitmodules

Whitespace-only changes.

solidity/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"dependencies": {
99
"@hyperlane-xyz/core": "^5.4.1",
1010
"@openzeppelin/contracts": "^4.9.6",
11+
"@polymerdao/prover-contracts": "^0.0.17",
1112
"@uniswap/permit2": "github:Uniswap/permit2",
1213
"dotenv-run-script": "^0.4.1"
1314
},

solidity/remappings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ forge-std/=node_modules/forge-std/src/
33
@hyperlane-xyz/=node_modules/@hyperlane-xyz/core/contracts/
44
contracts/=node_modules/@hyperlane-xyz/core/contracts/
55
@uniswap/=node_modules/@uniswap/
6+
@polymerdao/prover-contracts=node_modules/@polymerdao/prover-contracts/contracts/
7+

solidity/src/Polymer7683.sol

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.25;
3+
4+
import { ICrossL2ProverV2 } from "@polymerdao/prover-contracts/interfaces/ICrossL2ProverV2.sol";
5+
import { OrderData, OrderEncoder } from "./libs/OrderEncoder.sol";
6+
import { BasicSwap7683 } from "./BasicSwap7683.sol";
7+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
8+
import { TypeCasts } from "@hyperlane-xyz/libs/TypeCasts.sol";
9+
10+
/**
11+
* @title Polymer7683
12+
* @author PolymerLabs
13+
* @notice This contract builds on top of BasicSwap7683 as a messaging layer using Polymer.
14+
* @dev It integrates with the Polymer protocol for cross-chain event verification.
15+
*/
16+
contract Polymer7683 is BasicSwap7683, Ownable {
17+
// ============ Constants ============
18+
string public constant CLIENT_TYPE = "polymer"; // Used for proof verification
19+
20+
// ============ Public Storage ============
21+
ICrossL2ProverV2 public immutable prover;
22+
uint256 public immutable localChainId;
23+
mapping(uint256 => address) public destinationContracts;
24+
25+
26+
// ============ Events ============
27+
/**
28+
* @notice Event emitted when a destination contract is updated
29+
* @param chainId The chain ID for the destination
30+
* @param contractAddress The new contract address
31+
*/
32+
event DestinationContractUpdated(uint256 indexed chainId, address contractAddress);
33+
34+
// ============ Errors ============
35+
error InvalidProof();
36+
error InvalidChainId();
37+
error InvalidEmitter();
38+
error InvalidEventData();
39+
error InvalidDestinationContract();
40+
error UnregisteredDestinationChain();
41+
error SettlementFailed();
42+
43+
// ============ Constructor ============
44+
/**
45+
* @notice Initializes the Polymer7683 contract with the specified Prover and PERMIT2 address.
46+
* @param _prover The address of the Polymer CrossL2Prover contract
47+
* @param _permit2 The address of the permit2 contract
48+
* @param _localChainId The chain ID of the chain this contract is deployed on
49+
*/
50+
constructor(
51+
ICrossL2ProverV2 _prover,
52+
address _permit2,
53+
uint256 _localChainId
54+
) BasicSwap7683(_permit2) {
55+
prover = _prover;
56+
localChainId = _localChainId;
57+
}
58+
59+
// ============ Admin Functions ============
60+
function setDestinationContract(uint256 chainId, address contractAddress) external onlyOwner {
61+
if (contractAddress == address(0)) revert InvalidDestinationContract();
62+
destinationContracts[chainId] = contractAddress;
63+
emit DestinationContractUpdated(chainId, contractAddress);
64+
}
65+
66+
// ============ External Functions ============
67+
/**
68+
* @notice Process a settlement proof from a destination chain
69+
* @param eventProof The proof of the Fill event from the destination chain
70+
* @param eventProof The proof of the Fill event from the destination chain
71+
*/
72+
function handleSettlementWithProof(bytes calldata eventProof) external {
73+
// Verify event using Polymer prover
74+
(
75+
uint32 provenChainId,
76+
address emitter,
77+
, // topics
78+
bytes memory data
79+
) = prover.validateEvent(eventProof);
80+
81+
// Verify destination contract is registered for the proven chain
82+
address expectedEmitter = destinationContracts[provenChainId];
83+
if (expectedEmitter == address(0)) revert UnregisteredDestinationChain();
84+
85+
// Validate emitter matches registered destination
86+
if (emitter != expectedEmitter) revert InvalidEmitter();
87+
88+
// Decode data from the Filled event format
89+
(bytes32 eventOrderId, bytes memory originData, bytes memory fillerData) = abi.decode(data, (bytes32, bytes, bytes));
90+
91+
// Process settlement with the message origin, sender, order ID, and receiver
92+
_handleSettleOrder(
93+
provenChainId,
94+
TypeCasts.addressToBytes32(emitter),
95+
eventOrderId,
96+
abi.decode(fillerData, (bytes32))
97+
);
98+
99+
// _handleSettleOrder checks eligibility with
100+
// _checkOrderEligibility and simply returns early without
101+
// reverting if the order isn't eligible. we need to check if
102+
// the order was settled successfully and revert if not.
103+
if (orderStatus[eventOrderId] != SETTLED) revert SettlementFailed();
104+
}
105+
106+
/**
107+
* @notice Process a refund proof from a destination chain
108+
* @param orderId The order ID being refunded
109+
* @param eventProof The proof of the Refund event from the destination chain
110+
*/
111+
function handleRefundWithProof(bytes32 orderId, bytes calldata eventProof) external {
112+
// Verify event using Polymer prover
113+
(
114+
uint32 provenChainId,
115+
address emitter,
116+
, // topics
117+
bytes memory data
118+
) = prover.validateEvent(eventProof);
119+
120+
// Verify destination contract is registered for the proven chain
121+
address expectedEmitter = destinationContracts[provenChainId];
122+
if (expectedEmitter == address(0)) revert UnregisteredDestinationChain();
123+
124+
// Validate emitter matches registered destination
125+
if (emitter != expectedEmitter) revert InvalidEmitter();
126+
127+
// Decode refund-specific data and validate order ID
128+
bytes32[] memory eventOrderIds = abi.decode(data, (bytes32[]));
129+
bool found = false;
130+
for (uint256 i = 0; i < eventOrderIds.length; i++) {
131+
if (eventOrderIds[i] == orderId) {
132+
found = true;
133+
break;
134+
}
135+
}
136+
if (!found) revert InvalidEventData();
137+
138+
// Process refund with message origin, sender, and order ID
139+
_handleRefundOrder(
140+
provenChainId,
141+
TypeCasts.addressToBytes32(emitter),
142+
orderId
143+
);
144+
145+
// Check if order was successfully refunded
146+
if (orderStatus[orderId] != REFUNDED) revert SettlementFailed();
147+
}
148+
149+
// ============ Internal Functions ============
150+
151+
/**
152+
* @notice Dispatches a settlement instruction by emitting a Filled event that will be proven on the origin chain
153+
* @param _originDomain The domain to which the settlement message is sent
154+
* @param _orderIds The IDs of the orders to settle
155+
* @param _ordersFillerData The filler data for the orders
156+
*/
157+
function _dispatchSettle(
158+
uint32 _originDomain,
159+
bytes32[] memory _orderIds,
160+
bytes[] memory _ordersFillerData
161+
) internal override {
162+
// No-op as the Filled event is already emitted in Base7683's fill method
163+
}
164+
165+
/**
166+
* @notice Dispatches a refund instruction by emitting a Refund event that will be proven on the origin chain
167+
* @param _originDomain The domain to which the refund message is sent
168+
* @param _orderIds The IDs of the orders to refund
169+
*/
170+
function _dispatchRefund(
171+
uint32 _originDomain,
172+
bytes32[] memory _orderIds
173+
) internal override {
174+
// No-op as Refund event is already emitted in Base7683's refund method
175+
}
176+
177+
/**
178+
* @notice Retrieves the local domain identifier
179+
* @return The local domain ID (chain ID)
180+
*/
181+
function _localDomain() internal view override returns (uint32) {
182+
return uint32(localChainId);
183+
}
184+
185+
}

0 commit comments

Comments
 (0)