
48 小时极客原型用 Viem 与 Framer Motion 打造全链 Web3 多签金库联签模拟器在 DAO 国库治理与企业级 Web3 资产管理中多签金库Multi-signature Vault / Gnosis Safe 2-of-3 / 3-of-5是保障数亿美元资金安全的终极防御墙。然而传统的 Safe Web 界面交互偏重于表格与生硬的文字状态新加入 DAO 的理事成员往往无法直观理解为什么需要多个私钥在链下进行EIP-712 结构化数据签名Off-chain Typed Signatures收集到的签名是如何在链上被**按地址升序拼接Sorted Signatures**并一次性触发执行的在上周末的 48 小时极客冲刺中我利用Viem 原生 EIP-712 签名聚合引擎、Framer Motion 赛博量子插槽动效以及Next.js 14 App Router从零打造了一款“全链 Web3 多签金库联签动态模拟看板”。一、多签金库联签与聚合执行全景拓扑graph TD Initiator[发起人创建提案: 调拨 100,000 USDC] -- SafeMessage[构造 EIP-712 SafeTx 结构体数据] subgraph 3/5 门限联签模拟器 (Framer Motion 动态量子插槽) SafeMessage -- SignerA[理事 A (0xAlice...): 点击签名 - 获得 SigA - 插入插槽 1 ] SafeMessage -- SignerB[理事 B (0xBob...): 点击签名 - 获得 SigB - 插入插槽 2 ] SafeMessage -- SignerC[理事 C (0xCharlie...): 点击签名 - 获得 SigC - 插入插槽 3 ] end SignerA SignerB SignerC -- Aggregator[Viem 签名聚合器: 严格按 Signer 地址大小升序排序并打包为 195 字节 bytes] Aggregator -- ThresholdMet{是否达到 3/3 门限?} ThresholdMet --|是| LaserActivate[触发能量充能动效: 解锁链上 EXECUTE 按钮] LaserActivate -- FinalExec[向 Safe 合约提交 execTransaction - 原子调拨资金上链!]二、多签签名收集与地址升序聚合算法实现在 Gnosis Safe 标准中链上合约在核验签名时强制要求所有签名必须严格按照签名者公钥地址的十六进制数值从小到大排列否则直接 Revert// lib/safeSignatureAggregator.ts import { encodePacked, recoverTypedDataAddress } from viem; export interface MultiSigSigner { id: string; name: string; address: 0x${string}; signature: 0x${string} | null; } export function aggregateAndSortSafeSignatures(signers: MultiSigSigner[]): 0x${string} { // 1. 过滤已签署的成员 const activeSignatures signers.filter((s) s.signature ! null); // 2. 核心铁律必须按钱包地址十六进制严格升序排序 activeSignatures.sort((a, b) { return a.address.toLowerCase().localeCompare(b.address.toLowerCase()); }); // 3. 紧凑拼接 65 字节签名数据 (r, s, v) let concatenated 0x; for (const signer of activeSignatures) { concatenated signer.signature!.slice(2); } console.log( [Signatures Aggregated] ${activeSignatures.length} signatures sorted and packed: ${concatenated}); return concatenated as 0x${string}; }三、React Framer Motion 赛博多签插槽组件实现// components/SafeMultiSigHUD.tsx use client; import React, { useState } from react; import { motion, AnimatePresence } from framer-motion; import { aggregateAndSortSafeSignatures, MultiSigSigner } from /lib/safeSignatureAggregator; export function SafeMultiSigHUD() { const [signers, setSigners] useStateMultiSigSigner[]([ { id: 1, name: 理事 A (Alice), address: 0x1111111111111111111111111111111111111111, signature: null }, { id: 2, name: 理事 B (Bob), address: 0x2222222222222222222222222222222222222222, signature: null }, { id: 3, name: 理事 C (Charlie), address: 0x3333333333333333333333333333333333333333, signature: null }, { id: 4, name: 理事 D (Dave), address: 0x4444444444444444444444444444444444444444, signature: null }, ]); const threshold 3; // 3/4 门限 const signedCount signers.filter((s) s.signature ! null).length; const isThresholdReached signedCount threshold; const handleSign (id: string) { setSigners((prev) prev.map((s) s.id id ? { ...s, signature: 0x999999999999999999999999999999999999999999999999999999999999999977 } : s ) ); }; const handleExecute () { const packed aggregateAndSortSafeSignatures(signers); alert( [多签达成] 正在向链上广播聚合签名包 (195 字节):\n${packed.slice(0, 32)}...); }; return ( div classNamemax-w-3xl mx-auto p-8 bg-slate-950 border border-slate-800 rounded-3xl text-white shadow-2xl {/* 头部状态 */} div classNameflex justify-between items-center mb-8 pb-4 border-b border-slate-800 div span classNametext-[10px] px-3 py-1 bg-cyan-950 text-cyan-400 border border-cyan-500/30 rounded-full font-mono font-bold SAFE PROTOCOL V1.4 // 3-of-4 THRESHOLD /span h2 classNametext-2xl font-black font-mono text-slate-100 mt-2DAO 国库多签联签模拟器/h2 /div div classNametext-right div classNametext-xs text-slate-400 font-mono已收集签名进度/div div classNametext-2xl font-black text-cyan-400 font-mono {signedCount} / {threshold} /div /div /div {/* 待签名提案详情 */} div classNamep-5 bg-slate-900/80 border border-slate-800 rounded-2xl mb-8 font-mono text-xs div classNametext-slate-400 mb-1PROPOSAL DETAILS:/div div classNametext-slate-100 font-bold调拨 100,000 USDC 至去中心化算力研发金库/div div classNametext-slate-500 text-[10px] mt-2TARGET: 0x87870Bca... | VALUE: 0 ETH | NONCE: #42/div /div {/* 理事成员签名插槽卡片列表 */} div classNamegrid grid-cols-2 gap-4 mb-8 {signers.map((signer) { const isSigned signer.signature ! null; return ( motion.div key{signer.id} whileHover{{ scale: 1.02 }} className{p-5 rounded-2xl border transition-all ${ isSigned ? bg-cyan-950/40 border-cyan-500/60 shadow-[0_0_20px_rgba(0,243,255,0.15)] : bg-slate-900 border-slate-800 }} div classNameflex justify-between items-center mb-3 span classNamefont-bold text-xs text-slate-200{signer.name}/span span className{text-[10px] font-mono font-bold px-2 py-0.5 rounded-full ${ isSigned ? bg-cyan-500 text-slate-950 : bg-slate-800 text-slate-500 }} {isSigned ? SIGNED : PENDING ⚪} /span /div div classNametext-[10px] font-mono text-slate-500 mb-4{signer.address.slice(0, 10)}.../div button onClick{() handleSign(signer.id)} disabled{isSigned} className{w-full py-2.5 rounded-xl text-xs font-bold transition font-mono ${ isSigned ? bg-slate-800 text-slate-600 cursor-not-allowed : bg-cyan-600 hover:bg-cyan-500 text-white shadow-lg active:scale-95 }} {isSigned ? 已完成 EIP-712 联签 : 签署此提案 (Sign)} /button /motion.div ); })} /div {/* 底部执行按钮 (门限达成时激活动态充能) */} motion.button onClick{handleExecute} disabled{!isThresholdReached} animate{isThresholdReached ? { scale: [1, 1.02, 1], boxShadow: [0 0 20px #00f3ff, 0 0 40px #00f3ff, 0 0 20px #00f3ff] } : {}} transition{{ repeat: Infinity, duration: 1.5 }} className{w-full py-4 rounded-2xl font-black font-mono text-sm transition-all ${ isThresholdReached ? bg-green-500 text-slate-950 shadow-2xl cursor-pointer : bg-slate-800 text-slate-500 cursor-not-allowed }} {isThresholdReached ? ⚡ 门限已达成立即上链执行交易 (EXECUTE) : ⏳ 仍需 ${threshold - signedCount} 个理事签名} /motion.button /div ); }四、48 小时极客原型调优复盘EIP-712 签名哈希防重放隔离在生成签名消息时严格注入chainId与verifyingContract确保在主网签署的多签交易绝无法在测试网或分叉链被重放地址排序自动化纠偏无论理事成员以什么先后顺序点击签名算法底层自动按照十六进制地址大小完成升序排列彻底避免了因顺序错乱导致的链上 Revert。用直观的动效展现密码学多签的协同魅力让 Web3 团队的国库治理在严密的交互设计中安全运转。