Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: account parser #374

Merged
merged 12 commits into from
Oct 9, 2024
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"
CalicoNino marked this conversation as resolved.
Show resolved Hide resolved
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