xor777
December 13, 2023, 1:15pm
1
Hi!
I am trying to send an ATOM staking transaction for raw signing in the following way.
I have a formed transaction in json:
"messages":[
{
"typeUrl":"/cosmos.staking.v1beta1.MsgDelegate",
"value":{
"delegatorAddress": ...
"validatorAddress": ...
...
}
}
}
],
"fee":{
"amount":[
{
"amount":"6500",
"denom":"uatom"
}
],
"gas":"260000"
}
as well as its encoded version:
0a9e010a232f636f736d6f732e7374616b696e672e763162657461312e4d736744656c656761746512770a2d636f736d6f7331737339376b636a6c76656c7a386c7330327270327a6c706a64786735643068666c386b7633381234636f736d6f7376616c6f7065723133326a757a6b3067646d77757876783470687567376d33796d796174786c68393733346734771a100a057561746f6d120731303030303030
I am forming a request to create a transaction in fireblocks dev sandbox:
"extra ="{
"rawMessageData":{
"messages":[
{
"content" : tx["encodedBody"]
}
]
}
}
status, id = fireblocks.create_transaction(
tx_type = 'RAW',
asset_id = 'ATOM_COS_TEST',
source=TransferPeerPath(VAULT_ACCOUNT, "0"),
extra_parameters = extra
).values()
The transaction is sent, but when processing the transaction, an error message appears:
Too many characters
The raw signing transaction contains at least one message with over 32 bytes. Fireblocks does not allow a message with this length for this encryption protocol.
Can you please tell me what the problem is? Am I forming the request incorrectly, or do I need to pass different parameters in the content?
Hi @xor777 ,
My name is Alon and I am part of the Solutions Engineering team at Fireblocks.
For ATOM we recommend that you their @cosmjs SDK and their custom signer, like this:
import { encodeSecp256k1Signature } from "@cosmjs/amino";
import { AccountData, DirectSignResponse, makeSignBytes, OfflineDirectSigner } from "@cosmjs/proto-signing";
import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { FireblocksSDK, PeerType, TransactionArguments, TransactionOperation } from "fireblocks-sdk";
import { sha256 } from "js-sha256";
import {inspect} from 'util';
export default class ATOMFireblocksSigner implements OfflineDirectSigner{
private assetId: string;
private note: string;
constructor(private fbks: FireblocksSDK, private vaId: string, private testnet: boolean = false){
this.assetId = testnet ? "ATOM_COS_TEST" : "ATOM_COS";
this.note = undefined;
}
async getAccounts(): Promise<AccountData[]> {
const pubkey = await this.fbks.getPublicKeyInfoForVaultAccount({
assetId: this.assetId,
vaultAccountId: parseInt(this.vaId),
compressed: true,
change: 0,
addressIndex: 0
});
const addr = (await this.fbks.getDepositAddresses(this.vaId, this.assetId)).filter(x=>x.type==='Permanent')[0].address;
return new Promise((r)=>{
r(
[
{
address: addr,
pubkey: Uint8Array.from(Buffer.from(pubkey.publicKey, 'hex')),
algo: 'secp256k1',
}
]
)
});
}
async signDirect (signerAddress : string , signDoc :SignDoc){
// derived from directsecp256k1hdwallet code, important part is to hash the message and then sign it.
const signBytes = makeSignBytes(signDoc);
const msgHash = sha256.create().update(signBytes).hex()
let txPayload: TransactionArguments = {
assetId: this.assetId,
source: {
type: PeerType.VAULT_ACCOUNT,
id: `${this.vaId}`
},
operation: TransactionOperation.RAW,
extraParameters: {
rawMessageData: {
messages: [
{
content: msgHash
}
]
}
}
};
// Wait for TX to finish here
const pubkey = (await this.getAccounts())[0].pubkey;
const sig = encodeSecp256k1Signature(pubkey, Uint8Array.from(Buffer.from(tx.signedMessages![0].signature.fullSig, 'hex')));
this.note = undefined;
return {
signed: signDoc,
signature: sig
}
}
setNote(note: string){
this.note = note;
}
}
When it comes to usage within the ATOM SDK, this is the approach you can use:
const stargateClient = await SigningStargateClient.connectWithSigner(
rpcUrl,
signer,
undefined
);
Where rpcUrl is the RPC URL and the signer is an instance of ATOMFireblocksSigner
mentioned above.
Thanks,
Alon