entropyxyz / sdk

Official JavaScript SDK for Entropy blockchain.
GNU Affero General Public License v3.0
8 stars 0 forks source link

Add Entropy Type to Exports #248

Closed benschac closed 7 months ago

benschac commented 10 months ago

Currently need to:


// need to import twice
import Entropy from '@entropyxyz/entropy-js'
import type EntropyType from '@entropyxyz/entropy-js'

import React from 'react'

import { useState } from 'react'
// ^?

function useEntropy(): EntropyType {
  const entropy = new Entropy({
    seed: '0x1234567890123456789012345678901234567890123456789012345678901234',
    endpoint: 'https://api.entropy.xyz',
  })
  type HELLO = typeof entropy
  type Sign = HELLO['signTransaction']
  return entropy
}

Just looking through the dist in the entropy-client project.

import { ApiPromise } from '@polkadot/api';
import { SignatureLike } from '@ethersproject/bytes';
import { Keypair } from '@polkadot/util-crypto/types';
import { KeyringPair } from '@polkadot/keyring/types';
import { AccountId32 } from '@polkadot/types/interfaces/runtime';
import { EventRecord } from '@polkadot/types/interfaces/types';
import { SubmittableExtrinsic } from '@polkadot/api/types';

interface Signer {
    wallet: KeyringPair;
    pair: Keypair;
}
interface ValidatorInfo {
    x25519_public_key: string;
    ip_address: string;
    tss_account: string;
}
type Address = AccountId32 | string | Uint8Array;
interface EventFilter {
    section: string;
    name: string;
}
declare enum Arch {
    Evm = "evm",
    Btc = "btc"
}
interface EncMsg {
    msg: string;
    url: string;
}

declare class ExtrinsicBaseClass {
    substrate: ApiPromise;
    signer: Signer;
    constructor({ substrate, signer }: {
        substrate: any;
        signer: any;
    });
    sendAndWaitFor(call: SubmittableExtrinsic<'promise'>, freeTx: boolean, filter: EventFilter): Promise<EventRecord>;
    /**
     * @alpha
     *
     * @remarks
     * This function is part of the {@link Substrate} class
     *
     * @param {SubmittableExtrinsic<'promise'>} call - The extrinsic to send.
     * @returns {*}  {Promise<SubmittableExtrinsic<'promise'>>} - A promise that resolves when the transaction is included in a block.
     */
    handleFreeTx(call: SubmittableExtrinsic<'promise'>): Promise<SubmittableExtrinsic<'promise'>>;
}

interface RegistrationParams {
    freeTx?: boolean;
    initialProgram?: string;
    keyVisibility?: 'Public' | 'Permissioned' | 'Private';
    address: Address;
}
declare class RegistrationManager extends ExtrinsicBaseClass {
    constructor({ substrate, signer, }: {
        substrate: ApiPromise;
        signer: Signer;
    });
    register({ freeTx, initialProgram, keyVisibility, address, }: RegistrationParams): Promise<undefined>;
    checkRegistrationStatus(address: Address): Promise<boolean>;
}

interface Adapter {
    type: string;
    arch: Arch;
    preSign: (sigReq: any) => Promise<string>;
    postSign: (sig: any) => Promise<string>;
}

interface CryptoLib {
    from_hex: (input: string) => Uint8Array;
    encrypt_and_sign: (secretKey: Uint8Array, encoded: Uint8Array, serverDHKey: Uint8Array) => Promise<string>;
    decrypt_and_verify: (secretKey: Uint8Array, msg: string) => Promise<string>;
    public_key_from_secret: (secretKey: Uint8Array) => Promise<Uint8Array>;
}

