You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Porto wallet currently provides excellent asset diff previews for regular on-chain transactions, but this doesn't work for modern intent-based protocols that use EIP-712 signatures. When users interact with protocols like CoWSwap, UniswapX, or 1inch Fusion, they only see cryptic signature data instead of a meaningful preview.
Create an extensible API that allows protocol developers to contribute decoders for their EIP-712 signatures. This enables Porto to show rich previews while keeping the codebase maintainable and secure.
Core API Design
// Main API - what Porto wallet would useexportasyncfunctiondecodeProtocolSignature(input: SignatureInput): Promise<DecodedTransaction>{constprotocol=awaitdetectProtocol(input)if(!protocol){returncreateUnknownProtocolResponse(input)}returnawaitprotocol.decode(input)}// Fallback for unknown protocols - still show what we canfunctioncreateUnknownProtocolResponse(input: SignatureInput): DecodedTransaction{return{protocol: 'Unknown Protocol',action: 'unknown_signature',summary: 'Unknown EIP-712 signature - use at your own risk',details: {assets: [],fees: [],interactions: input.to ? [{contract: input.to,function: 'unknown',description: 'Contract interaction with unknown signature'}] : []},warnings: ['Protocol not recognized by Porto wallet','Cannot verify transaction details - proceed with caution','Contact Porto team to request protocol support'],metadata: {rawTypedData: input.typedData,detectionFailed: true}}}// Input format - what Porto providesinterfaceSignatureInput{signature?: HextypedData?: {domain: TypedDataDomaintypes: Record<string,any>primaryType: stringmessage: any}calldata?: Hexto?: AddresschainId: numbercontext?: {origin?: string// e.g., "cowswap.exchange"userAgent?: stringreferrer?: string}}// Output format - what Porto displays to usersinterfaceDecodedTransaction{protocol: string// "CoW Protocol", "UniswapX", etc.action: string// "sell_order", "cross_chain_intent"summary: string// "Sell 1000 USDC for 0.5 ETH"details: {assets: AssetTransfer[]// What tokens are sent/receivedfees: Fee[]// Protocol and gas feestiming?: {// Deadlines and expiryvalidUntil?: Datedeadline?: Date}sender: Address// Who is sending/signing the transactionrecipient: Address// Who will receive the output tokensinteractions: ContractInteraction[]// Which contracts are called}warnings?: string[]// Cross-chain risks, short deadlines, receiver mismatchesmetadata?: Record<string,any>// Protocol-specific data}interfaceAssetTransfer{type: 'send'|'receive'token: {address: Addresssymbol?: stringname?: stringdecimals?: number}amount: bigintamountFormatted?: stringrecipient?: Address// Specific recipient for this asset (if different from main recipient)}
Real-World Detection Examples
This approach handles various ways CoW Protocol can be accessed:
// How protocols are detected - multiple detection methodsinterfaceProtocolDetector{name: stringdetect: (input: SignatureInput)=>Promise<boolean>decode: (input: SignatureInput)=>Promise<DecodedTransaction>}// Example: CoW Protocol detector with aggregator supportconstcowswapDetector: ProtocolDetector={name: 'CoW Protocol',asyncdetect(input: SignatureInput): Promise<boolean>{// Method 1: ABI/Structure detection (catches aggregator usage)if(input.typedData?.primaryType==='Order'&&hasCoWOrderFields(input.typedData.types?.Order)){returntrue}// Method 2: Contract address detection (direct contract calls)constcowContracts={1: ['0x9008D19f58AAbD9eD0D60971565AA8510560ab41'],100: ['0x9008D19f58AAbD9eD0D60971565AA8510560ab41'],11155111: ['0x3430d04E42a722c5Ae52C5Bffbf1F230C2677600']}if(input.to&&cowContracts[input.chainId]?.includes(input.to.toLowerCase())){returntrue}// Method 3: EIP-712 domain detection (direct frontend usage)if(input.typedData?.domain?.name==='Gnosis Protocol'&&input.typedData?.primaryType==='Order'){returntrue}// Method 4: Origin + validation (fallback with extra checks)if(input.context?.origin?.includes('swap.cow.fi')){returnhasCoWOrderFields(input.typedData?.types?.Order)}returnfalse},asyncdecode(input: SignatureInput): Promise<DecodedTransaction>{constorder=input.typedData.message// Detect if this came through an aggregatorconstisAggregated=input.typedData?.domain?.name!=='Gnosis Protocol'constaggregatorName=isAggregated ? detectAggregator(input) : null// Extract sender and receiver addressesconstsender=order.swapper||order.user||input.typedData.domain.verifyingContractconstreceiver=order.receiver||order.swapper||order.user// Check for receiver mismatch warningconstwarnings=[]if(sender.toLowerCase()!==receiver.toLowerCase()){warnings.push(`⚠️ Tokens will be sent to a different address: ${receiver}`)}constprotocolName=aggregatorName ? `CoW Protocol (via ${aggregatorName})` : 'CoW Protocol'return{protocol: protocolName,action: 'sell_order',summary: `Sell ${formatAmount(order.sellAmount)}${awaitgetTokenSymbol(order.sellToken)} for ${formatAmount(order.buyAmount)}${awaitgetTokenSymbol(order.buyToken)}`,details: {sender: sender,recipient: receiver,assets: [{type: 'send',token: order.sellToken,amount: order.sellAmount,recipient: receiver},{type: 'receive',token: order.buyToken,amount: order.buyAmount,recipient: receiver}],fees: [{type: 'protocol',amount: order.feeAmount,token: order.sellToken}],timing: {validUntil: newDate(order.validTo*1000)},interactions: [{contract: input.typedData.domain.verifyingContract,function: 'settlement',description: 'CoW Protocol settlement execution'}]},
warnings,metadata: {aggregator: aggregatorName,isDirectUsage: !isAggregated}}}}// Helper functionsfunctionhasCoWOrderFields(orderFields: any[]): boolean{if(!orderFields)returnfalseconstrequiredFields=['sellToken','buyToken','sellAmount','buyAmount','validTo','appData']constfieldNames=orderFields.map(f=>f.name)returnrequiredFields.every(field=>fieldNames.includes(field))}functiondetectAggregator(input: SignatureInput): string|null{if(input.context?.origin?.includes('app.1inch.io'))return'1inch'if(input.context?.origin?.includes('paraswap.io'))return'ParaSwap'if(input.context?.origin?.includes('matcha.xyz'))return'Matcha'if(input.typedData?.domain?.name?.includes('1inch'))return'1inch'return'Unknown Aggregator'}
Standard Support (ERC-7683)
// Automatic support for ERC-7683 CrossChainIntent standardconsterc7683Detector: ProtocolDetector={name: 'ERC-7683 CrossChainIntent',asyncdetect(input: SignatureInput): Promise<boolean>{conststandardTypes=['CrossChainOrder','OnchainCrossChainOrder','GaslessCrossChainOrder']returnstandardTypes.includes(input.typedData?.primaryType)},asyncdecode(input: SignatureInput): Promise<DecodedTransaction>{// Generic ERC-7683 decoding with cross-chain contextconstorder=input.typedData.messageconstoriginChain=getChainName(order.originChainId)constdestChain=getChainName(order.destinationChainId)return{protocol: 'ERC-7683 CrossChainIntent',action: 'cross_chain_intent',summary: `Bridge tokens from ${originChain} to ${destChain}`,details: {/* decoded cross-chain details */},warnings: [`Cross-chain transaction: tokens will arrive on ${destChain}`]}}}
Implementation for dApp Developers
For CoW Protocol Team
// cowswap-porto-decoder.ts - what CoW team would contributeexportconstcowProtocolDetector: ProtocolDetector={name: 'CoW Protocol',asyncdetect(input: SignatureInput): Promise<boolean>{returninput.typedData?.domain?.name==='Gnosis Protocol'&&input.typedData?.primaryType==='Order'},asyncdecode(input: SignatureInput): Promise<DecodedTransaction>{constorder=input.typedData.message// Use CoW's existing orderUid generationconstorderUid=generateOrderUid(order,input.typedData.domain)// Fetch from CoW API for complete dataconstapiUrl=getCoWApiUrl(input.chainId)constorderDetails=awaitfetch(`${apiUrl}/orders/${orderUid}`).then(r=>r.json())// Decode appData for rich contextconstappData=awaitdecodeAppData(order.appData)return{protocol: 'CoW Protocol',summary: `${order.kind==='sell' ? 'Sell' : 'Buy'}${formatTokenAmount(order)} on CoW Protocol`,details: {sender: order.swapper||order.user,recipient: order.receiver,assets: buildAssetTransfers(order),fees: buildFees(order),timing: {validUntil: newDate(order.validTo*1000)}//},warnings: generateReceiverWarnings(order.swapper||order.user,order.receiver),metadata: { orderUid, appData, orderDetails }}}}// Register with PortoregisterProtocolDetector(cowProtocolDetector)
For 1inch Team
// 1inch-porto-decoder.ts - what 1inch team would contribute exportconstoneinchDetector: ProtocolDetector={name: '1inch Fusion',asyncdetect(input: SignatureInput): Promise<boolean>{returninput.typedData?.domain?.name?.includes('1inch')||input.context?.origin?.includes('app.1inch.io')},asyncdecode(input: SignatureInput): Promise<DecodedTransaction>{// 1inch-specific decoding logicconstorder=input.typedData.messagereturn{protocol: '1inch Fusion',summary: 'Limit order on 1inch Fusion',details: decode1inchOrder(order)}}}
Example for AAVE:
// Deposit ETH + Borrow USDC in single Porto smart wallet transaction{protocol: 'Aave Protocol',action: 'supply_and_borrow',summary: 'Deposit 2 ETH as collateral and borrow 3000 USDC',details: {assets: [// What you're sending{type: 'send',token: 'ETH',amount: '2000000000000000000',recipient: 'Aave Pool'},// Multiple tokens you're receiving{type: 'receive',token: 'aETH',amount: '2000000000000000000',recipient: '0xYourAddress'},{type: 'receive',token: 'USDC',amount: '3000000000',recipient: '0xYourAddress'}],interactions: [{contract: '0xAave...',function: 'supply',description: 'Deposit ETH as collateral'},{contract: '0xAave...',function: 'borrow',description: 'Borrow USDC against ETH'}]},warnings: ['Your aETH can be liquidated if value drops below liquidation threshold','You will accrue interest on the 3000 USDC debt','aETH will earn interest over time']}
Benefits
For Porto Wallet
Rich previews for all major intent protocols
Maintainable: Protocol teams contribute their own decoders
Extensible: Easy to add new protocols as they emerge
For Protocol Teams
Better UX: Users see clear previews instead of cryptic signatures
Self-service: Teams can add support without waiting for Porto
Brand visibility: Protocol name and context shown in wallet
Reduced support: Fewer confused users asking "what am I signing?"
For Users
Transparency: Know exactly what tokens are being traded
Safety: See fees, deadlines, and cross-chain risks upfront
Confidence: Trust the transaction before signing
Migration Path
Phase 1: Porto implements the core API and detector system
Phase 2: Add built-in support for major protocols (CoW, UniswapX, 1inch)
Phase 3: Open contribution system for protocol teams to submit decoders
Phase 4: Automatic ERC-7683 standard support for future protocols
Security Considerations
Contract validation: All detectors validate against hardcoded contract addresses
Origin verification: Website origin checked against known protocol domains
Signature verification: EIP-712 signatures validated before decoding
Receiver validation: Clear warnings when tokens go to different address than sender
Input validation: All user inputs sanitized and validated
Review process: All contributed decoders reviewed by Porto team
Critical Security Feature: Receiver Validation
One of the most important security features is validating the token receiver:
// Generate receiver warnings to prevent social engineering attacksfunctiongenerateReceiverWarnings(sender: Address,receiver: Address): string[]{constwarnings=[]if(sender.toLowerCase()!==receiver.toLowerCase()){warnings.push(`⚠️ ATTENTION: Tokens will be sent to ${receiver} (different from your address)`)warnings.push(`Double-check this is intentional - this could be a social engineering attack`)}returnwarnings}
This is critical because:
Social engineering attacks: Malicious actors can create legitimate-looking swaps that send tokens elsewhere
Phishing protection: Users immediately see if tokens aren't coming to their wallet
Multi-sig scenarios: Legitimate use cases (like sending to another wallet) are still supported with clear warnings
This proposal would make Porto the first wallet to provide comprehensive intent preview support, setting a new standard for DeFi wallet UX while creating a sustainable way to keep up with the rapidly evolving intent-based ecosystem.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
Porto wallet currently provides excellent asset diff previews for regular on-chain transactions, but this doesn't work for modern intent-based protocols that use EIP-712 signatures. When users interact with protocols like CoWSwap, UniswapX, or 1inch Fusion, they only see cryptic signature data instead of a meaningful preview.
Current experience on CowSwap UI using Safe:
{ "owner": "0x1F49737b9Ca352782550cF58cdBbA421C0A52dC1", "orderUid": "0x6dd12afbbd165c0e9e513df669985beab90e7df78c287270984e9768b7650bec...", "signed": true }The user needs to manually check orderId to get the details but still no way to get appData details without CowSwap ABI https://api.cow.fi/sepolia/api/v1/orders/0x6dd12afbbd165c0e9e513df669985beab90e7df78c287270984e9768b7650bec1f49737b9ca352782550cf58cdbba421c0a52dc1684ea19c
On Metamask, the user gets the json details but still no appData decoded.
What users need to see:
Solution
Create an extensible API that allows protocol developers to contribute decoders for their EIP-712 signatures. This enables Porto to show rich previews while keeping the codebase maintainable and secure.
Core API Design
Real-World Detection Examples
This approach handles various ways CoW Protocol can be accessed:
Standard Support (ERC-7683)
Implementation for dApp Developers
For CoW Protocol Team
For 1inch Team
Example for AAVE:
Benefits
For Porto Wallet
For Protocol Teams
For Users
Migration Path
Security Considerations
Critical Security Feature: Receiver Validation
One of the most important security features is validating the token receiver:
This is critical because:
This proposal would make Porto the first wallet to provide comprehensive intent preview support, setting a new standard for DeFi wallet UX while creating a sustainable way to keep up with the rapidly evolving intent-based ecosystem.
Beta Was this translation helpful? Give feedback.
All reactions