Skip to content

Instantly share code, notes, and snippets.

@Pitasi
Created September 6, 2024 14:17
Show Gist options
  • Save Pitasi/15dde7839bb12a71776458cfdb543286 to your computer and use it in GitHub Desktop.
Save Pitasi/15dde7839bb12a71776458cfdb543286 to your computer and use it in GitHub Desktop.
[draft] signing Cosmos SDK tx using ethers.js (+ evmos)
import { makeAuthInfoBytes, makeSignDoc } from "@cosmjs/proto-signing";
import { toBech32 } from "@cosmjs/encoding";
import { Int53 } from "@cosmjs/math";
import { ethers } from "ethers";
import { ethermint, cosmos, google, warden, getSigningWardenClientOptions } from "@wardenprotocol/wardenjs";
const { Any } = google.protobuf;
const { TxBody, TxRaw, SignDoc } = cosmos.tx.v1beta1;
const { MsgNewSpace } = warden.warden.v1beta3;
const PubKey = ethermint.crypto.v1.ethsecp256k1.PubKey;
main();
async function main() {
// init an ethers wallet from mnemonic
const mnemonic = "surround miss nominee dream gap cross assault thank captain prosper drop duty group candy wealth weather scale put";
const ethWallet = ethers.Wallet.fromPhrase(mnemonic);
const ethAddress = ethWallet.address;
const wardenAddress = toBech32("warden", ethers.getBytes(ethAddress));
const pubkey = ethers.getBytes(ethWallet.publicKey);
console.log("Wallet:", ethAddress, wardenAddress);
// prepare the content of the transaction
const fee = {
amount: [{ denom: "award", amount: "100" }],
gas: "200000",
};
const txBody = TxBody.fromPartial({
messages: [{
typeUrl: MsgNewSpace.typeUrl,
value: MsgNewSpace.fromPartial({
creator: wardenAddress,
}),
}],
});
// bundle the transaction and sign it
const signDoc = await buildSignDoc("warden_1337-1", wardenAddress, pubkey, txBody, fee);
const signedTx = await signTransaction(ethWallet, signDoc);
// broadcast
const broadcastRes = await broadcastTx(signedTx);
console.log(broadcastRes);
}
async function buildSignDoc(chainId, wardenAddr, pubkey, txBody, fee) {
const account = await fetchAccount(wardenAddr);
const pubk = Any.fromPartial({
typeUrl: PubKey.typeUrl,
value: PubKey.encode({
key: pubkey,
}).finish(),
});
const txBodyEncodeObject = {
typeUrl: TxBody.typeUrl,
value: txBody,
};
const { registry } = getSigningWardenClientOptions();
const txBodyBytes = registry.encode(txBodyEncodeObject);
const gasLimit = Int53.fromString(fee.gas).toNumber();
const authInfoBytes = makeAuthInfoBytes(
[{ pubkey: pubk, sequence: account.sequence }],
fee.amount,
gasLimit,
fee.granter,
fee.payer,
);
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, Number(account.accountNumber));
return signDoc;
}
async function signTransaction(wallet, signDoc) {
const signDocBytes = SignDoc.encode(signDoc).finish();
const signatureRaw = wallet.signingKey.sign(ethers.keccak256(signDocBytes));
const signature = ethers.Signature.from(signatureRaw);
const signatureRS = ethers.concat([signature.r, signature.s])
const signatureRSBytes = ethers.getBytes(signatureRS);
const signedTx = TxRaw.encode(TxRaw.fromPartial({
authInfoBytes: signDoc.authInfoBytes,
bodyBytes: signDoc.bodyBytes,
signatures: [signatureRSBytes],
})).finish();
return signedTx;
}
async function fetchAccount(address) {
const client = await warden.ClientFactory.createRPCQueryClient({
rpcEndpoint: "http://localhost:26657",
})
const { account } = await client.cosmos.auth.v1beta1.account({ address });
if (!account) {
throw new Error("Failed to retrieve account from chain", account);
}
return account;
}
async function broadcastTx(bytes) {
const res = await fetch("http://localhost:1317/cosmos/tx/v1beta1/txs", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"tx_bytes": ethers.encodeBase64(bytes),
"mode": "BROADCAST_MODE_SYNC"
}),
})
const resJson = await res.json();
return resJson;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment