Javascript
Using Javascript/node.js to sign a transaction

Encode and Sign Raw Message

The parameters should consist with your account information. The accountNumber and sequence could be found by querying LCD /cosmos/auth/v1beta1/accounts/{fromAddress} endpoint.
1
const secp256k1 = require('secp256k1');
2
const createHash = require('create-hash');
3
const Long = require('long');
4
const { MsgSend } = require("./build/codec/cosmos/bank/v1beta1/tx");
5
const { TxBody, AuthInfo, SignDoc, TxRaw } = require("./build/codec/cosmos/tx/v1beta1/tx");
6
const { PubKey } = require("./build/codec/cosmos/crypto/secp256k1/keys");
7
8
// define parameters
9
const chainId = "likechain-local-testnet";
10
const privateKey = "69b4e47d3aa61ad6184493529cd0feb0d2dfb55ea31aa9799af42607de3cd1a9";
11
const publicKey = "A4Fj1Y4k77Qaxuy496CHYB2rpfWXkM3LCnlyrU8eKbH7";
12
const accountNumber = 2;
13
const fromAddress = "cosmos1lsagfzrm4gz28he4wunt63sts5xzmczw8pkek3";
14
const toAddress = "cosmos1mnyn7x24xj6vraxeeq56dfkxa009tvhgknhm04";
15
const tokenAmount = 1000000;
16
const memo = "Enjoy your money";
17
const gasLimit = 100000;
18
const sequence = 17;
19
20
const messages = [{
21
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
22
value: {
23
fromAddress,
24
toAddress,
25
amount: [
26
{
27
denom: "nanolike",
28
amount: tokenAmount.toString(),
29
},
30
],
31
}
32
}];
33
34
const wrappedMessages = messages.map(msg => {
35
return {
36
typeUrl: msg.typeUrl,
37
value: MsgSend.encode(msg.value).finish(),
38
}
39
})
40
41
const body = {
42
typeUrl: "/cosmos.tx.v1beta1.TxBody",
43
value: {
44
memo,
45
messages: wrappedMessages,
46
timeoutHeight: Long.UZERO,
47
extensionOptions: [],
48
nonCriticalExtensionOptions: [],
49
},
50
}
51
const bodyBytes = TxBody.encode(body.value).finish();
52
53
const pubkeyBytes = PubKey.encode({ key: publicKey }).finish();
54
55
const authInfo = {
56
signerInfos: [
57
{
58
sequence: Long.fromNumber(sequence),
59
publicKey: {
60
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
61
value: pubkeyBytes,
62
},
63
modeInfo: {
64
single: {
65
mode: 1,
66
},
67
},
68
},
69
],
70
fee: {
71
gasLimit: Long.fromNumber(gasLimit),
72
payer: "",
73
granter: "",
74
amount: [
75
{
76
denom: "nanolike",
77
amount: "0",
78
},
79
],
80
},
81
}
82
83
const authInfoBytes = AuthInfo.encode(authInfo).finish();
84
const signDoc = {
85
bodyBytes,
86
authInfoBytes,
87
chainId,
88
accountNumber: Long.fromNumber(accountNumber),
89
}
90
const signBytes = SignDoc.encode(signDoc).finish();
91
92
const privkeyBytes = Buffer.from(privateKey, 'hex')
93
94
const sign = (msg, privateKey) => {
95
const msgSha256 = createHash('sha256');
96
msgSha256.update(msg);
97
const msgHash = msgSha256.digest();
98
const { signature: signatureArr } = secp256k1.ecdsaSign(msgHash, privateKey);
99
const signature = Buffer.from(signatureArr)
100
return signature;
101
}
102
const signatureBytes = sign(signBytes, privkeyBytes);
103
104
const tx = {
105
bodyBytes,
106
authInfoBytes,
107
signatures: [signatureBytes],
108
}
109
110
const txBytes = TxRaw.encode(tx).finish();
111
112
console.log("signature_bytes:", signatureBytes.toString('base64'));
113
console.log("sign_bytes:", signBytes.toString('base64'));
114
console.log("tx_bytes:", txBytes.toString('base64'));
115
116
// signature_bytes: zIfF132OINwGz0psHn+nxeYVQdHZXiqO/94qaXrowcRFEL2jA0qFarz22VBXYXQudOV6y0BL84/m475awNkiFA==
117
// sign_bytes: CqgBCpMBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnMKLWNvc21vczFsc2FnZnpybTRnejI4aGU0d3VudDYzc3RzNXh6bWN6dzhwa2VrMxItY29zbW9zMW1ueW43eDI0eGo2dnJheGVlcTU2ZGZreGEwMDl0dmhna25obTA0GhMKCG5hbm9saWtlEgcxMDAwMDAwEhBFbmpveSB5b3VyIG1vbmV5EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOBY9WOJO+0GsbsuPegh2Adq6X1l5DNywp5cq1PHimx+xIECgIIARgREhMKDQoIbmFub2xpa2USATAQoI0GGhdsaWtlY2hhaW4tbG9jYWwtdGVzdG5ldCAC
118
// tx_bytes: CqgBCpMBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnMKLWNvc21vczFsc2FnZnpybTRnejI4aGU0d3VudDYzc3RzNXh6bWN6dzhwa2VrMxItY29zbW9zMW1ueW43eDI0eGo2dnJheGVlcTU2ZGZreGEwMDl0dmhna25obTA0GhMKCG5hbm9saWtlEgcxMDAwMDAwEhBFbmpveSB5b3VyIG1vbmV5EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOBY9WOJO+0GsbsuPegh2Adq6X1l5DNywp5cq1PHimx+xIECgIIARgREhMKDQoIbmFub2xpa2USATAQoI0GGkDMh8XXfY4g3AbPSmwef6fF5hVB0dleKo7/3ippeujBxEUQvaMDSoVqvPbZUFdhdC505XrLQEvzj+bjvlrA2SIU
Copied!

