Skip to content
105 changes: 105 additions & 0 deletions src/sdk/tx/account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { accountFromEthAccount, accountFromNibiru } from "./account"
import { EthAccount } from "src/protojs/eth/types/v1/account"
import { Any } from "src/protojs/google/protobuf/any"
import Long from "long"
import * as cosmjs from "@cosmjs/stargate"
import { decodeOptionalPubkey } from "@cosmjs/proto-signing"
import { BaseAccount } from "src/protojs/cosmos/auth/v1beta1/auth"

// Mock decodeOptionalPubkey
jest.mock("@cosmjs/proto-signing", () => ({
decodeOptionalPubkey: jest.fn(),
}))

const mockedDecodeOptionalPubkey = decodeOptionalPubkey as jest.Mock

describe("accountFromEthAccount", () => {
it("should throw an error if baseAccount is undefined", () => {
const baseAccount: BaseAccount = undefined as unknown as BaseAccount

expect(() => accountFromEthAccount(baseAccount)).toThrow()
})

it("should return a valid account when baseAccount is defined", () => {
const baseAccount: BaseAccount = {
address: "nibi1testaddress",
pubKey: {
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([1, 2, 3]),
},
accountNumber: Long.fromNumber(123),
sequence: Long.fromNumber(1),
}

mockedDecodeOptionalPubkey.mockReturnValue({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([1, 2, 3]),
})

const account = accountFromEthAccount(baseAccount)

expect(account.address).toBe("nibi1testaddress")
expect(account.pubkey).toEqual({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([1, 2, 3]),
})
expect(account.accountNumber).toEqual(123)
expect(account.sequence).toEqual(1)
})
})

describe("accountFromNibiru", () => {
it("should parse EthAccount typeUrl and return valid account", () => {
const input: Any = {
typeUrl: "/eth.types.v1.EthAccount",
value: EthAccount.encode({
baseAccount: {
address: "nibi1testaddress",
pubKey: {
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([4, 5, 6]),
},
accountNumber: Long.fromNumber(456),
sequence: Long.fromNumber(2),
},
codeHash: "",
}).finish(),
}

mockedDecodeOptionalPubkey.mockReturnValue({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([4, 5, 6]),
})

const account = accountFromNibiru(input)

expect(account.address).toBe("nibi1testaddress")
expect(account.pubkey).toEqual({
typeUrl: "/cosmos.crypto.secp256k1.PubKey",
value: new Uint8Array([4, 5, 6]),
})
expect(account.accountNumber).toEqual(456)
expect(account.sequence).toEqual(2)
})

it("should handle non-EthAccount typeUrl by calling accountFromAny", () => {
const mockAccountFromAny = jest
.spyOn(cosmjs, "accountFromAny")
.mockReturnValue({
address: "nibi1otheraddress",
pubkey: null,
accountNumber: 789,
sequence: 3,
})

const input: Any = {
typeUrl: "/other.types.v1.Account",
value: new Uint8Array([7, 8, 9]),
}

const account = accountFromNibiru(input)

expect(account.address).toBe("nibi1otheraddress")
expect(mockAccountFromAny).toHaveBeenCalledWith(input)
})
})
42 changes: 42 additions & 0 deletions src/sdk/tx/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { decodeOptionalPubkey } from "@cosmjs/proto-signing"
import { Account, accountFromAny, AccountParser } from "@cosmjs/stargate"
import { EthAccount } from "src/protojs/eth/types/v1/account"
import { Any } from "src/protojs/google/protobuf/any"
import { assert } from "@cosmjs/utils"
import { BaseAccount } from "src/protojs/cosmos/auth/v1beta1/auth"

/**
* Converts an EthAccount to a general Cosmos Account object.
*
* @param {EthAccount} ethAccount - The EthAccount object containing the account's base information.
* @returns {Account} The Cosmos account object.
*/
export const accountFromEthAccount = ({
address,
pubKey,
accountNumber,
sequence,
}: BaseAccount): Account => ({
address,
pubkey: decodeOptionalPubkey(pubKey),
accountNumber: accountNumber.toNumber(),
sequence: sequence.toNumber(),
})

/**
* Parses an account input into a Cosmos account. Handles both EthAccount and other standard accounts.
*
* @param {Any} input - The input account information, containing the typeUrl and value.
* @returns {Account} Parsed account object.
*/
export const accountFromNibiru: AccountParser = (input: Any): Account => {
const { typeUrl, value } = input

if (typeUrl === "/eth.types.v1.EthAccount") {
const baseAccount = EthAccount.decode(value).baseAccount
assert(baseAccount)
return accountFromEthAccount(baseAccount)
}

return accountFromAny(input)
}
2 changes: 2 additions & 0 deletions src/sdk/tx/txClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
setupWasmExtension,
} from "@cosmjs/cosmwasm-stargate"
import { NibiruExtensions, setupNibiruExtension } from ".."
import { accountFromNibiru } from "./account"

export const nibiruRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [
...defaultRegistryTypes,
Expand Down Expand Up @@ -69,6 +70,7 @@ export class NibiruTxClient extends SigningStargateClient {
registry: new Registry(nibiruRegistryTypes),
gasPrice: GasPrice.fromString("0.025unibi"),
broadcastPollIntervalMs: 1_000, // 1 second poll times
accountParser: accountFromNibiru,
...options,
},
wasmClient
Expand Down
Loading