exportinterfaceLinkedAddress{ens:string,address:string,}exportasyncfunctiongetLinkedAddress(provider:ethers.providers.EnsProvider,address:string):Promise<LinkedAddress|null>{constaddressENS=awaitprovider.lookupAddress(address);if(!addressENS)returnnull;constvaultInfo=await(awaitprovider.getResolver(addressENS))?.getText('eip5131:vault');if(!vaultInfo)returnnull;constvaultInfoArray=vaultInfo.split(':');if(vaultInfoArray.length!==2){thrownewError('EIP5131: Authkey and vault address not configured correctly.');}const[authKey,vaultAddress]=vaultInfoArray;constvaultENS=awaitprovider.lookupAddress(vaultAddress);if(!vaultENS){thrownewError(`EIP5131: No ENS domain with reverse record set for vault.`);};constexpectedSigningAddress=await(awaitprovider.getResolver(vaultENS))?.getText(`eip5131:${authKey}`);if(expectedSigningAddress?.toLowerCase()!==address.toLowerCase()){thrownewError(`EIP5131: Authentication mismatch.`);};return{ens:vaultENS,address:vaultAddress};}
合约端
带有后端
如果您的应用程序运行安全的后端服务器,您可以运行上面的客户端/服务器代码,然后结合 EIP-1271 等规范使用结果:Standard Signature Validation Method for Contracts 这是一种廉价且安全的方式来验证消息签名者确实已通过主地址进行身份验证。
没有后端 (仅 JavaScript)
提供了一个参考实现,用于验证消息发送者是否具有指向主地址的身份验证链接的内部函数。
// SPDX-License-Identifier: MIT
pragmasolidity^0.8.0;/// @author: manifold.xyz
/**
* ENS Registry Interface
*/interfaceENS{functionresolver(bytes32node)externalviewreturns(address);}/**
* ENS Resolver Interface
*/interfaceResolver{functionaddr(bytes32node)externalviewreturns(address);functionname(bytes32node)externalviewreturns(stringmemory);functiontext(bytes32node,stringcalldatakey)externalviewreturns(stringmemory);}/**
* Validate a signing address is associtaed with a linked address
*/libraryLinkedAddress{/**
* Validate that the message sender is an authentication address for mainAddress
* 验证消息发送者是 mainAddress 的身份验证地址
* @param ensRegistry Address of ENS registry ENS 注册表的地址
* @param mainAddress The main address we want to authenticate for. 我们要验证的主地址
* @param mainENSNodeHash The main ENS Node Hash 主 ENS 节点哈希
* @param authKey The TEXT record of the authKey we are using for validation 我们用于验证的 authKey 的 TEXT 记录
* @param authENSNodeHash The auth ENS Node Hash 身份验证 ENS 节点哈希
*/functionvalidateSender(addressensRegistry,addressmainAddress,bytes32mainENSNodeHash,stringcalldataauthKey,bytes32authENSNodeHash)internalviewreturns(bool){returnvalidate(ensRegistry,mainAddress,mainENSNodeHash,authKey,msg.sender,authENSNodeHash);}/**
* Validate that the authAddress is an authentication address for mainAddress
* 验证 authAddress 是 mainAddress 的身份验证地址
* @param ensRegistry Address of ENS registry ENS 注册表的地址
* @param mainAddress The main address we want to authenticate for. 我们要验证的主地址
* @param mainENSNodeHash The main ENS Node Hash 主 ENS 节点哈希
* @param authAddress The address of the authentication wallet 身份验证钱包的地址
* @param authENSNodeHash The auth ENS Node Hash 身份验证 ENS 节点哈希
*/functionvalidate(addressensRegistry,addressmainAddress,bytes32mainENSNodeHash,stringcalldataauthKey,addressauthAddress,bytes32authENSNodeHash)internalviewreturns(bool){_verifyMainENS(ensRegistry,mainAddress,mainENSNodeHash,authKey,authAddress);_verifyAuthENS(ensRegistry,mainAddress,authKey,authAddress,authENSNodeHash);returntrue;}// *********************
// Helper Functions
// *********************
function_verifyMainENS(addressensRegistry,addressmainAddress,bytes32mainENSNodeHash,stringcalldataauthKey,addressauthAddress)privateview{// Check if the ENS nodes resolve correctly to the provided addresses
// 检查 ENS 节点是否正确解析为提供的地址
addressmainResolver=ENS(ensRegistry).resolver(mainENSNodeHash);require(mainResolver!=address(0),"Main ENS not registered");require(mainAddress==Resolver(mainResolver).addr(mainENSNodeHash),"Main address is wrong");// Verify the authKey TEXT record is set to authAddress by mainENS
// 验证 authKey TEXT 记录是否由 mainENS 设置为 authAddress
stringmemoryauthText=Resolver(mainResolver).text(mainENSNodeHash,string(abi.encodePacked("eip5131:",authKey)));require(keccak256(bytes(authText))==keccak256(bytes(_addressToString(authAddress))),"Invalid auth address");}function_verifyAuthENS(addressensRegistry,addressmainAddress,stringmemoryauthKey,addressauthAddress,bytes32authENSNodeHash)privateview{// Check if the ENS nodes resolve correctly to the provided addresses
// 检查 ENS 节点是否正确解析为提供的地址
addressauthResolver=ENS(ensRegistry).resolver(authENSNodeHash);require(authResolver!=address(0),"Auth ENS not registered");require(authAddress==Resolver(authResolver).addr(authENSNodeHash),"Auth address is wrong");// Verify the TEXT record is appropriately set by authENS
// 验证 TEXT 记录是否由 authENS 适当设置
stringmemoryvaultText=Resolver(authResolver).text(authENSNodeHash,"eip5131:vault");require(keccak256(abi.encodePacked(authKey,":",_addressToString(mainAddress)))==keccak256(bytes(vaultText)),"Invalid auth text record");}bytes16privateconstant_HEX_SYMBOLS="0123456789abcdef";functionsha3HexAddress(addressaddr)privatepurereturns(bytes32ret){uint256value=uint256(uint160(addr));bytesmemorybuffer=newbytes(40);for(uint256i=39;i>1;--i){buffer[i]=_HEX_SYMBOLS[value&0xf];value>>=4;}returnkeccak256(buffer);}function_addressToString(addressaddr)privatepurereturns(stringmemoryptr){// solhint-disable-next-line no-inline-assembly
assembly{ptr:=mload(0x40)// Adjust mem ptr and keep 32 byte aligned
// 32 bytes to store string length; address is 42 bytes long
mstore(0x40,add(ptr,96))// Store (string length, '0', 'x') (42, 48, 120)
// Single write by offsetting across 32 byte boundary
ptr:=add(ptr,2)mstore(ptr,0x2a3078)// Write string backwards
for{// end is at 'x', ptr is at lsb char
letend:=add(ptr,31)ptr:=add(ptr,71)}gt(ptr,end){ptr:=sub(ptr,1)addr:=shr(4,addr)}{letv:=and(addr,0xf)// if > 9, use ascii 'a-f' (no conditional required)
v:=add(v,mul(gt(v,9),39))// Add ascii for '0'
v:=add(v,48)mstore8(ptr,v)}// return ptr to point to length (32 + 2 for '0x' - 1)
ptr:=sub(ptr,33)}returnstring(ptr);}}