Commit a Transaction

Put the tx_bytes from above step in the JSON request body as below, and post the LCD/cosmos/tx/v1beta1/txs endpoint.
1
{
2
"tx_bytes": "Your tx bytes here",
3
"mode": "BROADCAST_MODE_SYNC"
4
}
Copied!
For details of the POST /txs and other chain RPC API, please refer to the LikeCoin chain RPC API section.

Verify Transaction Signature

1
const secp256k1 = require('secp256k1');
2
const createHash = require('create-hash');
3
4
const publicKey = Buffer.from("A4Fj1Y4k77Qaxuy496CHYB2rpfWXkM3LCnlyrU8eKbH7", "base64");
5
const signature = Buffer.from("zIfF132OINwGz0psHn+nxeYVQdHZXiqO/94qaXrowcRFEL2jA0qFarz22VBXYXQudOV6y0BL84/m475awNkiFA==", "base64");
6
const signBytes = Buffer.from("CqgBCpMBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnMKLWNvc21vczFsc2FnZnpybTRnejI4aGU0d3VudDYzc3RzNXh6bWN6dzhwa2VrMxItY29zbW9zMW1ueW43eDI0eGo2dnJheGVlcTU2ZGZreGEwMDl0dmhna25obTA0GhMKCG5hbm9saWtlEgcxMDAwMDAwEhBFbmpveSB5b3VyIG1vbmV5EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOBY9WOJO+0GsbsuPegh2Adq6X1l5DNywp5cq1PHimx+xIECgIIARgREhMKDQoIbmFub2xpa2USATAQoI0GGhdsaWtlY2hhaW4tbG9jYWwtdGVzdG5ldCAC", "base64");
7
const msgSha256 = createHash('sha256');
8
msgSha256.update(signBytes);
9
const msgHash = msgSha256.digest();
10
console.log(secp256k1.ecdsaVerify(signature, msgHash, publicKey));
11
12
// true
Copied!