interface Config {
    signer: Signer;
    substrate: ApiPromise;
    adapters: {
        [key: string | number]: Adapter;
    };
    crypto: CryptoLib;
}
interface TxParams {
    [key: string]: any;
}
interface SigTxOps {
    txParams: TxParams;
    type?: string;
}
interface SigOps {
    sigRequestHash: string;
    type?: string;
}
declare class SignatureRequestManager extends ExtrinsicBaseClass {
    adapters: {
        [key: string | number]: Adapter;
    };
    crypto: CryptoLib;
    constructor({ signer, substrate, adapters, crypto }: Config);
    signTransaction({ txParams, type }: SigTxOps): Promise<SignatureLike>;
    sign({ sigRequestHash }: SigOps): Promise<SignatureLike>;
    getTimeStamp(): {
        secs_since_epoch: number;
        nanos_since_epoch: number;
    };
    formatTxRequests({ strippedsigRequestHash, validatorsInfo, }: {
        strippedsigRequestHash: string;
        validatorsInfo: Array<ValidatorInfo>;
    }): Promise<EncMsg[]>;
    submitTransactionRequest(txReq: Array<EncMsg>): Promise<string[]>;
    getArbitraryValidators(sigRequest: string): Promise<ValidatorInfo[]>;
}

/**
 * @alpha
 * @remarks
 * This is the {@link ProgramManager} class
 * A class for interfacing with the V2 Entropy Constraints system
 */
declare class ProgramManager extends ExtrinsicBaseClass {
    /**
     * @alpha
     * @remarks
     * This function is part of the {@link ProgramManager} class
     * Creates an instance of ProgramManager.
     *
     * @param {ApiPromise} substrate - The api object for an Entropy blockchain
     * @param {Signer} signer - The signer object for the user interfacing with the Entropy blockchain
     */
    constructor({ substrate, signer }: {
        substrate: ApiPromise;
        signer: Signer;
    });
    get(deployKey?: string): Promise<ArrayBuffer>;
    set(program: ArrayBuffer): Promise<void>;
}

interface EntropyOpts {
    seed?: string;
    endpoint?: string;
    adapters?: {
        [key: string | number]: Adapter;
    };
}
declare class Entropy {
    #private;
    ready: Promise<void>;
    isRegistered: (address: Address) => Promise<boolean>;
    keys?: Signer;
    registrationManager: RegistrationManager;
    programs: ProgramManager;
    signingManager: SignatureRequestManager;
    substrate: ApiPromise;
    init(opts: EntropyOpts): Promise<void>;
    constructor(opts: EntropyOpts);
    register(params: RegistrationParams): Promise<undefined>;
    signTransaction(params: SigTxOps): Promise<SignatureLike>;
    sign(params: SigOps): Promise<SignatureLike>;
}

export { EntropyOpts, Entropy as default };

there isn't an Entropy type exported, and the class is exported as a default, which the new Entropy object is also exposed as.

doing a named export instead of a default export might help solve this issue.

frankiebee commented 10 months ago

I dont understand this issue fully. Why can you not use the class as a type?

benschac commented 10 months ago

Class is a value. Type is a type.

Values can be inferred into types

When providing an sdk, we should provide all types a dev would need. Not make them infer types and guess around.

import Entropy from '@entropyxyz/entropy-js'
import type EntropyType from '@entropyxyz/entropy-js'

should be

import Entropy, type {
Entropy as EntropyType
} from '@entropyxyz/entropy-js'
benschac commented 10 months ago

making devs have to import the module twice is what should be avoided.

If I'm doing something wrong plz update the docs, and I'll give it another shot. tyty

frankiebee commented 10 months ago

why does this not work?

const entropy: Entropy = new Entropy(opts)
frankiebee commented 10 months ago

is there a pattern you are expecting not handled by the docs in typescript? https://www.typescriptlang.org/docs/handbook/2/classes.html

frankiebee commented 10 months ago

if you are unsatisfied with this pattern please open a PR with proposal pattern.

benschac commented 10 months ago

why does this not work?

const entropy: Entropy = new Entropy(opts)

Typescript doesn't know which is a value, and which is a type because they're both Entropy

benschac commented 10 months ago

if you are unsatisfied with this pattern please open a PR with proposal pattern.

Not sure its satisfied thing. A developer is going to have to do something like:

const entropy = new Entropy()

const EntropyType = typeof entropy
// or
const EntropyType = ReturnType<() => Entropy>

which could be handled if we export a type for a developer to use.

Not really the end of the world or anything. Just, a quick developer experience win.

benschac commented 10 months ago

But, can happily add a PR.

frankiebee commented 7 months ago

done