@etereum-surcify/ clear- signing (中文(简体) )
引用类型脚本执行 ERC-7730:结构化数据清晰签名格式。 。 。 .
这个库将原始交易 calldata和EIP-712打出的数据转换为人可读的显示模型,使钱包能够准确显示用户的签名内容.
设计可跳入钱包代码库 :
- 运行在现代浏览器上,Node.js (≥22),和回放原生(ESM + CJS).
- 两个运行时间依赖:
@noble/hashes财务报告和审定财务报表@noble/curves。 。 。 . - 纯格式化:没有 RPC 客户端, 没有令牌/ 链/ ENS 获取; 外部数据通过 :
ExternalDataProvider。 。 。 . - 没有内部缓存 : 调用者在获取描述和索引时控制 .
钱包整合指南
融入钱包? 见 钱包整合指南。 。 。 .
安装
npm install @ethereum-sourcify/clear-signing
快速启动
建议的模式:在应用程序启动时获取登记索引,并传递给每个 format() 编号 : formatTypedData() 电话 库不会在内部缓存取来索引——没有索引,每个格式调用再fetches.
import {
format,
fetchPrebuiltRegistryIndex,
} from "@ethereum-sourcify/clear-signing";
// At app startup — fetch once, keep in memory.
const index = await fetchPrebuiltRegistryIndex();
const baseOpts = {
descriptorResolverOptions: { type: "github" as const, index },
externalDataProvider: {
resolveToken: async (chainId, address) => {
// return token metadata from your wallet's token list
// or call the ERC-20 methods on-chain
return { name: "Tether USD", symbol: "USDT", decimals: 6 };
},
},
};
// Reuse on every format call — no re-fetching of the index.
const result = await format(
{
chainId: 1,
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT
data:
"0x095ea7b3" +
"0000000000000000000000001234567890abcdef1234567890abcdef12345678" + // spender
"00000000000000000000000000000000000000000000000000000000000f4240", // value
},
baseOpts,
);
console.log(result.intent); // "Approve"
console.log(result.fields); // [{ label: "Spender", value: "0x1234..." }, { label: "Amount", value: "1 USDT" }]
console.log(result.interpolatedIntent); // "Approve 1 USDT to 0x1234..."
如果你省略 index (或 descriptorResolverOptions 完全如此, format() 仍然有效——它只是收集每个电话的预设索引.
API 参考
format(tx, opts?)
将单个交易的调用数据格式化为 DisplayModel。通过链和合同地址确定一个ERC-7730描述符,解码调用数据,并制作字段。 跌回一个 rawCalldataFallback 当没有描述符匹配时.
async function format(
tx: Transaction,
opts?: FormatOptions,
): Promise<DisplayModel>;
interface Transaction {
chainId: number;
to: string;
data: string; // calldata as hex string
value?: bigint;
from?: string;
}
formatTypedData(typedData, opts?)
将 EIP-712 输入的数据电文格式化为 DisplayModel通过域链和核查合同,确定一个ERC-7730描述符,与主类型匹配 encodeType,并制作地块。 要求数 chainId 财务报告和审定财务报表 verifyingContract 在域内.
async function formatTypedData(
typedData: TypedData,
opts?: FormatOptions,
): Promise<DisplayModel>;
interface TypedData {
account: string; // signer address
types: Record<string, TypeMember[]>;
primaryType: string;
domain: TypedDataDomain;
message: Record<string, unknown>;
}
formatEip5792Batch(batch, opts?)
格式化为 EIP-5792 信息技术 批次调用为a BatchDisplayModel中,每个调用都通过 format()。批量级别 interpolatedIntent 将所有个人意图与 " 和 " 合并,具体方式如下: ERC-7730 (英语)。 。 。 .
async function formatEip5792Batch(
batch: Eip5792Batch,
opts?: FormatOptions,
): Promise<BatchDisplayModel>;
interface Eip5792Batch {
from?: string;
chainId: number;
calls: Eip5792Call[];
}
interface Eip5792Call {
to?: string;
data?: string;
value?: bigint;
}
FormatOptions
interface FormatOptions {
/**
* Wallets should provide an object with async methods to resolve
* external data like ENS names, token metadata, and NFT collection
* names. The provided functions may use RPC calls or fetch data
* from internal sources. This allows the library to remain
* agnostic about how this data is fetched. If absent, the library
* will fall back to raw formats for the corresponding fields.
*/
externalDataProvider?: ExternalDataProvider;
/**
* Controls where descriptors are fetched from, and optionally carries a
* `trustedTokens` list (see below). Defaults to the GitHub registry when
* omitted.
*/
descriptorResolverOptions?: GitHubResolverOptions | CustomResolverOptions;
}
ExternalDataProvider
图书馆将所有外部数据解析到钱包中 。 钱包可能使用 RPC 调用来解决数据 。 见 src/types.ts 用于完整返回类型定义(例如:. TokenResult, (中文(简体) ), ChainInfoResult(见a/c.5/49/70,附件).
interface ExternalDataProvider {
/**
* Raw chain access for checks the library performs itself. Required when an AttestationOptions policy
* is set: the ERC-8176 revocation check reads the EAS contract on Ethereum
* mainnet.
*/
chainClient?: ChainClient;
/**
* Resolution for addressName formats. The wallet should verify whether the
* address matches any of the provided accepted types (e.g., "eoa", "contract", ...)
* if able to. If none of the types match, set typeMatch to false so the library
* can include a warning in the DisplayModel. When acceptedTypes is absent, the
* descriptor has no type constraint and typeMatch: true can be returned safely.
*/
resolveLocalName?: (
address: string,
acceptedTypes?: DescriptorAddressType[],
) => Promise<AddressNameResult | null>;
/**
* Resolution for addressName formats. The wallet should verify whether the
* address matches any of the provided accepted types (e.g., "eoa", "contract", ...)
* if able to. If none of the types match, set typeMatch to false so the library
* can include a warning in the DisplayModel. When acceptedTypes is absent, the
* descriptor has no type constraint and typeMatch: true can be returned safely.
*/
resolveEnsName?: (
address: string,
acceptedTypes?: DescriptorAddressType[],
) => Promise<AddressNameResult | null>;
/** Resolution for tokenAmount formats. */
resolveToken?: (
chainId: number,
tokenAddress: string,
) => Promise<TokenResult | null>;
/** Resolution for nftName formats. */
resolveNftCollectionName?: (
chainId: number,
collectionAddress: string,
) => Promise<NftCollectionNameResult | null>;
/** Resolution for date format with blockheight encoding. */
resolveBlockTimestamp?: (
chainId: number,
blockHeight: bigint,
) => Promise<BlockTimestampResult | null>;
/** Resolution for chainId and amount formats. */
resolveChainInfo?: (chainId: number) => Promise<ChainInfoResult | null>;
/**
* Decryption for fields carrying an `encryption` annotation. Optional and
* scheme-specific. Return `null` when the value cannot be decrypted — the
* field then renders the descriptor's `fallbackLabel`. See DECRYPTION.md.
*/
resolveDecryptedValue?: (
chainId: number,
encryptedValue: string,
params: {
scheme: DescriptorFieldEncryptionScheme;
contractAddress?: string;
},
) => Promise<DecryptedValueResult | null>;
}
interface ChainClient {
/** Performs eth_call against `chainId` and returns the 0x-hex return data. */
call: (
chainId: number,
request: { to: string; data: string },
) => Promise<string>;
}
描述来源
描述从 明确签名的登记册 默认在 GitHub 上。 见 快速启动 用于推荐的预选模式.
GitHub 注册( 默认)
过 githubSource 改为 fetchPrebuiltRegistryIndex 和比对 descriptorResolverOptions数字 :
const index = await fetchPrebuiltRegistryIndex(source);
const opts = {
descriptorResolverOptions: {
type: "github" as const,
githubSource: {
repo: "ethereum/clear-signing-erc7730-registry", // default
ref: "master", // default
},
index,
},
};
从描述文件构建索引
如果预设的索引缺失,或者您正在使用一个没有公布的分叉,请自己走入登记册 createGitHubRegistryIndex()。它的速度要慢得多(每个描述文件一个抓取对预建索引的两个),所以保留它用于设置时间.
文件系统解析器( 只有节点)
对于节点中捆绑的描述符或测试,从本地目录中构建自己的索引并加载描述符JSON文件. 文件系统解析器生活在一个仅节点的子路径导出中, 因此它不会被浏览器捆绑 :
import { format } from "@ethereum-sourcify/clear-signing";
import { createFilesystemResolver } from "@ethereum-sourcify/clear-signing/filesystem";
import type { RegistryIndex } from "@ethereum-sourcify/clear-signing";
const index: RegistryIndex = {
calldataIndex: {
"eip155:1:0xdac17f958d2ee523a2206206994597c13d831ec7":
"path/to/descriptor.json",
},
typedDataIndex: {},
};
const result = await format(tx, {
descriptorResolverOptions: {
type: "custom",
resolver: createFilesystemResolver({
index,
descriptorDirectory: "./descriptors",
}),
},
});
自定义解析器
{ type: "custom", resolver } 接受任何匹配对象 DescriptorResolver 形状,因此可以插入任意描述源(在映射图中,自定义的 HTTP 端点,.):
import type { DescriptorResolver } from "@ethereum-sourcify/clear-signing";
const resolver: DescriptorResolver = {
index,
fetchDescriptor: async (path) => {
/* return parsed descriptor for `path` */
},
};
const result = await format(tx, {
descriptorResolverOptions: { type: "custom", resolver },
});
证明(ERC-8176)
描述符控制用户在签名前所看到的. 钱包不能使用未经审查的描述符 . ERC-8176 (中文(简体) ) 增加一个审查层:审计员用一个 埃及 脱链证明。 登记册将证明文件存放在描述器旁边,单位为 registry/<project>/sigs/登记册 auditors/ 目录列出审计员和审计准则.
设定 attestations 启用此检查的政策。 只有在您所信任的审计员之一有有效证明时,图书馆才接受描述文件:
const result = await format(tx, {
descriptorResolverOptions: {
type: "github",
index,
attestations: {
// Auditor addresses your wallet trusts — see the registry's auditors/ directory.
trustedAttesters: ["0x3846c3A30E62075Fa916216b35EF04B8F53931f6"],
},
},
externalDataProvider: {
// Required when `attestations` is set. The library uses this raw eth_call
// access to read the revocation state from the EAS contract on Ethereum
// mainnet. See GUIDE.md.
chainClient: {
call: async (chainId, { to, data }) => rpcEthCall(chainId, to, data),
},
},
});
图书馆对每份证明进行离线核查(schema、descriptor hash、过期、签名、UID)。 然后它读取撤销状态 通过 externalDataProvider.chainClient当没有可信证明有效时,结果包含 NO_TRUSTED_ATTESTATION 警告,对于调用数据 rawCalldataFallback。 。 。 .
没有 attestations 政策,图书馆使用登记册描述符,无需任何审查检查。 只用这个模式进行测试.
注释:
- 政策需要
externalDataProvider.chainClient,而自定义解析器必须执行DescriptorResolver.fetchAttestation。否则,每门分辨率都会失败ATTESTATION_OPTIONS_INCOMPLETE。 。 。 . - 捆绑的可信任的脚本描述符( E)
trustedTokens)没有入门. - 只支持带有EAA签名的EAS出链证明(第2版).
信任的令牌列表
登记处不能为每个标志持有描述符。 仍然使ERC-20和ERC-721的平坦互动(transfer, (中文(简体) ), approve, (中文(简体) ), transferFrom, (中文(简体) ), safeTransferFrom, (中文(简体) ), setApprovalForAll),添加一个 trustedTokens 列表为 descriptorResolverOptions地图 chainId → tokenAddress → standard (处理小写或EIP-55复验)。 何时 无登记描述符解析 对于交易合同,图书馆在上面查找;如果列出,交易来自 捆绑 ERC-20 / ERC-721 模板描述符 而不是回到原始的呼叫数据。 登记说明总是优先.
import type { TrustedTokens } from "@ethereum-sourcify/clear-signing";
const trustedTokens: TrustedTokens = {
1: {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "erc20", // USDC
"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d": "erc721", // BAYC
},
};
const result = await format(tx, {
descriptorResolverOptions: { type: "github", index, trustedTokens },
});
信任完全委托给钱包。 图书馆从来不决定哪些合同值得信赖.
显示模式
所有格式函数返回 a DisplayModel数字 :
/**
* The complete display model produced by the library.
*
* According to ERC-7730, wallets have two display options:
* 1. Show `intent` as an explanation what the contract call does, and
* `fields` as a list of labeled values representing the calldata parameters.
* 2. Show `interpolatedIntent` as a short string presentation of intent and fields,
* which already has formatted field values embedded in it — in this case
* `fields` can be omitted or shown as supplementary detail.
*
* When interpolation fails or is not defined, wallets should fall back to Option 1.
*/
interface DisplayModel {
/**
* The intent from the resolved descriptor, representing a short
* description of the operation, e.g. "Approve token spending".
* Two possible forms:
* - A simple human-readable string
* - A list of human-readable key-value pairs
*/
intent?: string | Record<string, string>;
/**
* Ordered list of fields to show to the user,
* formatted according to their field format specification.
*/
fields?: (DisplayField | DisplayFieldGroup)[];
/**
* Full sentence with formatted field values interpolated in, e.g.
* "Approve USDC spending up to 1,000 USDC for Uniswap V3".
* Absent when the descriptor does not define an interpolatedIntent,
* or when interpolation fails.
*/
interpolatedIntent?: string;
/**
* Additional metadata directly from the resolved descriptor.
* Wallets may choose to display these items to provide additional
* context about the contract being interacted with.
*/
metadata?: {
owner?: string;
contractName?: string;
info?: { deploymentDate?: string; url?: string };
};
/**
* Raw calldata fallback when no descriptor matched or the descriptor was faulty.
* Only present for calldata formatting — not applicable to EIP-712 typed data.
*/
rawCalldataFallback?: RawCalldataFallback;
/**
* Warnings providing additional context, e.g. why
* interpolation failed.
*/
warnings?: Warning[];
}
批量显示模式
formatEip5792Batch 返回 a BatchDisplayModel数字 :
/**
* The `callDisplays` array has the same order as the input `calls`.
* The batch `interpolatedIntent` joins all individual intents with " and ".
*/
interface BatchDisplayModel {
interpolatedIntent?: string;
warnings?: Warning[];
callDisplays: DisplayModel[];
}
见 src/types.ts 用于完整类型定义.
已知的局限性——EIP-712指数覆盖率
GitHub 索引只显示密钥 EIP-712 描述符 context.eip712.deployments通过下列方式绑定的说明 context.eip712.domain 或者说 context.eip712.domainSeparator 无法预索引.
发展
npm install # Install dependencies
npm run build # Compile to dist/
npm test # Run tests
npm run test:watch # Watch mode
相关
许可证
麻省理工学院