From 79d6ac348ff9aad40051c5fc5ee1f0051106aeda Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Thu, 6 Jul 2023 17:11:07 +0200 Subject: [PATCH] refactor: replace borsh-js schemas with borsh.ts --- apps/extension/package.json | 2 +- .../src/background/approvals/service.ts | 10 +- .../src/background/keyring/keyring.ts | 9 +- apps/extension/src/provider/Anoma.test.ts | 21 ++-- apps/extension/src/provider/Signer.ts | 22 ++-- apps/extension/tsconfig.json | 23 ++-- .../slices/StakingAndGovernance/actions.ts | 2 + apps/namada-interface/src/slices/transfers.ts | 7 ++ apps/namada-interface/tsconfig.json | 23 ++-- package.json | 2 +- packages/crypto/tsconfig.json | 19 ++-- packages/integrations/src/Anoma.ts | 7 +- packages/integrations/tsconfig.json | 19 ++-- packages/shared/lib/src/sdk/tx.rs | 22 ++-- packages/types/package.json | 2 +- packages/types/src/tx/messages/index.ts | 29 ++--- packages/types/src/tx/schema/account.ts | 27 ++--- packages/types/src/tx/schema/bond.ts | 58 ++++------ packages/types/src/tx/schema/ibcTransfer.ts | 101 ++++++------------ packages/types/src/tx/schema/index.ts | 14 ++- packages/types/src/tx/schema/shielded.ts | 86 --------------- packages/types/src/tx/schema/transfer.ts | 79 +++++--------- packages/types/src/tx/schema/tx.ts | 56 ++++------ packages/types/src/tx/schema/unbond.ts | 50 ++++----- packages/types/src/tx/schema/utils.ts | 12 +++ packages/types/src/tx/types.ts | 10 +- packages/types/tsconfig.json | 19 ++-- packages/utils/src/helpers/index.ts | 68 ------------ yarn.lock | 31 ++---- 29 files changed, 283 insertions(+), 547 deletions(-) delete mode 100644 packages/types/src/tx/schema/shielded.ts create mode 100644 packages/types/src/tx/schema/utils.ts diff --git a/apps/extension/package.json b/apps/extension/package.json index 452853121..9ed075975 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -39,7 +39,7 @@ "@ledgerhq/hw-transport-webusb": "^6.27.14", "@zondax/ledger-namada": "^0.0.2", "bignumber.js": "^9.1.1", - "borsh": "^0.7.0", + "@dao-xyz/borsh": "^5.1.5", "buffer": "^6.0.3", "dompurify": "^3.0.2", "framer-motion": "6.2.4", diff --git a/apps/extension/src/background/approvals/service.ts b/apps/extension/src/background/approvals/service.ts index 0927155eb..17c358c7e 100644 --- a/apps/extension/src/background/approvals/service.ts +++ b/apps/extension/src/background/approvals/service.ts @@ -1,15 +1,15 @@ import browser from "webextension-polyfill"; import { fromBase64 } from "@cosmjs/encoding"; -import { deserialize } from "borsh"; import { v4 as uuid } from "uuid"; import BigNumber from "bignumber.js"; -import { SubmitTransferMsgSchema, TransferMsgValue } from "@anoma/types"; +import { TransferMsgValue } from "@anoma/types"; import { KVStore } from "@anoma/storage"; import { ExtensionRequester } from "extension"; import { KeyRingService } from "background/keyring"; import { TabStore } from "background/keyring"; +import { deserialize } from "@dao-xyz/borsh"; export class ApprovalsService { constructor( @@ -27,11 +27,7 @@ export class ApprovalsService { this.txStore.set(id, txMsg); // Decode tx details and launch approval screen - const txDetails = deserialize( - SubmitTransferMsgSchema, - TransferMsgValue, - txMsgBuffer - ); + const txDetails = deserialize(txMsgBuffer, TransferMsgValue); const { source, target, token, amount: amountBN } = txDetails; const amount = new BigNumber(amountBN.toString()); const url = `${browser.runtime.getURL( diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 34311cf75..7de229502 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -22,7 +22,6 @@ import { AccountType, Bip44Path, DerivedAccount, - SubmitTransferMsgSchema, TransferMsgValue, } from "@anoma/types"; import { chains } from "@anoma/chains"; @@ -39,8 +38,8 @@ import { readVecStringPointer, readStringPointer, } from "@anoma/crypto/src/utils"; -import { deserialize } from "borsh"; import { Result } from "@anoma/utils"; +import { deserialize } from "@dao-xyz/borsh"; // Generated UUID namespace for uuid v5 const UUID_NAMESPACE = "9bfceade-37fe-11ed-acc0-a3da3461b38c"; @@ -686,11 +685,7 @@ export class KeyRing { // We need to get the source address in case it is shielded one, so we can // decrypt the extended spending key for a transfer. - const { source } = deserialize( - SubmitTransferMsgSchema, - TransferMsgValue, - Buffer.from(txMsg) - ); + const { source } = deserialize(Buffer.from(txMsg), TransferMsgValue); const account = await this._keyStore.getRecord("address", source); if (!account) { diff --git a/apps/extension/src/provider/Anoma.test.ts b/apps/extension/src/provider/Anoma.test.ts index 7175841b8..211e81a50 100644 --- a/apps/extension/src/provider/Anoma.test.ts +++ b/apps/extension/src/provider/Anoma.test.ts @@ -3,13 +3,10 @@ import { toBase64 } from "@cosmjs/encoding"; import BigNumber from "bignumber.js"; import { - AccountMsgSchema, AccountMsgValue, - SubmitIbcTransferMsgSchema, IbcTransferMsgValue, IbcTransferProps, Message, - SubmitTransferMsgSchema, TransferMsgValue, TransferProps, Chain, @@ -96,6 +93,7 @@ describe("Anoma", () => { feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId: chain.chainId, + publicKey: undefined, }, source: keyStore[0].address, target: @@ -103,15 +101,13 @@ describe("Anoma", () => { token, amount: new BigNumber(1000), nativeToken: token, + subPrefix: undefined, }; const transferMsgValue = new TransferMsgValue(transferProps); const transferMessage = new Message(); - const serializedTransfer = transferMessage.encode( - SubmitTransferMsgSchema, - transferMsgValue - ); + const serializedTransfer = transferMessage.encode(transferMsgValue); jest.spyOn(keyRingService, "submitTransfer"); anoma.submitTransfer(toBase64(serializedTransfer)); @@ -132,6 +128,7 @@ describe("Anoma", () => { feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId: chain.chainId, + publicKey: undefined, }, source: keyStore[0].address, receiver: @@ -140,15 +137,15 @@ describe("Anoma", () => { amount: new BigNumber(1000), portId: "transfer", channelId: "channel-0", + subPrefix: undefined, + timeoutHeight: undefined, + timeoutSecOffset: undefined, }; const transferMsgValue = new IbcTransferMsgValue(transferProps); const transferMessage = new Message(); - const serializedTransfer = transferMessage.encode( - SubmitIbcTransferMsgSchema, - transferMsgValue - ); + const serializedTransfer = transferMessage.encode(transferMsgValue); const res = anoma.submitIbcTransfer(toBase64(serializedTransfer)); @@ -162,7 +159,7 @@ describe("Anoma", () => { vpCode: new Uint8Array(), }); const accountMessage = new Message(); - const serialized = accountMessage.encode(AccountMsgSchema, accountMsgValue); + const serialized = accountMessage.encode(accountMsgValue); await expect( anoma.encodeInitAccount({ diff --git a/apps/extension/src/provider/Signer.ts b/apps/extension/src/provider/Signer.ts index 0f877f672..36c0f3132 100644 --- a/apps/extension/src/provider/Signer.ts +++ b/apps/extension/src/provider/Signer.ts @@ -3,8 +3,6 @@ import { Account, Anoma, AccountMsgValue, - AccountMsgSchema, - SubmitIbcTransferMsgSchema, IbcTransferMsgValue, IbcTransferProps, InitAccountProps, @@ -15,10 +13,7 @@ import { AccountType, SubmitBondProps, SubmitBondMsgValue, - SubmitBondMsgSchema, - SubmitTransferMsgSchema, SubmitUnbondMsgValue, - SubmitUnbondMsgSchema, } from "@anoma/types"; export class Signer implements ISigner { @@ -45,7 +40,7 @@ export class Signer implements ISigner { const msgValue = new SubmitBondMsgValue(args); const msg = new Message(); - const encoded = msg.encode(SubmitBondMsgSchema, msgValue); + const encoded = msg.encode(msgValue); return await this._anoma.submitBond(toBase64(encoded)); } @@ -57,7 +52,7 @@ export class Signer implements ISigner { const msgValue = new SubmitUnbondMsgValue(args); const msg = new Message(); - const encoded = msg.encode(SubmitUnbondMsgSchema, msgValue); + const encoded = msg.encode(msgValue); return await this._anoma.submitUnbond(toBase64(encoded)); } @@ -68,10 +63,7 @@ export class Signer implements ISigner { public async submitTransfer(args: TransferProps): Promise { const transferMsgValue = new TransferMsgValue(args); const transferMessage = new Message(); - const serializedTransfer = transferMessage.encode( - SubmitTransferMsgSchema, - transferMsgValue - ); + const serializedTransfer = transferMessage.encode(transferMsgValue); return await this._anoma.submitTransfer(toBase64(serializedTransfer)); } @@ -82,10 +74,8 @@ export class Signer implements ISigner { public async submitIbcTransfer(args: IbcTransferProps): Promise { const ibcTransferMsgValue = new IbcTransferMsgValue(args); const ibcTransferMessage = new Message(); - const serializedIbcTransfer = ibcTransferMessage.encode( - SubmitIbcTransferMsgSchema, - ibcTransferMsgValue - ); + const serializedIbcTransfer = + ibcTransferMessage.encode(ibcTransferMsgValue); return await this._anoma.submitIbcTransfer(toBase64(serializedIbcTransfer)); } @@ -102,7 +92,7 @@ export class Signer implements ISigner { vpCode, }); const accountMessage = new Message(); - const serialized = accountMessage.encode(AccountMsgSchema, accountMsgValue); + const serialized = accountMessage.encode(accountMsgValue); return await this._anoma.encodeInitAccount({ txMsg: toBase64(serialized), diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json index 75d2b1435..e36d2b612 100644 --- a/apps/extension/tsconfig.json +++ b/apps/extension/tsconfig.json @@ -1,22 +1,23 @@ { "compilerOptions": { - "baseUrl": "./src", - "target": "es2015", - "lib": ["dom", "dom.iterable", "esnext", "webworker"], "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "baseUrl": "./src", + "esModuleInterop": true, + "esModuleInterop": true, + "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["dom", "dom.iterable", "esnext", "webworker"], "module": "esnext", "moduleResolution": "node", - "resolveJsonModule": true, - "esModuleInterop": true, - "isolatedModules": true, "noEmit": false, - "jsx": "react-jsx" + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es2015" }, "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": ["node_modules"], diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts index 5e0a83e68..49c66e2b7 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts @@ -171,6 +171,7 @@ export const postNewBonding = createAsyncThunk< feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId, + publicKey: undefined, }, }); }); @@ -197,6 +198,7 @@ export const postNewUnbonding = createAsyncThunk< feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId, + publicKey: undefined, }, }); }); diff --git a/apps/namada-interface/src/slices/transfers.ts b/apps/namada-interface/src/slices/transfers.ts index 75851a928..9fc339f3f 100644 --- a/apps/namada-interface/src/slices/transfers.ts +++ b/apps/namada-interface/src/slices/transfers.ts @@ -161,12 +161,14 @@ export const submitTransferTransaction = createAsyncThunk< feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId, + publicKey: undefined, }, source: txTransferArgs.account.address, target: txTransferArgs.target, token: Tokens.NAM.address || "", amount: txTransferArgs.amount, nativeToken: Tokens.NAM.address || "", + subPrefix: undefined, }); } ); @@ -194,6 +196,7 @@ export const submitIbcTransferTransaction = createAsyncThunk< feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId, + publicKey: undefined, }, source: txIbcTransferArgs.account.address, receiver: txIbcTransferArgs.target, @@ -201,6 +204,9 @@ export const submitIbcTransferTransaction = createAsyncThunk< amount: txIbcTransferArgs.amount, portId: txIbcTransferArgs.portId, channelId: txIbcTransferArgs.channelId, + subPrefix: undefined, + timeoutHeight: undefined, + timeoutSecOffset: undefined, }, }); @@ -237,6 +243,7 @@ export const submitBridgeTransferTransaction = createAsyncThunk< feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), chainId, + publicKey: undefined, }, source: txBridgeTransferArgs.account.address, target: txBridgeTransferArgs.target, diff --git a/apps/namada-interface/tsconfig.json b/apps/namada-interface/tsconfig.json index 97eea5810..686304fb4 100644 --- a/apps/namada-interface/tsconfig.json +++ b/apps/namada-interface/tsconfig.json @@ -1,22 +1,23 @@ { "compilerOptions": { - "baseUrl": "./src", - "target": "es2015", - "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "baseUrl": "./src", + "esModuleInterop": true, + "esModuleInterop": true, + "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "moduleResolution": "node", - "resolveJsonModule": true, - "esModuleInterop": true, - "isolatedModules": true, "noEmit": false, - "jsx": "react-jsx" + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es2015" }, "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": ["node_modules"], diff --git a/package.json b/package.json index c581f97da..f87184556 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@cosmjs/json-rpc": "^0.27.1", "@cosmjs/tendermint-rpc": "^0.27.1", "bn.js": "^5.2.0", - "borsh": "^0.7.0", + "@dao-xyz/borsh": "^5.1.5", "bs58": "^5.0.0", "buffer": "^6.0.3", "typescript": "^5.1.3", diff --git a/packages/crypto/tsconfig.json b/packages/crypto/tsconfig.json index c9d5f74da..b3d0954ff 100644 --- a/packages/crypto/tsconfig.json +++ b/packages/crypto/tsconfig.json @@ -1,20 +1,21 @@ { "compilerOptions": { - "baseUrl": "./src", - "target": "es2015", - "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "baseUrl": "./src", + "esModuleInterop": true, + "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, "noEmit": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es2015", "types": ["node", "jest"] }, "include": ["src"], diff --git a/packages/integrations/src/Anoma.ts b/packages/integrations/src/Anoma.ts index 5844c45bb..31197c790 100644 --- a/packages/integrations/src/Anoma.ts +++ b/packages/integrations/src/Anoma.ts @@ -60,13 +60,17 @@ export default class Anoma implements Integration { token: Tokens.NAM.address || "", feeAmount: new BigNumber(0), gasLimit: new BigNumber(0), + publicKey: undefined, }, + subPrefix: undefined, source, receiver, channelId, portId, token: tokenAddress || "", amount, + timeoutHeight: undefined, + timeoutSecOffset: undefined, }); } else if (props.bridgeProps) { console.log("TODO: Implement Ethereum Bridge transfer"); @@ -81,7 +85,8 @@ export default class Anoma implements Integration { const tokenBalances = Object.keys(Tokens).map((tokenType: string) => { const { address: tokenAddress = "" } = Tokens[tokenType as TokenType]; const amount = - balance.find(({ token }) => token === tokenAddress)?.amount || new BigNumber(0); + balance.find(({ token }) => token === tokenAddress)?.amount || + new BigNumber(0); // TODO: Implement balance fetching via SDK return { diff --git a/packages/integrations/tsconfig.json b/packages/integrations/tsconfig.json index 957f6baec..ad3196ae9 100644 --- a/packages/integrations/tsconfig.json +++ b/packages/integrations/tsconfig.json @@ -1,20 +1,21 @@ { "compilerOptions": { - "baseUrl": "./src", - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "baseUrl": "./src", + "esModuleInterop": true, + "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, "noEmit": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es5", "types": ["node", "jest"] }, "include": ["src"], diff --git a/packages/shared/lib/src/sdk/tx.rs b/packages/shared/lib/src/sdk/tx.rs index 9a44c29db..01887dddf 100644 --- a/packages/shared/lib/src/sdk/tx.rs +++ b/packages/shared/lib/src/sdk/tx.rs @@ -19,8 +19,8 @@ use wasm_bindgen::JsError; #[derive(BorshSerialize, BorshDeserialize)] pub struct TxMsg { token: String, - fee_amount: u64, - gas_limit: u64, + fee_amount: String, + gas_limit: String, chain_id: String, public_key: Option, } @@ -29,7 +29,7 @@ pub struct TxMsg { pub struct SubmitBondMsg { source: String, validator: String, - amount: u64, + amount: String, native_token: String, tx: TxMsg, } @@ -59,7 +59,7 @@ pub fn bond_tx_args(tx_msg: &[u8], password: Option) -> Result) -> Result) -> Result, - amount: u64, + amount: String, native_token: String, } @@ -205,7 +205,7 @@ pub struct SubmitIbcTransferMsg { receiver: String, token: String, sub_prefix: Option, - amount: u64, + amount: String, port_id: String, channel_id: String, timeout_height: Option, @@ -243,7 +243,7 @@ pub fn ibc_transfer_tx_args( let source = Address::from_str(&source)?; let token = Address::from_str(&token)?; - let amount = Amount::from(amount); + let amount = Amount::from_string_precise(&amount)?; let port_id = PortId::from_str(&port_id).expect("Port id to be valid"); let channel_id = ChannelId::from_str(&channel_id).expect("Channel id to be valid"); @@ -285,14 +285,14 @@ fn tx_msg_into_args(tx_msg: TxMsg, password: Option) -> Result { let pk = PublicKey::from_str(&v).map_err(JsError::from)?; diff --git a/packages/types/package.json b/packages/types/package.json index 164f01fe2..344369cc6 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "bn.js": "^5.2.1", - "borsh": "^0.7.0", + "@dao-xyz/borsh": "^5.1.5", "slip44": "^2.0.4", "typescript": "^5.1.3" }, diff --git a/packages/types/src/tx/messages/index.ts b/packages/types/src/tx/messages/index.ts index fbc383796..998cf6bdf 100644 --- a/packages/types/src/tx/messages/index.ts +++ b/packages/types/src/tx/messages/index.ts @@ -1,32 +1,23 @@ -import { - serialize, - deserialize, - BinaryReader, - BinaryWriter, - Schema, -} from "borsh"; +import { Constructor, deserialize, serialize } from "@dao-xyz/borsh"; +import { Schema } from "../schema"; -export type ClassType = { - new (args: unknown): T; -}; - -export interface IMessage { - encode(schema: Schema, value: T): Uint8Array; - decode(schema: Schema, buffer: Uint8Array, parser: ClassType): T; +export interface IMessage { + encode(value: T): Uint8Array; + decode(buffer: Uint8Array, parser: Constructor): T; } -export class Message implements IMessage { - public encode(schema: Schema, value: T): Uint8Array { +export class Message implements IMessage { + public encode(value: T): Uint8Array { try { - return serialize(schema, value, BinaryWriter); + return serialize(value); } catch (e) { throw new Error(`Unable to serialize message: ${e}`); } } - public decode(schema: Schema, buffer: Uint8Array, parser: ClassType): T { + public decode(buffer: Uint8Array, parser: Constructor): T { try { - return deserialize(schema, parser, Buffer.from(buffer), BinaryReader); + return deserialize(Buffer.from(buffer), parser); } catch (e) { throw new Error(`Unable to deserialize message: ${e}`); } diff --git a/packages/types/src/tx/schema/account.ts b/packages/types/src/tx/schema/account.ts index 41c045c96..9f7c2884c 100644 --- a/packages/types/src/tx/schema/account.ts +++ b/packages/types/src/tx/schema/account.ts @@ -1,26 +1,11 @@ -import { Schema } from "borsh"; -import { SchemaObject } from "@anoma/utils"; - -import { InitAccountProps } from "../types"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { field, vec } from "@dao-xyz/borsh"; export class AccountMsgValue { - vp_code: Uint8Array; + @field({ type: vec("u8") }) + vpCode!: Uint8Array; - constructor(properties: InitAccountProps | SchemaObject) { - this.vp_code = 'vpCode' in properties ? - properties.vpCode : - properties.vp_code; + constructor(data: AccountMsgValue) { + Object.assign(this, data); } } - -const accountMsgSchemaEntry = [ - AccountMsgValue, - { - kind: "struct", - fields: [["vp_code", ["u8"]]], - }, -] as const; // needed for SchemaObject to deduce types correctly - -export const AccountMsgSchema = new Map([ - accountMsgSchemaEntry as [unknown, unknown] -]) as Schema; diff --git a/packages/types/src/tx/schema/bond.ts b/packages/types/src/tx/schema/bond.ts index afee8616e..9deb75b02 100644 --- a/packages/types/src/tx/schema/bond.ts +++ b/packages/types/src/tx/schema/bond.ts @@ -1,42 +1,28 @@ -import BN from "bn.js"; -import { Schema } from "borsh"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import BigNumber from "bignumber.js"; +import { field } from "@dao-xyz/borsh"; +import { BigNumberSerializer } from "./utils"; +import { TxMsgValue } from "./tx"; import { SubmitBondProps } from "../types"; -import { TxMsgSchema, TxMsgValue } from "./tx"; -import { SchemaObject } from "@anoma/utils"; export class SubmitBondMsgValue { - source: string; - validator: string; - amount: BN; - native_token: string; - tx: TxMsgValue; + @field({ type: "string" }) + source!: string; - constructor(properties: SubmitBondProps | SchemaObject) { - this.source = properties.source; - this.validator = properties.validator; - this.amount = new BN(properties.amount.toString()); - this.native_token = "nativeToken" in properties ? - properties.nativeToken : - properties.native_token; - this.tx = new TxMsgValue(properties.tx); - } -} + @field({ type: "string" }) + validator!: string; + + @field(BigNumberSerializer) + amount!: BigNumber; -export const BondMsgSchema = [ - SubmitBondMsgValue, - { - kind: "struct", - fields: [ - ["source", "string"], - ["validator", "string"], - ["amount", "u64"], - ["native_token", "string"], - ["tx", TxMsgValue], - ], - }, -] as const; // needed for SchemaObject to deduce types correctly + @field({ type: "string" }) + nativeToken!: string; -export const SubmitBondMsgSchema = new Map([ - TxMsgSchema as [unknown, unknown], - BondMsgSchema as [unknown, unknown], -]) as Schema; + @field({ type: TxMsgValue }) + tx!: TxMsgValue; + + constructor(data: SubmitBondProps) { + Object.assign(this, data); + this.tx = new TxMsgValue(data.tx); + } +} diff --git a/packages/types/src/tx/schema/ibcTransfer.ts b/packages/types/src/tx/schema/ibcTransfer.ts index 519284d51..36830b4aa 100644 --- a/packages/types/src/tx/schema/ibcTransfer.ts +++ b/packages/types/src/tx/schema/ibcTransfer.ts @@ -1,74 +1,43 @@ -import BN from "bn.js"; -import { Schema } from "borsh"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import BigNumber from "bignumber.js"; +import { field, option } from "@dao-xyz/borsh"; +import { BigNumberSerializer } from "./utils"; +import { TxMsgValue } from "./tx"; import { IbcTransferProps } from "../types"; -import { TxMsgSchema, TxMsgValue } from "./tx"; -import { SchemaObject } from "@anoma/utils"; export class IbcTransferMsgValue { - tx: TxMsgValue; - source: string; - receiver: string; - token: string; - sub_prefix?: string; - amount: BN; - port_id: string; - channel_id: string; - timeout_height?: BN; - timeout_sec_offset?: BN; + @field({ type: TxMsgValue }) + tx!: TxMsgValue; - constructor(properties: IbcTransferProps | SchemaObject) { - const timeoutHeight = - "timeoutHeight" in properties ? properties.timeoutHeight : - "timeout_height" in properties ? properties.timeout_height : - undefined; + @field({ type: "string" }) + source!: string; - const timeoutSecOffset = - "timeoutSecOffset" in properties ? properties.timeoutSecOffset : - "timeout_sec_offset" in properties ? properties.timeout_sec_offset : - undefined; + @field({ type: "string" }) + receiver!: string; - this.tx = new TxMsgValue(properties.tx); - this.source = properties.source; - this.receiver = properties.receiver; - this.token = properties.token; - this.sub_prefix = - 'subPrefix' in properties ? properties.subPrefix : - 'sub_prefix' in properties ? properties.sub_prefix : - undefined; - this.amount = new BN(properties.amount.toString()); - this.port_id = 'portId' in properties ? - properties.portId : - properties.port_id; - this.channel_id = 'channelId' in properties ? - properties.channelId : - properties.channel_id; - this.timeout_height = - timeoutHeight !== undefined ? new BN(timeoutHeight) : undefined; - this.timeout_sec_offset = - timeoutSecOffset !== undefined ? new BN(timeoutSecOffset) : undefined; - } -} + @field({ type: "string" }) + token!: string; + + @field({ type: option("string") }) + subPrefix!: string | undefined; + + @field(BigNumberSerializer) + amount!: BigNumber; + + @field({ type: "string" }) + portId!: string; -const IbcTransferMsgSchema = [ - IbcTransferMsgValue, - { - kind: "struct", - fields: [ - ["tx", TxMsgValue], - ["source", "string"], - ["receiver", "string"], - ["token", "string"], - ["sub_prefix", { kind: "option", type: "string" }], - ["amount", "u64"], - ["port_id", "string"], - ["channel_id", "string"], - ["timeout_height", { kind: "option", type: "u64" }], - ["timeout_sec_offset", { kind: "option", type: "u64" }], - ], - }, -] as const; // needed for SchemaObject to deduce types correctly + @field({ type: "string" }) + channelId!: string; -export const SubmitIbcTransferMsgSchema = new Map([ - TxMsgSchema, - IbcTransferMsgSchema as [unknown, unknown], -]) as Schema; + @field({ type: option("u64") }) + timeoutHeight!: bigint | undefined; + + @field({ type: option("u64") }) + timeoutSecOffset!: bigint | undefined; + + constructor(data: IbcTransferProps) { + Object.assign(this, data); + this.tx = new TxMsgValue(data.tx); + } +} diff --git a/packages/types/src/tx/schema/index.ts b/packages/types/src/tx/schema/index.ts index 3b18bd5e8..65c1b1bf3 100644 --- a/packages/types/src/tx/schema/index.ts +++ b/packages/types/src/tx/schema/index.ts @@ -1,6 +1,18 @@ export * from "./account"; export * from "./ibcTransfer"; -export * from "./shielded"; export * from "./transfer"; export * from "./bond"; export * from "./unbond"; + +import { AccountMsgValue } from "./account"; +import { IbcTransferMsgValue } from "./ibcTransfer"; +import { TransferMsgValue } from "./transfer"; +import { SubmitBondMsgValue } from "./bond"; +import { SubmitUnbondMsgValue } from "./unbond"; + +export type Schema = + | AccountMsgValue + | IbcTransferMsgValue + | TransferMsgValue + | SubmitBondMsgValue + | SubmitUnbondMsgValue; diff --git a/packages/types/src/tx/schema/shielded.ts b/packages/types/src/tx/schema/shielded.ts deleted file mode 100644 index 4c4e0c24d..000000000 --- a/packages/types/src/tx/schema/shielded.ts +++ /dev/null @@ -1,86 +0,0 @@ -import BN from "bn.js"; -import { ShieldedDataProps, ShieldedProps } from "../types"; - -// A Zcash Transaction -export class ShieldedTransferMsgValue { - txid: Uint8Array; - data: Uint8Array; - - constructor(properties: ShieldedProps) { - this.txid = properties.txId; - this.data = properties.data; - } -} - -export const ShieldedTransferMsgSchema = new Map([ - [ - ShieldedTransferMsgValue, - { - kind: "struct", - fields: [ - ["txid", ["u8", 32]], - ["data", ["u8"]], - ], - }, - ], -]); - -// Zcash Transaction Data -export class ShieldedDataMsgValue { - overwintered: boolean; - version: string; - version_group_id: string; - vin: Uint8Array; - vout: Uint8Array; - lock_time: BN; - expiry_height: BN; - value_balance: BN; - shielded_spends: Uint8Array; - shielded_converts: Uint8Array; - shielded_outputs: Uint8Array; - join_splits: string; - join_split_pubkey?: Uint8Array; - join_split_sig?: Uint8Array; - binding_sig?: Uint8Array; - - constructor(properties: ShieldedDataProps) { - this.overwintered = properties.overwintered; - this.version = properties.version; - this.version_group_id = properties.versionGroupId; - this.vin = properties.vin; - this.vout = properties.vout; - this.lock_time = new BN(properties.lockTime); - this.expiry_height = new BN(properties.expiryHeight); - this.value_balance = new BN(properties.valueBalance.toString()); - this.shielded_spends = properties.shieldedSpends; - this.shielded_converts = properties.shieldedConverts; - this.shielded_outputs = properties.shieldedOutputs; - this.join_splits = properties.joinSplits; - this.join_split_pubkey = properties.joinSplitPubKey; - this.join_split_sig = properties.joinSplitSig; - this.binding_sig = properties.bindingSig; - } -} - -export const ShieldedDataMsg = new Map([ - [ - ShieldedDataMsgValue, - [ - ["overwintered", "boolean"], - ["version", "string"], - ["version_group_id", ["u8"]], - ["vin", ["u8"]], - ["vout", ["u8"]], - ["lock_time", "u64"], - ["expiry_height", "u64"], - ["value_balance", "u64"], - ["shielded_spends", ["u8"]], - ["shielded_converts", ["u8"]], - ["shielded_outputs", ["u8"]], - ["join_splits", "string"], - ["join_split_pubkey", { kind: "option", type: ["u8", 32] }], - ["join_split_sig", { kind: "option", type: ["u8", 64] }], - ["binding_sig", { kind: "option", type: ["u8"] }], - ], - ], -]); diff --git a/packages/types/src/tx/schema/transfer.ts b/packages/types/src/tx/schema/transfer.ts index 5d0835225..ce5ccc123 100644 --- a/packages/types/src/tx/schema/transfer.ts +++ b/packages/types/src/tx/schema/transfer.ts @@ -1,55 +1,34 @@ -import BN from "bn.js"; -import { Schema } from "borsh"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import BigNumber from "bignumber.js"; +import { field, option } from "@dao-xyz/borsh"; +import { TxMsgValue } from "./tx"; +import { BigNumberSerializer } from "./utils"; import { TransferProps } from "../types"; -import { TxMsgSchema, TxMsgValue } from "./tx"; -import { SchemaObject } from "@anoma/utils"; export class TransferMsgValue { - tx: TxMsgValue; - source: string; - target: string; - token: string; - sub_prefix?: string; - amount: BN; - native_token: string; - - constructor(properties: TransferProps | SchemaObject) { - this.tx = properties.tx instanceof TxMsgValue ? - properties.tx : - new TxMsgValue(properties.tx); - this.source = properties.source; - this.target = properties.target; - this.token = properties.token; - this.sub_prefix = - 'subPrefix' in properties ? properties.subPrefix : - 'sub_prefix' in properties ? properties.sub_prefix : - undefined; - this.amount = properties.amount instanceof BN ? - properties.amount : - new BN(properties.amount.toString()); - this.native_token = 'nativeToken' in properties ? - properties.nativeToken : - properties.native_token; + @field({ type: TxMsgValue }) + tx!: TxMsgValue; + + @field({ type: "string" }) + source!: string; + + @field({ type: "string" }) + target!: string; + + @field({ type: "string" }) + token!: string; + + @field({ type: option("string") }) + subPrefix!: string | undefined; + + @field(BigNumberSerializer) + amount!: BigNumber; + + @field({ type: "string" }) + nativeToken!: string; + + constructor(data: TransferProps) { + Object.assign(this, data); + this.tx = new TxMsgValue(data.tx); } } - -export const TransferMsgSchema = [ - TransferMsgValue, - { - kind: "struct", - fields: [ - ["tx", TxMsgValue], - ["source", "string"], - ["target", "string"], - ["token", "string"], - ["sub_prefix", { kind: "option", type: "string" }], - ["amount", "u64"], - ["native_token", "string"], - ], - }, -] as const; // needed for SchemaObject to deduce types correctly - -export const SubmitTransferMsgSchema = new Map([ - TxMsgSchema as [unknown, unknown], - TransferMsgSchema as [unknown, unknown], -]) as Schema; diff --git a/packages/types/src/tx/schema/tx.ts b/packages/types/src/tx/schema/tx.ts index b3e055e98..f209d00c7 100644 --- a/packages/types/src/tx/schema/tx.ts +++ b/packages/types/src/tx/schema/tx.ts @@ -1,41 +1,25 @@ -import BN from "bn.js"; -import { TxProps } from "../types"; -import { SchemaObject } from "@anoma/utils"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import BigNumber from "bignumber.js"; +import { field, option } from "@dao-xyz/borsh"; +import { BigNumberSerializer } from "./utils"; export class TxMsgValue { - token: string; - fee_amount: BN; - gas_limit: BN; - chain_id: string; - public_key?: string; + @field({ type: "string" }) + token!: string; - constructor(properties: TxProps | SchemaObject) { - this.token = properties.token; - this.fee_amount = - "feeAmount" in properties - ? new BN(properties.feeAmount.toString()) - : properties.fee_amount; - this.gas_limit = - "gasLimit" in properties - ? new BN(properties.gasLimit.toString()) - : properties.gas_limit; - this.chain_id = - "chainId" in properties ? properties.chainId : properties.chain_id; - this.public_key = - "publicKey" in properties ? properties.publicKey : undefined; + @field(BigNumberSerializer) + feeAmount!: BigNumber; + + @field(BigNumberSerializer) + gasLimit!: BigNumber; + + @field({ type: "string" }) + chainId!: string; + + @field({ type: option("string") }) + publicKey!: string | undefined; + + constructor(data: TxMsgValue) { + Object.assign(this, data); } } - -export const TxMsgSchema = [ - TxMsgValue, - { - kind: "struct", - fields: [ - ["token", "string"], - ["fee_amount", "u64"], - ["gas_limit", "u64"], - ["chain_id", "string"], - ["public_key", { kind: "option", type: "string" }], - ], - }, -] as const; // needed for SchemaObject to deduce types correctly diff --git a/packages/types/src/tx/schema/unbond.ts b/packages/types/src/tx/schema/unbond.ts index b000be579..e87c699a2 100644 --- a/packages/types/src/tx/schema/unbond.ts +++ b/packages/types/src/tx/schema/unbond.ts @@ -1,37 +1,25 @@ -import BN from "bn.js"; -import { Schema } from "borsh"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import BigNumber from "bignumber.js"; +import { field } from "@dao-xyz/borsh"; +import { BigNumberSerializer } from "./utils"; +import { TxMsgValue } from "./tx"; import { SubmitUnbondProps } from "../types"; -import { TxMsgSchema, TxMsgValue } from "./tx"; -import { SchemaObject } from "@anoma/utils"; export class SubmitUnbondMsgValue { - source: string; - validator: string; - amount: BN; - tx: TxMsgValue; + @field({ type: "string" }) + source!: string; - constructor(properties: SubmitUnbondProps | SchemaObject) { - this.source = properties.source; - this.validator = properties.validator; - this.amount = new BN(properties.amount.toString()); - this.tx = new TxMsgValue(properties.tx); - } -} + @field({ type: "string" }) + validator!: string; + + @field(BigNumberSerializer) + amount!: BigNumber; -export const UnbondMsgSchema = [ - SubmitUnbondMsgValue, - { - kind: "struct", - fields: [ - ["source", "string"], - ["validator", "string"], - ["amount", "u64"], - ["tx", TxMsgValue], - ], - }, -] as const; // needed for SchemaObject to deduce types correctly + @field({ type: TxMsgValue }) + tx!: TxMsgValue; -export const SubmitUnbondMsgSchema = new Map([ - TxMsgSchema as [unknown, unknown], - UnbondMsgSchema as [unknown, unknown], -]) as Schema; + constructor(data: SubmitUnbondProps) { + Object.assign(this, data); + this.tx = new TxMsgValue(data.tx); + } +} diff --git a/packages/types/src/tx/schema/utils.ts b/packages/types/src/tx/schema/utils.ts new file mode 100644 index 000000000..df9c6dcba --- /dev/null +++ b/packages/types/src/tx/schema/utils.ts @@ -0,0 +1,12 @@ +import BigNumber from "bignumber.js"; +import { BinaryWriter, BinaryReader } from "@dao-xyz/borsh"; + +export const BigNumberSerializer = { + serialize: (value: BigNumber, writer: BinaryWriter) => { + writer.string(value.toString()); + }, + deserialize: (reader: BinaryReader): BigNumber => { + const valueString = reader.string(); + return new BigNumber(valueString); + }, +}; diff --git a/packages/types/src/tx/types.ts b/packages/types/src/tx/types.ts index 4eb07f906..91a70c5b6 100644 --- a/packages/types/src/tx/types.ts +++ b/packages/types/src/tx/types.ts @@ -20,7 +20,7 @@ export type TxProps = { feeAmount: BigNumber; gasLimit: BigNumber; chainId: string; - publicKey?: string; + publicKey: string | undefined; }; export type TransferProps = { @@ -28,7 +28,7 @@ export type TransferProps = { source: string; target: string; token: string; - subPrefix?: string; + subPrefix: string | undefined; amount: BigNumber; nativeToken: string; }; @@ -38,12 +38,12 @@ export type IbcTransferProps = { source: string; receiver: string; token: string; - subPrefix?: string; + subPrefix: string | undefined; amount: BigNumber; portId: string; channelId: string; - timeoutHeight?: number; - timeoutSecOffset?: number; + timeoutHeight: bigint | undefined; + timeoutSecOffset: bigint | undefined; }; // TODO: This is a placeholder diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index beb2cf248..6a75bd108 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -1,20 +1,21 @@ { "compilerOptions": { - "baseUrl": "./src", - "target": "es5", - "lib": ["dom", "esnext"], "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "baseUrl": "./src", + "esModuleInterop": true, + "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, + "isolatedModules": true, + "lib": ["dom", "esnext"], "module": "esnext", "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, "noEmit": true, + "noFallthroughCasesInSwitch": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es5", "types": ["node"] }, "include": ["src"] diff --git a/packages/utils/src/helpers/index.ts b/packages/utils/src/helpers/index.ts index ab1e5e7a6..e7d9a9fd2 100644 --- a/packages/utils/src/helpers/index.ts +++ b/packages/utils/src/helpers/index.ts @@ -2,7 +2,6 @@ import { JsonRpcRequest } from "@cosmjs/json-rpc"; import { DateTime } from "luxon"; import { JsonCompatibleArray, JsonCompatibleDictionary } from "@anoma/types"; import BigNumber from "bignumber.js"; -import BN from "bn.js"; const MICRO_FACTOR = 1000000; // 1,000,000 @@ -173,70 +172,3 @@ export const Result = { return { ok: false as const, error }; }, }; - - -// Translates Borsh type to JS type -type FromBorsh = - T extends "u8" | "u16" | "u32" ? number : - T extends "u64" | "u128" | "u256" | "u512" ? BN : - T extends "string" ? string : - T extends { kind: "option", type: infer S } ? FromBorsh : - T extends new(...args: infer _) => infer Res ? Res : - T extends readonly unknown[] ? Uint8Array : - unknown; - -// Gets first element of a tuple pair -type Key = - T extends readonly [infer Key, unknown] ? Key : never; - -// Gets second element of a tuple pair -type Value = - T extends readonly [unknown, infer Value] ? Value : never; - -/** - * Turns a Borsh schema value into a type with fields translated from - * Borsh types to JS types. - * - * Useful when deserializing for making sure the object passed by - * Borsh to a constructor is properly typed. Without this, there is no - * guarantee that the object the constructor expects will match what - * Borsh passes to it as an argument. - * - * Usage: - * - * class TxMsgValue { - * - * constructor(args: SchemaObject) { // note use of typeof - * args.token // : string - * args.fee_amount // : BN - * ... - * } - * } - * - * // schema value must match the following form: - * const TxMsgSchema = [ - * TxMsgValue, - * { - * kind: "struct", - * fields: [ - * ["token", "string"], - * ["fee_amount", "u64"], - * ["gas_limit", "u64"], - * ["chain_id", "string"], - * ], - * }, - * ] as const; // as const needed for typeof to deduce types correctly - */ -export type SchemaObject = - T extends readonly [unknown, { kind: "struct", fields: infer Fields }] ? - Fields extends readonly (infer FieldEntry extends (readonly [string, unknown]))[] ? - { - // optional types need special handling to add the '?' suffix to their keys - [KV in FieldEntry as Value extends { kind: "option" } ? Key : never]?: - Value extends { type: infer S } ? FromBorsh : unknown; - } & { - [KV in FieldEntry as Value extends { kind: "option" } ? never : Key]: - FromBorsh>; - } : - never : - never; diff --git a/yarn.lock b/yarn.lock index 96ebd1302..878344e58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2780,6 +2780,14 @@ dependencies: postcss-value-parser "^4.2.0" +"@dao-xyz/borsh@^5.1.5": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@dao-xyz/borsh/-/borsh-5.1.5.tgz#f932e81ad939f9dd5f7270f1359200cb160b0d34" + integrity sha512-F2p+jUPEu/0F1b5CBg1sL2chKP1STByV5rt8ulQnu6s9Pm++Nm7oWFZ9Deb4SljQ7KoAfXe8mWQ3z4XSUf2jpg== + dependencies: + "@protobufjs/float" "^1.0.2" + "@protobufjs/utf8" "^1.1.0" + "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -6655,7 +6663,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.6: +base-x@^3.0.6: version "3.0.9" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== @@ -6803,15 +6811,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -borsh@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" - integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== - dependencies: - bn.js "^5.2.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - bowser@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -6924,13 +6923,6 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" -bs58@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - bs58@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" @@ -16451,11 +16443,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"