Signature Description
Signature Mechanism
This system uses the ECDSA signature algorithm to verify the authenticity of requests.
Signature Process
-
Parameter Collection – Gather all fields that need to be signed.
-
Key Sorting – Sort the parameter keys in alphabetical order (a–z).
-
Value Processing – Process the values according to the sorted keys:
- Skip
nullorundefinedvalues. - Array values: expand all non-empty elements and convert them to strings.
- Other values: convert directly to strings.
- Skip
-
Message Construction – Concatenate all processed values into a string, separated by commas.
-
Hash Calculation – Compute the message hash using
keccak256. -
Signature Generation – Sign the hash with the private key using ECDSA.
-
Format Conversion – Convert the signature into
{r, s, v}format (decimal strings).
Parameter Mapping Format
Token Creation Parameters
{
"decimals": 8,
"masterAuthority": "0x...",
"name": "My Token",
"nonce": 0,
"recentCheckpoint": 12345,
"symbol": "MTK"
}Dynamic Call Parameters
{
"methodArgs": ["0x1234567890123456789012345678901234567890", "1000000000000000000"],
"nonce": 1,
"recentCheckpoint": 12345,
"token": "0x..."
}Note: When calculating the signature, the methodArgs array is expanded, and each element participates in the message construction as an individual value.
Example
Go signature example:
// Global configuration
var (
baseURL = "http://localhost:8545"
privateKey = ""
client = &http.Client{Timeout: 30 * time.Second}
)
// Configure API endpoint and private key
func Config(url, key string) {
baseURL = url
privateKey = key
}
// Signature structure
type Signature struct {
R string `json:"r"`
S string `json:"s"`
V string `json:"v"`
}
// Response handler (supports method chaining)
type ResponseHandler[T any] struct {
data T
err error
}
func (r *ResponseHandler[T]) Success(callback func(T)) *ResponseHandler[T] {
if r.err == nil {
callback(r.data)
}
return r
}
func (r *ResponseHandler[T]) Error(callback func(error)) *ResponseHandler[T] {
if r.err != nil {
callback(r.err)
}
return r
}
// Build a sorted message (supports array parameter handling)
func buildSortedMessage(params map[string]interface{}) string {
keys := make([]string, 0, len(params))
for key := range params {
keys = append(keys, key)
}
sort.Strings(keys) // a-z
var messageParts []string
for _, key := range keys {
value := params[key]
if value == nil {
continue
}
if slice, ok := value.([]interface{}); ok {
if len(slice) > 0 {
for _, element := range slice {
if element != nil {
messageParts = append(messageParts, fmt.Sprintf("%v", element))
}
}
}
} else {
formattedValue := fmt.Sprintf("%v", value)
messageParts = append(messageParts, formattedValue)
}
}
return strings.Join(messageParts, ",")
}
// Unified signature method (using the global private key)
func generateSignature(params map[string]interface{}) (*Signature, error) {
// Remove 0x prefix
privateKeyHex := privateKey
if len(privateKeyHex) > 2 && privateKeyHex[:2] == "0x" {
privateKeyHex = privateKeyHex[2:]
}
// Parse private key
privKey, err := crypto.HexToECDSA(privateKeyHex)
if err != nil {
return nil, fmt.Errorf("invalid private key: %w", err)
}
// Build message string (keys sorted a-z)
message := buildSortedMessage(params)
// Compute message hash
hash := crypto.Keccak256Hash([]byte(message))
// Sign the hash
signature, err := crypto.Sign(hash.Bytes(), privKey)
if err != nil {
return nil, fmt.Errorf("sign error: %w", err)
}
// Extract r, s, v values
r := new(big.Int).SetBytes(signature[:32])
s := new(big.Int).SetBytes(signature[32:64])
v := new(big.Int).SetBytes([]byte{signature[64] + 27}) // Add 27 is Ethereum convention
return &Signature{
R: r.String(),
S: s.String(),
V: v.String(),
}, nil
}
// Example usage
func main() {
// Configure private key
Config("http://localhost:8545", "1234567891234567899")
// Token creation
createTokenParams := map[string]interface{}{
"decimals": 8,
"masterAuthority": "0xa6459EF31C68DCF46cC603C526526DB1C6eE4fD1",
"name": "My Token",
"nonce": 0,
"recentCheckpoint": 12345,
"symbol": "MTK",
}
createTokenSignature, err := generateSignature(createTokenParams)
if err != nil {
panic(err)
}
// Dynamic call (includes array parameters)
mintParams := map[string]interface{}{
"methodArgs": []interface{}{"0x1234567890123456789012345678901234567890", "1000000000000000000"},
"nonce": 1,
"recentCheckpoint": 12346,
"token": "0x1234567890123456789012345678901234567890",
}
mintSignature, err := generateSignature(mintParams)
if err != nil {
panic(err)
}
// Example API call
result := CreateToken("My Token", "MTK", 8, "0xa6459EF31C68DCF46cC603C526526DB1C6eE4fD1")
result.Success(func(data *TokenIssueResult) {
fmt.Printf("Token created: %s, Hash: %s\n", data.Token, data.Hash)
}).Error(func(err error) {
fmt.Printf("Error: %v\n", err)
})
}
JavaScript signature example:
// Dependency installation
npm install ethers
// Code example
const { ethers } = require('ethers');
// Global configuration
let config = {
baseURL: 'http://localhost:8545',
privateKey: ''
};
// Configure API endpoint and private key
function Config(url, key) {
config.baseURL = url;
config.privateKey = key;
}
// Unified signature method (supports array parameter handling)
function generateSignature(params) {
const sortedKeys = Object.keys(params).sort();
const messageParts = [];
for (const key of sortedKeys) {
const value = params[key];
if (value == null) {
continue;
}
if (Array.isArray(value)) {
if (value.length > 0) {
const arrayElements = value
.filter(element => element != null)
.map(element => String(element));
messageParts.push(...arrayElements);
}
} else {
const formattedValue = String(value);
messageParts.push(formattedValue);
}
}
const message = messageParts.join(',');
const messageBytes = ethers.toUtf8Bytes(message);
const messageHash = ethers.keccak256(messageBytes);
// Ensure the private key has 0x prefix
let privateKey = config.privateKey;
if (!privateKey.startsWith('0x')) {
privateKey = '0x' + privateKey;
}
const wallet = new ethers.Wallet(privateKey);
const signature = wallet.signingKey.sign(messageHash);
return {
r: ethers.getBigInt(signature.r).toString(),
s: ethers.getBigInt(signature.s).toString(),
v: signature.v.toString()
};
}
// Create response object (supports method chaining)
function createResponse(data, error) {
return {
success: function(callback) {
if (!error && data) {
callback(data);
}
return this;
},
error: function(callback) {
if (error) {
callback(error);
}
return this;
}
};
}
// Example usage - Token creation
const createTokenParams = {
decimals: 8,
masterAuthority: "0xa6459EF31C68DCF46cC603C526526DB1C6eE4fD1",
name: "My Token",
nonce: 0,
recentCheckpoint: 12345,
symbol: "MTK"
};
const createTokenSignature = generateSignature(createTokenParams);
// Example usage - Dynamic call (includes array parameters)
const mintParams = {
methodArgs: ["0x1234567890123456789012345678901234567890", "1000000000000000000"],
nonce: 1,
recentCheckpoint: 12346,
token: "0x1234567890123456789012345678901234567890"
};
const mintSignature = generateSignature(mintParams);
// Actual API call example
async function CreateToken(name, symbol, decimals, masterAuthority) {
try {
const blockNum = await getBlockNumber();
const nonce = 0;
const params = {
decimals: decimals,
masterAuthority: masterAuthority,
name: name,
nonce: nonce,
recentCheckpoint: blockNum,
symbol: symbol
};
const signature = generateSignature(params);
const reqParams = {
decimals: decimals,
masterAuthority: masterAuthority,
name: name,
symbol: symbol,
nonce: nonce,
recentCheckpoint: blockNum,
signature: signature
};
const result = await rpcCall("create_token", reqParams);
return createResponse(result, null);
} catch (error) {
return createResponse(null, error);
}
}
Updated about 1 month ago
