Signature Description


Signature Mechanism

This system uses the ECDSA signature algorithm to verify the authenticity of requests.

Signature Process

  1. Parameter Collection – Gather all fields that need to be signed.

  2. Key Sorting – Sort the parameter keys in alphabetical order (a–z).

  3. Value Processing – Process the values according to the sorted keys:

    • Skip null or undefined values.
    • Array values: expand all non-empty elements and convert them to strings.
    • Other values: convert directly to strings.
  4. Message Construction – Concatenate all processed values into a string, separated by commas.

  5. Hash Calculation – Compute the message hash using keccak256.

  6. Signature Generation – Sign the hash with the private key using ECDSA.

  7. 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);
    }
}