Skip to content

Commit

Permalink
inj signing client and e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Zetazzz committed Sep 10, 2024
1 parent 7fa9432 commit 019f582
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"starship/__tests__/token.test.ts"
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/networks/ethermint",
"cwd": "${workspaceFolder}/networks/injective",
"internalConsoleOptions": "neverOpen"
},
{
Expand All @@ -97,7 +97,7 @@
"starship/__tests__/gov.test.ts"
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/networks/ethermint",
"cwd": "${workspaceFolder}/networks/injective",
"internalConsoleOptions": "neverOpen"
}
]
Expand Down
1 change: 1 addition & 0 deletions networks/injective/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@interchainjs/ethereum": "^0.0.1-beta.8",
"@interchainjs/types": "^0.0.1-beta.8",
"@interchainjs/utils": "^0.0.1-beta.8",
"interchainjs": "^0.0.1-beta.12",
"decimal.js": "^10.4.3"
},
"keywords": [
Expand Down
4 changes: 2 additions & 2 deletions networks/injective/src/accounts/inj-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { keccak_256 } from '@noble/hashes/sha3';
*/
export class InjAccount extends AccountBase {
/**
* Create inj address.
* Create inj address by pubkey.
*/
getAddress(): string {
getAddressByPubKey(): string {
const uncompressedPubKey = this.auth.getPublicKey(false);

const pubkeyHex = uncompressedPubKey.toHex().substring(2);
Expand Down
13 changes: 11 additions & 2 deletions networks/injective/src/signers/amino.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AminoSignerBase } from '@interchainjs/cosmos/signers/amino';
import { BaseCosmosTxBuilder } from '@interchainjs/cosmos/base';
import { BaseCosmosTxBuilder, CosmosDocSigner } from '@interchainjs/cosmos/base';
import { BaseCosmosTxBuilderContext } from '@interchainjs/cosmos/base/builder-context';
import { AminoTxBuilder } from '@interchainjs/cosmos/builder/amino-tx-builder';
import { AminoSigBuilder, AminoTxBuilder } from '@interchainjs/cosmos/builder/amino-tx-builder';
import {
AminoConverter,
CosmosAminoDoc,
Expand All @@ -16,6 +16,15 @@ import { InjAccount } from '../accounts/inj-account';
import { defaultSignerOptions } from '../defaults';
import { InjectiveAminoSigner } from '../types';

/**
* AminoDocSigner is a signer for Amino document.
*/
export class AminoDocSigner extends CosmosDocSigner<CosmosAminoDoc> {
getTxBuilder(): AminoSigBuilder {
return new AminoSigBuilder(new BaseCosmosTxBuilderContext(this));
}
}

/**
* AminoDocSigner is a signer for inj Amino document.
*/
Expand Down
15 changes: 14 additions & 1 deletion networks/injective/src/signers/direct.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { DirectSignerBase } from '@interchainjs/cosmos/signers/direct';
import { Encoder, SignerOptions } from '@interchainjs/cosmos/types';
import { CosmosDirectDoc, Encoder, SignerOptions } from '@interchainjs/cosmos/types';
import { DirectDocAuth } from '@interchainjs/cosmos/types/docAuth';
import { OfflineDirectSigner } from '@interchainjs/cosmos/types/wallet';
import { Auth, HttpEndpoint } from '@interchainjs/types';

import { InjAccount } from '../accounts/inj-account';
import { defaultSignerOptions } from '../defaults';
import { InjectiveDirectSigner } from '../types';
import { CosmosDocSigner } from '@interchainjs/cosmos/base';
import { DirectSigBuilder } from '@interchainjs/cosmos/builder/direct-tx-builder';
import { BaseCosmosTxBuilderContext } from '@interchainjs/cosmos/base/builder-context';

/**
* DirectDocSigner is a signer for Direct document.
*/
export class DirectDocSigner extends CosmosDocSigner<CosmosDirectDoc> {
getTxBuilder(): DirectSigBuilder {
return new DirectSigBuilder(new BaseCosmosTxBuilderContext(this));
}
}


/**
* DirectDocSigner is a signer for inj Direct document.
Expand Down
61 changes: 61 additions & 0 deletions networks/injective/src/signing-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { isOfflineAminoSigner, isOfflineDirectSigner, OfflineSigner } from "@interchainjs/cosmos/types/wallet";
import { HttpEndpoint } from "@interchainjs/types";
import { SigningClient } from "interchainjs/signing-client"
import { SignerOptions } from "interchainjs/types/signing-client";
import { RpcClient } from '@interchainjs/cosmos/query/rpc';
import { AminoSigner } from "./signers/amino";
import { DirectSigner } from "./signers/direct";

/**
* signingClient for inj
*/
export class InjSigningClient extends SigningClient {
static async connectWithSigner(
endpoint: string | HttpEndpoint,
signer: OfflineSigner,
options: SignerOptions = {}
): Promise<InjSigningClient> {
const signingClient = new InjSigningClient(
new RpcClient(endpoint, options.prefix),
signer,
options
);

await signingClient.connect();

return signingClient;
}

override async connect() {
if (isOfflineAminoSigner(this.offlineSigner)) {
const aminoSigners = await AminoSigner.fromWalletToSigners(
this.offlineSigner,
this.encoders,
this.converters,
this.endpoint,
{
prefix: this.options.prefix,
}
);

for (const signer of aminoSigners) {
this.aminoSigners[await signer.getAddress()] = signer;
}
}

if (isOfflineDirectSigner(this.offlineSigner)) {
const directSigners = await DirectSigner.fromWalletToSigners(
this.offlineSigner,
this.encoders,
this.endpoint,
{
prefix: this.options.prefix,
}
);

for (const signer of directSigners) {
this.directSigners[await signer.getAddress()] = signer;
}
}
}
}
160 changes: 160 additions & 0 deletions networks/injective/src/wallets/ethEecp256k1hd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { EthSecp256k1Auth } from '@interchainjs/auth/ethSecp256k1';
import { AccountData, AddrDerivation, Auth, SignerConfig } from '@interchainjs/types';

import { AminoDocSigner } from '../signers/amino';
import { defaultSignerOptions } from '../defaults';
import { DirectDocSigner } from '../signers/direct';
import {
CosmosAccount,
CosmosAminoDoc,
CosmosDirectDoc,
ICosmosAccount,
ICosmosWallet,
} from '@interchainjs/cosmos/types';
import {
AminoSignResponse,
DirectSignResponse,
OfflineAminoSigner,
OfflineDirectSigner,
WalletOptions,
} from '@interchainjs/cosmos/types/wallet';
import { InjAccount } from '../accounts/inj-account';

/**
* Cosmos HD Wallet for secp256k1
*/
export class EthSecp256k1HDWallet
implements ICosmosWallet, OfflineAminoSigner, OfflineDirectSigner
{
constructor(
public accounts: ICosmosAccount[],
public options: SignerConfig
) {
this.options = { ...defaultSignerOptions.Cosmos, ...options };
}

/**
* Create a new HD wallet from mnemonic
* @param mnemonic
* @param derivations infos for derivate addresses
* @param options wallet options
* @returns HD wallet
*/
static fromMnemonic(
mnemonic: string,
derivations: AddrDerivation[],
options?: WalletOptions
) {
const hdPaths = derivations.map((derivation) => derivation.hdPath);

const auths: Auth[] = EthSecp256k1Auth.fromMnemonic(mnemonic, hdPaths, {
bip39Password: options?.bip39Password,
});

const accounts = auths.map((auth, i) => {
const derivation = derivations[i];
return new InjAccount(derivation.prefix, auth);
});

return new EthSecp256k1HDWallet(accounts, options?.signerConfig);
}

/**
* Get account data
* @returns account data
*/
async getAccounts(): Promise<AccountData[]> {
return this.accounts.map((acct) => {
return acct.toAccountData();
});
}

/**
* Get one of the accounts using the address.
* @param address
* @returns
*/
private getAcctFromBech32Addr(address: string) {
const id = this.accounts.findIndex((acct) => acct.address === address);
if (id === -1) {
throw new Error('No such signerAddress been authed.');
}
return this.accounts[id];
}

/**
* Sign direct doc for signerAddress
*/
async signDirect(
signerAddress: string,
signDoc: CosmosDirectDoc
): Promise<DirectSignResponse> {
const account = this.getAcctFromBech32Addr(signerAddress);

const docSigner = new DirectDocSigner(account.auth, this.options);

const resp = await docSigner.signDoc(signDoc);

return {
signed: resp.signDoc,
signature: {
pub_key: {
type: 'tendermint/PubKeySecp256k1',
value: {
key: account.publicKey.toBase64(),
},
},
signature: resp.signature.toBase64(),
},
};
}

/**
* sign amino doc for signerAddress
*/
async signAmino(
signerAddress: string,
signDoc: CosmosAminoDoc
): Promise<AminoSignResponse> {
const account = this.getAcctFromBech32Addr(signerAddress);

const docSigner = new AminoDocSigner(account.auth, this.options);

const resp = await docSigner.signDoc(signDoc);

return {
signed: resp.signDoc,
signature: {
pub_key: {
type: 'tendermint/PubKeySecp256k1',
value: {
key: account.publicKey.toBase64(),
},
},
signature: resp.signature.toBase64(),
},
};
}

/**
* Convert this to offline direct signer for hiding the private key.
*/
toOfflineDirectSigner(): OfflineDirectSigner {
return {
getAccounts: async () => this.getAccounts(),
signDirect: async (signerAddress: string, signDoc: CosmosDirectDoc) =>
this.signDirect(signerAddress, signDoc),
};
}

/**
* Convert this to offline amino signer for hiding the private key.
*/
toOfflineAminoSigner(): OfflineAminoSigner {
return {
getAccounts: async () => this.getAccounts(),
signAmino: async (signerAddress: string, signDoc: CosmosAminoDoc) =>
this.signAmino(signerAddress, signDoc),
};
}
}
Loading

0 comments on commit 019f582

Please sign in to comment.