Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(app): improve accounts from private key fetching #416

Merged
merged 4 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/config/config.declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ declare module "@peersyst/react-native-components" {
export interface Config {
minimumTransactionAmount: number;
indexerTestnetUrl: string;
nearblocksTesnetApiUrl: string;
indexerMainnetUrl: string;
nearblocksMainnetApiUrl: string;
mainnetExplorerLink: string;
testnetExplorerLink: string;
faucetUrl: string;
Expand Down Expand Up @@ -74,7 +76,9 @@ declare module "@peersyst/react-native-components" {
export interface CreateConfig {
minimumTransactionAmount: number;
indexerTestnetUrl: string;
nearblocksTesnetApiUrl: string;
indexerMainnetUrl: string;
nearblocksMainnetApiUrl: string;
mainnetExplorerLink: string;
testnetExplorerLink: string;
faucetUrl: string;
Expand Down
2 changes: 2 additions & 0 deletions src/config/configs/config.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"mainnetExplorerLink": "https://explorer.mainnet.near.org",
"testnetExplorerLink": "https://explorer.testnet.near.org",
"indexerTestnetUrl": "https://testnet-api.kitwallet.app",
"nearblocksTesnetApiUrl": "https://api-testnet.nearblocks.io/v1",
"indexerMainnetUrl": "https://api.kitwallet.app",
"nearblocksMainnetApiUrl": "https://api.nearblocks.io/v1",
"faucetUrl": "https://faucet.nervos.org/",
"mainnetNodeUrl": "https://rpc.mainnet.near.org",
"testnetNodeUrl": "https://rpc.testnet.near.org",
Expand Down
17 changes: 14 additions & 3 deletions src/module/sdk/NearApiService/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TransactionWithoutActions,
} from "../NearSdkService";
import { convertYoctoToNear, parseBlockTimestamp } from "../utils";
import { FetchService } from "./FetchService";
import { FetchService, FetchError } from "./FetchService";
import {
AccessKeyApiDto,
LikelyResponseApiDto,
Expand All @@ -19,12 +19,16 @@ import {
StakingDepositApiDto,
ActionApiDto,
} from "./NearApiService.types";
import { NearBlocksService } from "./NearBlocksService";

export class ApiService extends FetchService implements NearApiServiceInterface {
public baseUrl: string;
constructor(endpoint: string) {
nearblocksService: NearBlocksService;

constructor(endpoint: string, chain: Chains) {
super();
this.baseUrl = endpoint;
this.nearblocksService = new NearBlocksService(chain);
}

/**
Expand Down Expand Up @@ -107,7 +111,14 @@ export class ApiService extends FetchService implements NearApiServiceInterface
*/

async getAccountsFromPublicKey({ address }: NearApiServiceParams): Promise<string[]> {
const accounts = await this.handleFetch<string[]>(`${this.baseUrl}/publicKey/${address}/accounts`);
let accounts: string[] = [];
try {
accounts = await this.nearblocksService.getAccountsFromPublicKey({ address });
} catch (error: unknown) {
if (error instanceof FetchError && error.code === 429) {
accounts = await this.handleFetch<string[]>(`${this.baseUrl}/publicKey/${address}/accounts`);
}
}
return this.parseNearAccounts(accounts);
}

Expand Down
28 changes: 24 additions & 4 deletions src/module/sdk/NearApiService/FetchService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
export class FetchService {
protected async handleFetch<T>(url: string): Promise<T> {
const response: Response = await fetch(url);
if (response.status !== 200) throw new Error(`Error ${response.status} ${response.statusText}`);
const data = await response.json();
return data as T;
try {
const response: Response = await fetch(url);
switch (response.status) {
case 200:
return (await response.json()) as Promise<T>;
case 429:
throw new FetchError(429, "Too many requests");
default:
throw new FetchError(response.status, response.statusText);
}
} catch (error: unknown) {
if (error instanceof Error) {
throw new FetchError(500, error.message);
}
throw new FetchError(500, "Unknown error");
}
}
}

export class FetchError extends Error {
code: number;
constructor(code: number, message: string) {
super(message);
this.code = code;
}
}
18 changes: 18 additions & 0 deletions src/module/sdk/NearApiService/NearApiService.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,21 @@ export interface StakingDepositApiDto {
validator_id: string;
deposit: string;
}

export interface NearblocksAccessKeyResponseDto {
keys: NearblocksAccessKeyDto[];
}

export interface NearblocksAccessKeyDto {
public_key: string;
account_id: string;
permission_kind: "FULL_ACCESS" | "FUNCTION_CALL";
created: {
transaction_hash: string;
block_timestamp: number;
};
deleted: {
transaction_hash: null | string;
block_timestamp: null | number;
};
}
40 changes: 40 additions & 0 deletions src/module/sdk/NearApiService/NearBlocksService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Do not `import {config} from "config"` in this file
* because it will cause a circular dependency with SettingsState
*/
import config from "../../../config/config";
import { Chains } from "../NearSdkService";
import { FetchService } from "./FetchService";
import { NearApiServiceParams, NearblocksAccessKeyResponseDto } from "./NearApiService.types";

export class NearBlocksService extends FetchService {
public chain: Chains;
public testnetUrl = config.nearblocksTesnetApiUrl;
public mainnetUrl = config.nearblocksMainnetApiUrl;

constructor(chain: Chains) {
super();
this.chain = chain;
}

private getNearblocksApiUrlFromChain(): string {
return this.chain === Chains.MAINNET ? this.mainnetUrl : this.testnetUrl;
}

private fetch<T>(path: string): Promise<T> {
const nearBlocksApi = this.getNearblocksApiUrlFromChain();
return this.handleFetch<T>(`${nearBlocksApi}${path}`);
}

async getAccountsFromPublicKey({ address }: NearApiServiceParams): Promise<string[]> {
const accounts: string[] = [];
const keys = await this.fetch<NearblocksAccessKeyResponseDto>(`/keys/${address}`);
if (!keys?.keys || keys.keys.length === 0) return accounts;
for (const key of keys.keys) {
if (key.permission_kind === "FULL_ACCESS") {
accounts.push(key.account_id);
}
}
return accounts;
}
}
16 changes: 9 additions & 7 deletions src/module/sdk/NearSdkService/NearSdkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class NearSDKService {
this.nameId = nameId;
this.mnemonic = mnemonic;
this.baseApiUrl = baseApiUrl;
this.apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer);
this.apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer, chain);
this.nearDecimals = nearDecimals;
this.enableIndexer = enableIndexer;
// Create KeyPairEd25519
Expand All @@ -111,8 +111,8 @@ export class NearSDKService {
return decode(encode(publicKey.data)).toString("hex");
}

static createApiService(baseApiUrl: string, enableIndexer: boolean): NearApiServiceInterface {
return enableIndexer ? new IndexerService(baseApiUrl) : new ApiService(baseApiUrl);
static createApiService(baseApiUrl: string, enableIndexer: boolean, chain: Chains): NearApiServiceInterface {
return enableIndexer ? new IndexerService(baseApiUrl) : new ApiService(baseApiUrl, chain);
}

static async createAndConnect(params: BaseCreateNearSdkParams): Promise<NearSDKService> {
Expand All @@ -127,16 +127,17 @@ export class NearSDKService {
mnemonic,
baseApiUrl,
enableIndexer,
chain,
...rest
}: CreateNearSdkWithMnemonicParams): Promise<NearSDKService[]> {
const { secretKey, publicKey } = parseSeedPhrase(mnemonic);
const apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer);
const apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer, chain);
const nameIds = await apiService.getAccountsFromPublicKey({ address: publicKey });
if (nameIds.length === 0) {
nameIds.push(NearSDKService.getAddressFromPublicKey(PublicKey.fromString(publicKey)));
}
const services = nameIds.map(async (nameId) => {
const service = new NearSDKService({ baseApiUrl, secretKey, nameId, mnemonic, enableIndexer, ...rest });
const service = new NearSDKService({ baseApiUrl, secretKey, nameId, mnemonic, enableIndexer, chain, ...rest });
await service.connect();
return service;
});
Expand All @@ -147,17 +148,18 @@ export class NearSDKService {
secretKey,
baseApiUrl,
enableIndexer,
chain,
...rest
}: CreateNearSdkWithSecretKeyParams): Promise<NearSDKService[]> {
const secret = secretKey.split(":").pop();
const publicKey = new KeyPairEd25519(secret!).getPublicKey().toString();
const apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer);
const apiService = NearSDKService.createApiService(baseApiUrl, enableIndexer, chain);
const nameIds = await apiService.getAccountsFromPublicKey({ address: publicKey });
if (nameIds.length === 0) {
nameIds.push(NearSDKService.getAddressFromPublicKey(PublicKey.fromString(publicKey)));
}
const services = nameIds.map(async (nameId) => {
const service = new NearSDKService({ ...rest, baseApiUrl, secretKey, nameId, enableIndexer });
const service = new NearSDKService({ ...rest, baseApiUrl, secretKey, nameId, enableIndexer, chain });
await service.connect();
return service;
});
Expand Down
4 changes: 2 additions & 2 deletions src/module/token/component/display/TokenCard/TokenCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const TokenCard = ({ token }: TokenCardProps): JSX.Element => {
{name}
</Typography>
</Row>
<Col alignItems="flex-end" justifyContent="center" gap={2}>
<Balance balance={token.balance} variant="body3Strong" units={symbol} />
<Col alignItems="flex-end" justifyContent="center" gap={2} flex={1}>
<Balance balance={token.balance} variant="body3Strong" textAlign="right" units={symbol} numberOfLines={undefined} />
<FiatBalance light balance={token.balance} token={token} variant="body4Strong" />
</Col>
</MainListCard>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface BalanceThreshold {
decimals: number;
}

export interface BalanceProps extends Omit<TypographyProps, "children" | "numberOfLines"> {
export interface BalanceProps extends Omit<TypographyProps, "children"> {
balance: FullNumber;
units?: AppCurrency | string;
unitsPosition?: "left" | "right";
Expand Down
2 changes: 1 addition & 1 deletion src/module/wallet/wallet.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface UnencryptedWalletInfo extends BaseWallet {
}

export interface UnsecureWalletStorageType {
testnet: [];
testnet: UnencryptedWalletInfo[];
mainnet: UnencryptedWalletInfo[];
}

Expand Down
Loading