solidity 复杂列表参数签名研究学习

需要参与签名的solidity结构是这样的:

// 定义一个结构体来存储代币地址和数量
struct ReceiverInfo {
    address tokenAddress; // 代币合约地址
    uint256 amount; // 要分发的代币数量
}
function distributeHash(
        uint256 season,
        ReceiverInfo[] calldata receiverInfos,
        address to,
        uint256 uniqueId
    ) internal  returns (bytes32) {
 }

 想法就是把ReceiverInfo[] calldata receiverInfos这种结构整理成字符串然后参与签名,

当然还有其他更好的使用库的abi的相关方法,后续再研究了。

solidity这边签名实现:

function distributeHash(
        uint256 season,
        ReceiverInfo[] calldata receiverInfos,
        address to,
        uint256 uniqueId
    ) internal  returns (bytes32) {
        require(receiverInfos.length > 0, "invalid receiverInfos!");
        string memory receiverInfoStr = receiverInfosToString(receiverInfos);
        return
            _hashTypedDataV4(
                keccak256(
                    abi.encode(
                        keccak256(
                            "distribute(uint256 season,address to,uint256 uniqueId,string receiverInfos)"
                        ),
                        season,
                        to,
                        uniqueId,
                        keccak256(bytes(receiverInfoStr))
                    )
                )
            );
    }
    // 将 ReceiverInfo 数组转换为字符串的函数
    function receiverInfosToString(ReceiverInfo[] calldata receiverInfos) public pure returns (string memory) {
        string memory result = "";
        uint256 length = receiverInfos.length;
        
        for (uint256 i = 0; i < length; i++) {
            string memory tokenAddressStr = addressToString(receiverInfos[i].tokenAddress);
            string memory amountStr = uintToString(receiverInfos[i].amount);
            
            // 拼接每个结构体的字符串表示
            result = string(abi.encodePacked(result, tokenAddressStr, amountStr));
        }
        return result;
    }

签名的类型是:"distribute(uint256 season,address to,uint256 uniqueId,string receiverInfos)" 这里定义成string receiverinfos

所以进行了转换:把列表转成字符串,这样就简单了嘛,二维降成一维:

string memory receiverInfoStr = receiverInfosToString(receiverInfos);

 需要注意的是这里:

这里是因为go在处理签名的时候:

typedData.HashStruct(typedData.PrimaryType, typedData.Message) 
这里面的这个方法:
在是string的时候会先计算签名,然后采用签名去进一步签名(这里我整了好久)
然后贴一下go的签名处理:

func MedalSigner(
	privateKeyStr string,
	chainId int64,
	contract string,
	season int64,
	uniqueId int64,
	to string,
	receiverInfos []RewardDistributor.RewardDistributorReceiverInfo,
) (string, error) {
	receiverInfoStr := receiverInfosToString(receiverInfos) // 把列表转成字符串

	// 签名
	typedData := &apitypes.TypedData{
		Types: apitypes.Types{
			"EIP712Domain": {
				{Name: "name", Type: "string"},
				{Name: "version", Type: "string"},
				{Name: "chainId", Type: "uint256"},
				{Name: "verifyingContract", Type: "address"},
			},
			"distribute": {
				{Name: "season", Type: "uint256"},
				{Name: "to", Type: "address"},
				{Name: "uniqueId", Type: "uint256"},
				{Name: "receiverInfos", Type: "string"},  // 这里定义签名为字符串类型
			},
		},
		Domain: apitypes.TypedDataDomain{
			Name:              "RewardDistributor",
			Version:           "1.0.0",
			ChainId:           math.NewHexOrDecimal256(chainId),
			VerifyingContract: contract,
			Salt:              "",
		},
		Message: map[string]interface{}{
			"season":        math.NewHexOrDecimal256(season),
			"to":            to,
			"uniqueId":      math.NewHexOrDecimal256(uniqueId),
			"receiverInfos": receiverInfoStr,
		},
		PrimaryType: "distribute",
	}

	privateKey, err := crypto.HexToECDSA(privateKeyStr)
	if err != nil {
		return "", err
	}

	signature, err := SignWithEip712(privateKey, typedData)
	if err != nil {
		return "", err
	}
	return hexutil.Encode(signature), nil
}

// receiverInfosToString 函数
func receiverInfosToString(receiverInfos []RewardDistributor.RewardDistributorReceiverInfo) string {
	var builder strings.Builder
	builder.WriteString("")

	for _, info := range receiverInfos {
		// 地址统一采用小写编码
		addressLower := strings.ToLower(info.TokenAddress.Hex())
		builder.WriteString(fmt.Sprintf("%s%d", addressLower, info.Amount))
	}

	s := builder.String()
	fmt.Println("receiverInfosToString: ", s)
	return s
}

然后就可以正常签名了。

posted @ 2024-06-19 00:44  若-飞  阅读(42)  评论(0)    收藏  举报