Skip to content

Commit

Permalink
Merge pull request #43 from oraichain/feat/oasis
Browse files Browse the repository at this point in the history
Oasis network
  • Loading branch information
marxeille authored Feb 21, 2024
2 parents 3570d55 + 0e7e1b6 commit ecba32c
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 64 deletions.
133 changes: 80 additions & 53 deletions src/keyring/handler.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/keyring/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import {
RequestPublicKeyMsg,
ChangeChainMsg,
RequestSignTronMsg,
RequestSignOasisMsg,
RequestSignBitcoinMsg,
GetDefaultAddressTronMsg,
GetDefaultAddressOasisMsg,
TriggerSmartContractMsg,
RequestSendRawTransactionMsg
} from './messages';
Expand All @@ -59,6 +61,7 @@ export function init(router: Router, service: KeyRingService): void {
router.registerMessage(RequestSignDirectMsg);
router.registerMessage(RequestSignEthereumMsg);
router.registerMessage(RequestSignTronMsg);
router.registerMessage(RequestSignOasisMsg);
router.registerMessage(TriggerSmartContractMsg);
router.registerMessage(RequestSignBitcoinMsg);
router.registerMessage(RequestSignEthereumTypedDataMsg);
Expand All @@ -68,6 +71,7 @@ export function init(router: Router, service: KeyRingService): void {
router.registerMessage(RequestSignReEncryptDataMsg);
router.registerMessage(GetMultiKeyStoreInfoMsg);
router.registerMessage(GetDefaultAddressTronMsg);
router.registerMessage(GetDefaultAddressOasisMsg);
router.registerMessage(RequestSendRawTransactionMsg);
router.registerMessage(ChangeKeyRingMsg);
router.registerMessage(GetIsKeyStoreCoinTypeSetMsg);
Expand Down
97 changes: 90 additions & 7 deletions src/keyring/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,20 @@ import {
getAddressTypeByAddress
} from '@owallet/bitcoin';
import { BIP44HDPath } from '@owallet/types';
import { handleAddressLedgerByChainId } from '../utils/helper';
import { getOasisNic, handleAddressLedgerByChainId } from '../utils/helper';
import { AddressesLedger } from '@owallet/types';
import { ChainsService } from '../chains';
import * as oasis from '@oasisprotocol/client';
import {
addressToPublicKey,
hex2uint,
parseRoseStringToBigNumber,
parseRpcBalance,
StringifiedBigInt,
uint2hex
} from '../utils/oasis-helper';
import { OasisTransaction, signerFromPrivateKey } from '../utils/oasis-tx-builder';

// inject TronWeb class
(globalThis as any).TronWeb = require('tronweb');
export enum KeyRingStatus {
Expand Down Expand Up @@ -569,7 +580,7 @@ export class KeyRing {
[ChainIdHelper.parse(chainId).identifier]: coinType
};

const keyStoreInMulti = this.multiKeyStore.find((keyStore) => {
const keyStoreInMulti = this.multiKeyStore.find(keyStore => {
return (
KeyRing.getKeyStoreId(keyStore) ===
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand Down Expand Up @@ -598,7 +609,7 @@ export class KeyRing {
const { publicKey, address } = (await this.ledgerKeeper.getPublicKey(env, hdPath, ledgerAppType)) || {};

const pubKey = publicKey ? Buffer.from(publicKey).toString('hex') : null;
const keyStoreInMulti = this.multiKeyStore.find((keyStore) => {
const keyStoreInMulti = this.multiKeyStore.find(keyStore => {
return (
KeyRing.getKeyStoreId(keyStore) ===
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand Down Expand Up @@ -775,6 +786,72 @@ export class KeyRing {
legacyAddress: legacyAddress
};
}

public async loadPublicKeyOasis(): Promise<Uint8Array> {
if (this.status !== KeyRingStatus.UNLOCKED || this.type === 'none' || !this.keyStore) {
throw new Error('Key ring is not unlocked');
}
if (!this.mnemonic) {
throw new Error('Key store type is mnemonic and it is unlocked. But, mnemonic is not loaded unexpectedly');
}
const signer = await oasis.hdkey.HDKey.getAccountSigner(this.mnemonic, 0);
return signer.publicKey;
}

public async signOasis(chainId: string, data): Promise<any> {
if (this.status !== KeyRingStatus.UNLOCKED || this.type === 'none' || !this.keyStore) {
throw new Error('Key ring is not unlocked');
}
if (!this.mnemonic) {
throw new Error('Key store type is mnemonic and it is unlocked. But, mnemonic is not loaded unexpectedly');
}

const { amount, to } = data;

const chainInfo = await this.chainsService.getChainInfo(chainId as string);

const nic = await getOasisNic(chainInfo.grpc);
const accountSigner = await oasis.hdkey.HDKey.getAccountSigner(this.mnemonic, 0);
const privateKey = uint2hex(accountSigner.secretKey);
const bytes = hex2uint(privateKey!);
const signer = signerFromPrivateKey(bytes);
const bigIntAmount = BigInt(parseRoseStringToBigNumber(amount).toString());
console.log('bigIntAmount', bigIntAmount);
const chainContext = await nic.consensusGetChainContext();

const tw = await OasisTransaction.buildTransfer(nic, signer, to.replaceAll(' ', ''), bigIntAmount);

await OasisTransaction.sign(chainContext, signer, tw);

const payload = await OasisTransaction.submit(nic, tw);

return payload;
}

public async loadBalanceOasis(
address: string,
chainId: string
): Promise<{
available: StringifiedBigInt;
validator: { escrow: StringifiedBigInt; escrow_debonding: StringifiedBigInt };
}> {
if (this.status !== KeyRingStatus.UNLOCKED || this.type === 'none' || !this.keyStore) {
throw new Error('Key ring is not unlocked');
}
if (!this.mnemonic) {
throw new Error('Key store type is mnemonic and it is unlocked. But, mnemonic is not loaded unexpectedly');
}
const chainInfo = await this.chainsService.getChainInfo(chainId as string);

const nic = await getOasisNic(chainInfo.grpc);

const publicKey = await addressToPublicKey(address);
const account = await nic.stakingAccount({ owner: publicKey, height: 0 });
const grpcBalance = parseRpcBalance(account);

return grpcBalance;
}

private loadPrivKey(coinType: number): PrivKeySecp256k1 {
if (this.status !== KeyRingStatus.UNLOCKED || this.type === 'none' || !this.keyStore) {
throw new Error('Key ring is not unlocked');
Expand All @@ -801,7 +878,6 @@ export class KeyRing {
if (!this.mnemonic) {
throw new Error('Key store type is mnemonic and it is unlocked. But, mnemonic is not loaded unexpectedly');
}
// could use it here
const privKey = Mnemonic.generateWalletFromMnemonic(this.mnemonic, path);
this.cached.set(path, privKey);
return new PrivKeySecp256k1(privKey);
Expand Down Expand Up @@ -1122,7 +1198,7 @@ export class KeyRing {
const privKey = this.loadPrivKey(60);
const privKeyBuffer = Buffer.from(privKey.toBytes());
const response = await Promise.all(
message[0].map(async (data) => {
message[0].map(async data => {
const encryptedData = {
ciphertext: Buffer.from(data.ciphertext, 'hex'),
ephemPublicKey: Buffer.from(data.ephemPublicKey, 'hex'),
Expand Down Expand Up @@ -1152,7 +1228,7 @@ export class KeyRing {
}
}

public async getPublicKey(chainId: string): Promise<string> {
public async getPublicKey(chainId: string): Promise<string | Uint8Array> {
if (this.status !== KeyRingStatus.UNLOCKED) {
throw new Error('Key ring is not unlocked');
}
Expand All @@ -1161,7 +1237,14 @@ export class KeyRing {
throw new Error('Key Store is empty');
}

if (chainId === '0x5afe') {
const pubKey = await this.loadPublicKeyOasis();
return pubKey;
}

const privKey = this.loadPrivKey(getCoinTypeByChainId(chainId));

// And Oasis here
const pubKeyHex = '04' + privateToPublic(Buffer.from(privKey.toBytes())).toString('hex');

return pubKeyHex;
Expand Down Expand Up @@ -1359,7 +1442,7 @@ export class KeyRing {
throw new Error('Arrays are unimplemented in encodeData; use V4 extension');
}
const parsedType = type.slice(0, type.lastIndexOf('['));
const typeValuePairs = value.map((item) => this.encodeField(types, name, parsedType, item, version));
const typeValuePairs = value.map(item => this.encodeField(types, name, parsedType, item, version));
return [
'bytes32',
keccak(
Expand Down
63 changes: 63 additions & 0 deletions src/keyring/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1396,3 +1396,66 @@ export class ExportKeyRingDatasMsg extends Message<ExportKeyRingData[]> {
return ExportKeyRingDatasMsg.type();
}
}

// Oasis
// request sign Oasis goes here
export class RequestSignOasisMsg extends Message<{}> {
public static type() {
return 'request-sign-oasis';
}

constructor(public readonly chainId: string, public readonly data: object) {
super();
}

validateBasic(): void {
if (!this.chainId) {
throw new OWalletError('keyring', 270, 'chain id not set');
}

if (!this.data) {
throw new OWalletError('keyring', 231, 'data not set');
}
}

approveExternal(): boolean {
return true;
}

route(): string {
return ROUTE;
}

type(): string {
return RequestSignOasisMsg.type();
}
}

export class GetDefaultAddressOasisMsg extends Message<{
hex?: string;
address?: string;
name?: string;
type?: number;
}> {
public static type() {
return 'get-default-address-oasis';
}

constructor(public readonly chainId: string) {
super();
}

validateBasic(): void {
if (!this.chainId) {
throw new OWalletError('keyring', 270, 'chain id not set');
}
}

route(): string {
return ROUTE;
}

type(): string {
return GetDefaultAddressOasisMsg.type();
}
}
26 changes: 24 additions & 2 deletions src/keyring/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { request } from '../tx';
import { Dec, DecUtils } from '@owallet/unit';
import { trimAminoSignDoc } from './amino-sign-doc';
import { KeyringHelper } from './utils';
import * as oasis from '@oasisprotocol/client';

@singleton()
export class KeyRingService {
private readonly keyRing: KeyRing;
Expand Down Expand Up @@ -513,8 +515,7 @@ export class KeyRingService {

async requestPublicKey(env: Env, chainId: string): Promise<string> {
try {
const rawTxHex = await this.keyRing.getPublicKey(chainId);

const rawTxHex = (await this.keyRing.getPublicKey(chainId)) as string;
return rawTxHex;
} catch (e) {
console.log('e', e.message);
Expand Down Expand Up @@ -848,4 +849,25 @@ export class KeyRingService {
this.interactionService.dispatchEvent(APP_PORT, 'request-sign-tron-end', {});
}
}

async requestSignOasis(env: Env, chainId: string, data: object): Promise<object> {
try {
const tx = await this.keyRing.signOasis(chainId, data);
return tx;
} finally {
this.interactionService.dispatchEvent(APP_PORT, 'request-sign-oasis-end', {});
}
}

async getDefaultOasisAddress(chainId: string): Promise<string> {
const signerPublicKey = await this.keyRing.loadPublicKeyOasis();
const addressUint8Array = await oasis.staking.addressFromPublicKey(signerPublicKey);
const address = oasis.staking.addressToBech32(addressUint8Array);
return address;
}

async getDefaultOasisBalance(address: string, chainId: string): Promise<string> {
const balance = await this.keyRing.loadBalanceOasis(address, chainId);
return balance.available;
}
}
13 changes: 12 additions & 1 deletion src/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { LedgerAppType, getNetworkTypeByChainId, typeBtcLedgerByAddress } from '
import { AddressBtcType, AddressesLedger, BIP44HDPath, ChainInfoWithoutEndpoints } from '@owallet/types';
import Joi from 'joi';
import { ChainInfoWithEmbed } from 'src/chains';
import * as oasis from '@oasisprotocol/client';

export const EIP712DomainTypeValidator = Joi.array()
.items(
Joi.object<{
Expand Down Expand Up @@ -48,7 +50,7 @@ export const EIP712DomainTypeValidator = Joi.array()
)
.unique()
.min(1)
.custom((value) => {
.custom(value => {
// Sort by name
const domainFieldNames: Array<string> = ['name', 'version', 'chainId', 'verifyingContract', 'salt'];

Expand Down Expand Up @@ -158,3 +160,12 @@ export const handleAddressLedgerByChainId = (
[typeBtcLedgerByAddress(chainInfo, addressType)]: address
};
};

/**
* Return a nic client for the specified network,
* or by default, for the currently selected network
*/
export const getOasisNic = async url => {
const nic = await new oasis.client.NodeInternal(url);
return nic;
};
Loading

0 comments on commit ecba32c

Please sign in to comment.