-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e1ef0a8
commit 78d65e0
Showing
7 changed files
with
573 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { AccountAddress } from '@injectivelabs/ts-types' | ||
import { DirectSignResponse } from '@cosmjs/proto-signing' | ||
import { Msgs } from '@injectivelabs/sdk-ts' | ||
import { GeneralException } from '@injectivelabs/exceptions' | ||
import { Wallet } from '../wallet-strategy/types' | ||
import Keplr from './strategies/Keplr' | ||
import Leap from './strategies/Leap' | ||
import Cosmostation from './strategies/Cosmostation' | ||
import { | ||
ConcreteCosmosWalletStrategy, | ||
CosmosWalletStrategyArguments, | ||
} from './types/strategy' | ||
|
||
const createWallet = ({ | ||
wallet, | ||
args, | ||
}: { | ||
wallet: Wallet | ||
args: CosmosWalletStrategyArguments | ||
}): ConcreteCosmosWalletStrategy | undefined => { | ||
switch (wallet) { | ||
case Wallet.Keplr: | ||
return new Keplr({ ...args }) | ||
case Wallet.Leap: | ||
return new Leap({ ...args }) | ||
case Wallet.Cosmostation: | ||
return new Cosmostation({ ...args }) | ||
default: | ||
throw new GeneralException( | ||
new Error(`The ${wallet} concrete wallet strategy is not supported`), | ||
) | ||
} | ||
} | ||
|
||
const createWallets = ( | ||
args: CosmosWalletStrategyArguments, | ||
): Record<Wallet, ConcreteCosmosWalletStrategy | undefined> => | ||
Object.values(Wallet).reduce( | ||
(strategies, wallet) => ({ | ||
...strategies, | ||
[wallet]: createWallet({ wallet, args }), | ||
}), | ||
{} as Record<Wallet, ConcreteCosmosWalletStrategy | undefined>, | ||
) | ||
|
||
export default class CosmosWalletStrategy { | ||
public strategies: Record<Wallet, ConcreteCosmosWalletStrategy | undefined> | ||
|
||
public wallet: Wallet | ||
|
||
constructor(args: CosmosWalletStrategyArguments) { | ||
this.strategies = createWallets(args) | ||
this.wallet = args.wallet || Wallet.Keplr | ||
} | ||
|
||
public getWallet(): Wallet { | ||
return this.wallet | ||
} | ||
|
||
public setWallet(wallet: Wallet) { | ||
this.wallet = wallet | ||
} | ||
|
||
public getStrategy(): ConcreteCosmosWalletStrategy { | ||
if (!this.strategies[this.wallet]) { | ||
throw new GeneralException( | ||
new Error(`Wallet ${this.wallet} is not enabled/available!`), | ||
) | ||
} | ||
|
||
return this.strategies[this.wallet] as ConcreteCosmosWalletStrategy | ||
} | ||
|
||
public getAddresses(): Promise<AccountAddress[]> { | ||
return this.getStrategy().getAddresses() | ||
} | ||
|
||
public async sendTransaction(tx: DirectSignResponse): Promise<string> { | ||
return this.getStrategy().sendTransaction(tx) | ||
} | ||
|
||
public async signTransaction(data: { | ||
address: string | ||
memo: string | ||
gas: string | ||
message: Msgs | Msgs[] | ||
}): Promise<string | DirectSignResponse> { | ||
return this.getStrategy().signTransaction(data) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './endpoints' | ||
export { default as CosmosWalletStrategy } from './CosmosWalletStrategy' |
190 changes: 190 additions & 0 deletions
190
packages/wallet-ts/src/Cosmos/strategies/Cosmostation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
/* eslint-disable class-methods-use-this */ | ||
import { CosmosChainId } from '@injectivelabs/ts-types' | ||
import { | ||
UnspecifiedErrorCode, | ||
CosmosWalletException, | ||
TransactionException, | ||
ErrorType, | ||
} from '@injectivelabs/exceptions' | ||
import { DEFAULT_STD_FEE } from '@injectivelabs/utils' | ||
import { | ||
createTxRawFromSigResponse, | ||
createTransactionAndCosmosSignDocForAddressAndMsg, | ||
} from '@injectivelabs/sdk-ts' | ||
import type { Msgs } from '@injectivelabs/sdk-ts' | ||
import { cosmos, InstallError, Cosmos } from '@cosmostation/extension-client' | ||
import { DirectSignResponse, makeSignDoc } from '@cosmjs/proto-signing' | ||
import { SEND_TRANSACTION_MODE } from '@cosmostation/extension-client/cosmos' | ||
import { ConcreteCosmosWalletStrategy } from '../types/strategy' | ||
import { WalletAction } from '../../wallet-strategy/types/enums' | ||
import { getEndpointsFromChainId } from '../endpoints' | ||
|
||
const INJECTIVE_CHAIN_NAME = 'injective' | ||
|
||
const getChainNameFromChainId = (chainId: CosmosChainId) => { | ||
const [chainName] = chainId.split('-') | ||
|
||
return chainName | ||
} | ||
|
||
export default class Cosmostation implements ConcreteCosmosWalletStrategy { | ||
public chainName: string | ||
|
||
public provider?: Cosmos | ||
|
||
public chainId: CosmosChainId | ||
|
||
constructor(args: { chainId: CosmosChainId }) { | ||
this.chainId = args.chainId | ||
this.chainName = getChainNameFromChainId(args.chainId) | ||
} | ||
|
||
async isChainIdSupported(chainId?: CosmosChainId): Promise<boolean> { | ||
const actualChainId = chainId || this.chainId | ||
const provider = await this.getProvider() | ||
|
||
const supportedChainIds = await provider.getSupportedChainIds() | ||
|
||
return !!supportedChainIds.official.find( | ||
(chainId) => chainId === actualChainId, | ||
) | ||
} | ||
|
||
async getAddresses(): Promise<string[]> { | ||
const { chainName } = this | ||
const provider = await this.getProvider() | ||
|
||
try { | ||
const accounts = await provider.requestAccount(chainName) | ||
|
||
return [accounts.address] | ||
} catch (e: unknown) { | ||
if ((e as any).code === 4001) { | ||
throw new CosmosWalletException( | ||
new Error('The user rejected the request'), | ||
{ | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.WalletError, | ||
contextModule: WalletAction.GetAccounts, | ||
}, | ||
) | ||
} | ||
|
||
throw new CosmosWalletException(new Error((e as any).message), { | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.WalletError, | ||
contextModule: WalletAction.GetAccounts, | ||
}) | ||
} | ||
} | ||
|
||
async sendTransaction(signResponse: DirectSignResponse): Promise<string> { | ||
const { chainName } = this | ||
const provider = await this.getProvider() | ||
const txRaw = createTxRawFromSigResponse(signResponse) | ||
|
||
try { | ||
const response = await provider.sendTransaction( | ||
chainName, | ||
txRaw.serializeBinary(), | ||
SEND_TRANSACTION_MODE.ASYNC, | ||
) | ||
|
||
return response.tx_response.txhash | ||
} catch (e: unknown) { | ||
throw new TransactionException(new Error((e as any).message), { | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.ChainError, | ||
contextModule: WalletAction.SendTransaction, | ||
}) | ||
} | ||
} | ||
|
||
async signTransaction(transaction: { | ||
memo: string | ||
address: string | ||
gas: string | ||
message: Msgs | Msgs[] | ||
}) { | ||
const { chainName, chainId } = this | ||
const provider = await this.getProvider() | ||
const signer = await provider.getAccount(INJECTIVE_CHAIN_NAME) | ||
const endpoints = getEndpointsFromChainId(chainId) | ||
|
||
try { | ||
/** Prepare the Transaction * */ | ||
const { bodyBytes, authInfoBytes, accountNumber } = | ||
await createTransactionAndCosmosSignDocForAddressAndMsg({ | ||
chainId, | ||
address: transaction.address, | ||
memo: transaction.memo, | ||
message: transaction.message, | ||
pubKey: Buffer.from(signer.publicKey).toString('base64'), | ||
endpoint: endpoints.rest, | ||
fee: { | ||
...DEFAULT_STD_FEE, | ||
gas: transaction.gas || DEFAULT_STD_FEE.gas, | ||
}, | ||
}) | ||
|
||
/* Sign the transaction */ | ||
const signDirectResponse = await provider.signDirect( | ||
chainName, | ||
{ | ||
chain_id: chainId, | ||
body_bytes: bodyBytes, | ||
auth_info_bytes: authInfoBytes, | ||
account_number: accountNumber.toString(), | ||
}, | ||
{ fee: false, memo: true }, | ||
) | ||
|
||
return { | ||
signed: makeSignDoc( | ||
signDirectResponse.signed_doc.body_bytes, | ||
signDirectResponse.signed_doc.auth_info_bytes, | ||
signDirectResponse.signed_doc.chain_id, | ||
parseInt(signDirectResponse.signed_doc.account_number, 10), | ||
), | ||
signature: { | ||
signature: signDirectResponse.signature, | ||
}, | ||
} as DirectSignResponse | ||
} catch (e: unknown) { | ||
throw new CosmosWalletException(new Error((e as any).message), { | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.WalletError, | ||
contextModule: WalletAction.SendTransaction, | ||
}) | ||
} | ||
} | ||
|
||
private async getProvider(): Promise<Cosmos> { | ||
if (this.provider) { | ||
return this.provider | ||
} | ||
|
||
try { | ||
const provider = await cosmos() | ||
|
||
this.provider = provider | ||
|
||
return provider | ||
} catch (e) { | ||
if (e instanceof InstallError) { | ||
throw new CosmosWalletException( | ||
new Error('Please install the Cosmostation extension'), | ||
{ | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.WalletNotInstalledError, | ||
}, | ||
) | ||
} | ||
|
||
throw new CosmosWalletException(new Error((e as any).message), { | ||
code: UnspecifiedErrorCode, | ||
type: ErrorType.WalletError, | ||
}) | ||
} | ||
} | ||
} |
Oops, something went wrong.