-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FE Release 2024-12-13 #3462
FE Release 2024-12-13 #3462
Changes from all commits
3d3530d
e2dc688
cb43466
9900167
ba4eb3c
c341b69
0847d81
e1ef6ae
f09d28b
8ce56bb
f465b47
7e73687
fb55d90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,25 @@ | |
All notable changes to this project will be documented in this file. | ||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
|
||
# [0.6.0](https://github.com/synapsecns/sanguine/compare/@synapsecns/[email protected]...@synapsecns/[email protected]) (2024-12-13) | ||
|
||
|
||
### Features | ||
|
||
* **synapse-interface:** Adds Hyperliquid bridge & deposit support ([#3461](https://github.com/synapsecns/sanguine/issues/3461)) ([7e73687](https://github.com/synapsecns/sanguine/commit/7e73687f707191d138c3f9f65d9428e03454194b)) | ||
|
||
|
||
|
||
|
||
|
||
## [0.5.14](https://github.com/synapsecns/sanguine/compare/@synapsecns/[email protected]...@synapsecns/[email protected]) (2024-12-13) | ||
|
||
**Note:** Version bump only for package @synapsecns/bridge-docs | ||
|
||
|
||
|
||
|
||
|
||
## [0.5.13](https://github.com/synapsecns/sanguine/compare/@synapsecns/[email protected]...@synapsecns/[email protected]) (2024-12-08) | ||
|
||
**Note:** Version bump only for package @synapsecns/bridge-docs | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
--- | ||
slug: synapse-intent-network-launch | ||
title: The Synapse Intent Network | ||
# authors: [synapse] | ||
tags: [update, fastbridgev2, intent-network] | ||
--- | ||
|
||
import { RFQFlow } from '@site/src/components/RFQFlow' | ||
|
||
We are excited to announce the **Synapse Intent Network**. | ||
|
||
<!--truncate--> | ||
|
||
# Summary | ||
|
||
The Synapse Intent Network is a cross-chain communication protocol that enables seamless asset transfers and message passing between different blockchain networks. At its core, it provides a robust infrastructure for executing complex cross-chain operations while maintaining security and efficiency. | ||
|
||
<figure> | ||
<RFQFlow /> | ||
<figcaption>RFQ flow: get Quote, get txData, sign transaction</figcaption> | ||
</figure> | ||
|
||
<br/> | ||
|
||
This major protocol upgrade represents a fundamental shift in the architecture, introducing intent-based routing, advanced bridging capabilities, and significant gas optimizations. The changes reflect a deeper focus on user experience while substantially improving the protocol's security, efficiency, and scalability. | ||
## Key Improvements At-A-Glance | ||
|
||
* **Gas Optimization Revolution** | ||
* Achieved 30-50% reduction in transaction costs through storage and execution improvements | ||
* Built-In Multicall allows efficient batching to save 21,000+ gas per operation | ||
|
||
* **Powerful Intent-Based Routing** | ||
* New "Zap" architecture enables complex actions to be atomically executed after the intent is fulfilled | ||
* Reduces cross-contract calls | ||
* Allows for sophisticated bridging scenarios | ||
|
||
* **Exclusive Relayer Functionality** | ||
* Intents can be assigned for exclusive fulfillment only by the Relayer who provided the best quote. | ||
* This eliminates wasteful on-chain competition while still incentivizing low fees and fast fills. | ||
|
||
* **Operational Flexibility** | ||
* Relayers can now Relay, Prove, and Claim all from different addresses. | ||
* This offers throughput & efficiency improvements for advanced Relayers | ||
* Multiple Quoting options for relayers to choose from to maximize competitiveness | ||
|
||
## Synapse Intent Network: Technical Improvements | ||
|
||
### Quoting and API Improvements | ||
|
||
The transition to the Synapse Intent Network brings significant changes to our quoting infrastructure, focusing on real-time price discovery and efficient market making. The most notable addition is active quoting alongside our existing passive quoting system. | ||
|
||
#### Active Quoting | ||
|
||
Traditional passive quoting works like an order book - relayers post standing offers that users can take. While this model is simple and efficient for stable market conditions, it can lag during volatile periods. Active quoting addresses this limitation by enabling relayers to respond to quote requests in real-time: | ||
|
||
```typescript | ||
// Example WebSocket quote request format | ||
interface QuoteRequest { | ||
data: { | ||
origin_chain_id: number; | ||
dest_chain_id: number; | ||
origin_token_addr: string; | ||
dest_token_addr: string; | ||
origin_amount_exact: string; | ||
expiration_window: number; | ||
} | ||
} | ||
``` | ||
|
||
This hybrid approach gives relayers flexibility in their market-making strategies. Simple integrations can stick with passive quoting, while sophisticated relayers can implement dynamic pricing models that account for immediate market conditions, liquidity depth, and cross-chain gas costs. | ||
|
||
### WebSocket API Evolution | ||
|
||
Supporting this new quoting model required rethinking our API infrastructure. The new WebSocket layer eliminates the need for polling and complex state management: | ||
|
||
```typescript | ||
const operations = { | ||
subscribe: "Subscribe to specific chains", | ||
send_quote: "Respond to quote request", | ||
request_quote: "New quote request notification" | ||
} | ||
``` | ||
|
||
The real-time nature of WebSockets dramatically reduces quote latency. Rather than repeatedly querying for updates, relayers receive instant notifications about new opportunities. This improved efficiency translates directly to better pricing for end users as relayers can operate with tighter spreads. | ||
|
||
## Contract Improvements | ||
|
||
:::info | ||
|
||
The Synapse Intent Network is backwards compatible with the original Fastbridge Contracts. | ||
|
||
::: | ||
|
||
### Gas Optimizations | ||
|
||
A core focus of V2 was reducing transaction costs without sacrificing code clarity. Through careful struct packing and custom error implementations, we've achieved significant gas savings: | ||
|
||
```solidity | ||
struct BridgeTxDetails { | ||
BridgeStatus status; // 1 byte | ||
uint32 destChainId; // 4 bytes | ||
uint16 proverID; // 2 bytes | ||
uint40 proofBlockTimestamp; // 5 bytes | ||
address proofRelayer; // 20 bytes | ||
} // Total: 32 bytes (1 slot) | ||
``` | ||
|
||
These optimizations balance efficiency with maintainability. While more aggressive packing was possible, it would have made the code significantly harder to reason about. The current implementation provides meaningful gas savings while keeping the codebase approachable for new developers. | ||
|
||
### Atomic Operations with Multicall and Zaps | ||
|
||
Cross-chain operations often require multiple transactions. V2 introduces multicall support and a new Zap interface to address this friction: | ||
|
||
```solidity | ||
// Multicall enables efficient batching | ||
fastBridge.multicallNoResults([ | ||
abi.encodeCall(IFastBridge.prove, (request1)), | ||
abi.encodeCall(IFastBridge.claim, (request1)) | ||
], false); | ||
// Zaps enable complex atomic operations | ||
interface IZapRecipient { | ||
function zap( | ||
address token, | ||
uint256 amount, | ||
bytes memory zapData | ||
) external payable returns (bytes4); | ||
} | ||
``` | ||
|
||
The Zap interface is intentionally minimal, maximizing composability while maintaining security. It enables powerful workflows like "bridge and stake" or "bridge and provide liquidity" in a single atomic transaction. These compound operations significantly improve the user experience by reducing transaction overhead and eliminating partial execution risks. | ||
|
||
## Final Thoughts | ||
|
||
The Synapse Intent Network (FastBridgeV2) update represents a thoughtful evolution of our protocol. Each change was evaluated not just for its immediate benefits, but for its long-term impact on protocol composability and user experience. The result is a more efficient, developer-friendly, and user-centric cross-chain bridging system. Please reach out to us if you have any questions or feedback. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,185 @@ | ||||||||||||||||||||||||||||||||||||||||||
export const RFQFlow = () => { | ||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||
<svg | ||||||||||||||||||||||||||||||||||||||||||
width="100%" | ||||||||||||||||||||||||||||||||||||||||||
viewBox="-240 0 480 224" | ||||||||||||||||||||||||||||||||||||||||||
xmlns="http://www.w3.org/2000/svg" | ||||||||||||||||||||||||||||||||||||||||||
className="flowAnimation" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add accessibility attributes and TypeScript types. Consider enhancing the SVG accessibility and type safety: -export const RFQFlow = () => {
+import { FC } from 'react'
+
+export const RFQFlow: FC = () => {
return (
<svg
width="100%"
viewBox="-240 0 480 224"
xmlns="http://www.w3.org/2000/svg"
className="flowAnimation"
+ role="img"
+ aria-label="RFQ Flow Diagram"
> 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
<set | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowTimer" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="x" | ||||||||||||||||||||||||||||||||||||||||||
begin="0s; rfqFlowTimerOut.end + 2s" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<g fill="currentcolor" fillOpacity=".05"> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="-50%" rx="4" y="0" width="100%" height="48" /> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="-50%" rx="4" y="56" width="100%" height="48" /> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="-50%" rx="4" y="112" width="100%" height="48" /> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="-50%" rx="4" y="168" width="100%" height="48" /> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="-50%" rx="4" y="0%" width="33.3%" height="100%" /> | ||||||||||||||||||||||||||||||||||||||||||
<rect x="16.7%" rx="4" y="0%" width="33.3%" height="100%" /> | ||||||||||||||||||||||||||||||||||||||||||
</g> | ||||||||||||||||||||||||||||||||||||||||||
<line | ||||||||||||||||||||||||||||||||||||||||||
x1="-50%" | ||||||||||||||||||||||||||||||||||||||||||
y1="100%" | ||||||||||||||||||||||||||||||||||||||||||
x2="-50%" | ||||||||||||||||||||||||||||||||||||||||||
y2="100%" | ||||||||||||||||||||||||||||||||||||||||||
stroke="currentcolor" | ||||||||||||||||||||||||||||||||||||||||||
strokeWidth="4" | ||||||||||||||||||||||||||||||||||||||||||
strokeOpacity=".25" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="x1" to="-50%" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="x2" to="-50%" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="x2" | ||||||||||||||||||||||||||||||||||||||||||
values="-50%; 50%" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSend.begin" | ||||||||||||||||||||||||||||||||||||||||||
dur="1.85s" | ||||||||||||||||||||||||||||||||||||||||||
calcMode="linear" | ||||||||||||||||||||||||||||||||||||||||||
keyTimes="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
keySplines=".5 0 1 1" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowTimerOut" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="x1" | ||||||||||||||||||||||||||||||||||||||||||
values="-50%; 50%" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowRepay.end + 1s" | ||||||||||||||||||||||||||||||||||||||||||
dur=".75s" | ||||||||||||||||||||||||||||||||||||||||||
calcMode="spline" | ||||||||||||||||||||||||||||||||||||||||||
keyTimes="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
keySplines=".5 0 .2 1" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
</line> | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+22
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add support for reduced motion preferences. Consider respecting the user's motion preferences to improve accessibility. + <style>
+ @media (prefers-reduced-motion: reduce) {
+ .flowAnimation line {
+ animation-duration: 0.01s;
+ transition-duration: 0.01s;
+ }
+ }
+ </style>
|
||||||||||||||||||||||||||||||||||||||||||
<g fill="currentcolor" textAnchor="middle" dominantBaseline="middle"> | ||||||||||||||||||||||||||||||||||||||||||
<text x="-33%" y="24"> | ||||||||||||||||||||||||||||||||||||||||||
originChain | ||||||||||||||||||||||||||||||||||||||||||
</text> | ||||||||||||||||||||||||||||||||||||||||||
<text x="33%" y="24"> | ||||||||||||||||||||||||||||||||||||||||||
destChain | ||||||||||||||||||||||||||||||||||||||||||
</text> | ||||||||||||||||||||||||||||||||||||||||||
<text y="24">App / SDK</text> | ||||||||||||||||||||||||||||||||||||||||||
<text y="80">User</text> | ||||||||||||||||||||||||||||||||||||||||||
<text y="136">Relayer</text> | ||||||||||||||||||||||||||||||||||||||||||
<text y="192">Bridge</text> | ||||||||||||||||||||||||||||||||||||||||||
</g> | ||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||
cx="-33%" | ||||||||||||||||||||||||||||||||||||||||||
cy="80" | ||||||||||||||||||||||||||||||||||||||||||
r="12" | ||||||||||||||||||||||||||||||||||||||||||
fill="hsl(211deg 67% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
stroke="hsl(211deg 67% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="cy" to="80" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowSign" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="opacity" | ||||||||||||||||||||||||||||||||||||||||||
values="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
dur=".1s" | ||||||||||||||||||||||||||||||||||||||||||
repeatCount="3" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowTimer.begin + 1s" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowSend" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="cy" | ||||||||||||||||||||||||||||||||||||||||||
to="192" | ||||||||||||||||||||||||||||||||||||||||||
dur=".5s" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSign.end + 2s" | ||||||||||||||||||||||||||||||||||||||||||
calcMode="spline" | ||||||||||||||||||||||||||||||||||||||||||
keyTimes="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
keySplines=".5 0 .2 1" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowRepay" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="cy" | ||||||||||||||||||||||||||||||||||||||||||
to="136" | ||||||||||||||||||||||||||||||||||||||||||
dur=".5s" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowReceive.end + 1.1s" | ||||||||||||||||||||||||||||||||||||||||||
calcMode="spline" | ||||||||||||||||||||||||||||||||||||||||||
keyTimes="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
keySplines=".5 0 .2 1" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
</circle> | ||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||
cx="-33%" | ||||||||||||||||||||||||||||||||||||||||||
cy="136" | ||||||||||||||||||||||||||||||||||||||||||
r="12" | ||||||||||||||||||||||||||||||||||||||||||
stroke="hsl(211deg 67% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
fill="none" | ||||||||||||||||||||||||||||||||||||||||||
opacity="0" | ||||||||||||||||||||||||||||||||||||||||||
strokeDasharray="2.5" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="opacity" to="0" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="opacity" | ||||||||||||||||||||||||||||||||||||||||||
values="0; .3; 0; .5; 0; .7; 0; 1" | ||||||||||||||||||||||||||||||||||||||||||
dur=".4s" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSign.begin" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="stroke-dashoffset" | ||||||||||||||||||||||||||||||||||||||||||
by="5" | ||||||||||||||||||||||||||||||||||||||||||
dur="1s" | ||||||||||||||||||||||||||||||||||||||||||
repeatCount="indefinite" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
</circle> | ||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||
r="11" | ||||||||||||||||||||||||||||||||||||||||||
cx="33%" | ||||||||||||||||||||||||||||||||||||||||||
cy="80" | ||||||||||||||||||||||||||||||||||||||||||
stroke="hsl(164deg 37% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
fill="none" | ||||||||||||||||||||||||||||||||||||||||||
opacity="0" | ||||||||||||||||||||||||||||||||||||||||||
strokeDasharray="2.5" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="opacity" to="0" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="opacity" | ||||||||||||||||||||||||||||||||||||||||||
values="0; .3; 0; .5; 0; .7; 0; 1" | ||||||||||||||||||||||||||||||||||||||||||
dur=".4s" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSign.begin" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="stroke-dashoffset" | ||||||||||||||||||||||||||||||||||||||||||
by="5" | ||||||||||||||||||||||||||||||||||||||||||
dur="1s" | ||||||||||||||||||||||||||||||||||||||||||
repeatCount="indefinite" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
</circle> | ||||||||||||||||||||||||||||||||||||||||||
<circle | ||||||||||||||||||||||||||||||||||||||||||
r="12" | ||||||||||||||||||||||||||||||||||||||||||
cx="33%" | ||||||||||||||||||||||||||||||||||||||||||
cy="136" | ||||||||||||||||||||||||||||||||||||||||||
fill="hsl(164deg 37% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
stroke="hsl(164deg 37% 50%)" | ||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||
<set attributeName="cy" to="136" begin="rfqFlowTimer.begin" /> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
attributeName="opacity" | ||||||||||||||||||||||||||||||||||||||||||
values="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
dur=".1s" | ||||||||||||||||||||||||||||||||||||||||||
repeatCount="3" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSign.begin" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
<animate | ||||||||||||||||||||||||||||||||||||||||||
id="rfqFlowReceive" | ||||||||||||||||||||||||||||||||||||||||||
attributeName="cy" | ||||||||||||||||||||||||||||||||||||||||||
to="80" | ||||||||||||||||||||||||||||||||||||||||||
dur=".5s" | ||||||||||||||||||||||||||||||||||||||||||
begin="rfqFlowSend.begin + 2s" | ||||||||||||||||||||||||||||||||||||||||||
calcMode="spline" | ||||||||||||||||||||||||||||||||||||||||||
keyTimes="0; 1" | ||||||||||||||||||||||||||||||||||||||||||
keySplines=".5 0 .2 1" | ||||||||||||||||||||||||||||||||||||||||||
fill="freeze" | ||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||
</circle> | ||||||||||||||||||||||||||||||||||||||||||
</svg> | ||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Document IZapRecipient interface requirements.
The interface lacks crucial documentation:
This documentation is essential for developers implementing the interface correctly.