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

Bugfix/tx history restore bump #945

Merged
merged 11 commits into from
Aug 18, 2023
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
31 changes: 0 additions & 31 deletions @shared/api/helpers/soroban.ts

This file was deleted.

151 changes: 61 additions & 90 deletions @shared/api/internal.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import StellarSdk from "stellar-sdk";
import * as SorobanClient from "soroban-client";
import { DataProvider } from "@stellar/wallet-sdk";
import {
getBalance,
getDecimals,
getName,
getSymbol,
} from "@shared/helpers/soroban/token";
import {
Account,
AccountBalancesInterface,
AccountHistoryInterface,
Balances,
HorizonOperation,
Settings,
SorobanTxStatus,
} from "./types";
import {
MAINNET_NETWORK_DETAILS,
Expand All @@ -24,10 +29,25 @@ import { getIconUrlFromIssuer } from "./helpers/getIconUrlFromIssuer";
import { getDomainFromIssuer } from "./helpers/getDomainFromIssuer";
import { stellarSdkServer } from "./helpers/stellarSdkServer";

import { decodei128, decodeU32, decodeStr } from "./helpers/soroban";

const TRANSACTIONS_LIMIT = 100;

export const SendTxStatus: {
[index: string]: SorobanClient.SorobanRpc.SendTransactionStatus;
} = {
Pending: "PENDING",
Duplicate: "DUPLICATE",
Retry: "TRY_AGAIN_LATER",
Error: "ERROR",
};

export const GetTxStatus: {
[index: string]: SorobanClient.SorobanRpc.GetTransactionStatus;
} = {
Success: "SUCCESS",
NotFound: "NOT_FOUND",
Failed: "FAILED",
};

export const createAccount = async (
password: string,
): Promise<{ publicKey: string; allAccounts: Array<Account> }> => {
Expand Down Expand Up @@ -610,24 +630,32 @@ export const submitFreighterSorobanTransaction = async ({
allowHttp: true,
});

// TODO: fixed in Sorobanclient, not yet released
let response = (await server.sendTransaction(tx)) as any;
let response = await server.sendTransaction(tx);

try {
// Poll this until the status is not "pending"
while (response.status === SorobanTxStatus.PENDING) {
if (response.errorResultXdr) {
throw new Error(response.errorResultXdr);
}

if (response.status === SendTxStatus.Pending) {
let txResponse = await server.getTransaction(response.hash);

// Poll this until the status is not "NOT_FOUND"
while (txResponse.status === GetTxStatus.NotFound) {
// See if the transaction is complete
// eslint-disable-next-line no-await-in-loop
response = await server.getTransaction(response.id);
txResponse = await server.getTransaction(response.hash);
// Wait a second
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => setTimeout(resolve, 1000));
}
} catch (e) {
throw new Error(e);
}

return response;
return response;
// eslint-disable-next-line no-else-return
} else {
throw new Error(
`Unabled to submit transaction, status: ${response.status}`,
);
}
};

export const addRecentAddress = async ({
Expand Down Expand Up @@ -859,94 +887,37 @@ export const getBlockedAccounts = async () => {
return resp;
};

type TxToOp = {
[index: string]: {
tx: SorobanClient.Transaction<
SorobanClient.Memo<SorobanClient.MemoType>,
SorobanClient.Operation[]
>;
decoder: (xdr: string) => string | number;
};
};

interface SorobanTokenRecord {
[key: string]: unknown;
balance: number;
name: string;
symbol: string;
decimals: string;
}

export const getSorobanTokenBalance = (
export const getSorobanTokenBalance = async (
server: SorobanClient.Server,
contractId: string,
txBuilders: {
// need a builder per operation until multi-op transactions are released
// need a builder per operation, Soroban currently has single op transactions
balance: SorobanClient.TransactionBuilder;
name: SorobanClient.TransactionBuilder;
decimals: SorobanClient.TransactionBuilder;
symbol: SorobanClient.TransactionBuilder;
},
params: SorobanClient.xdr.ScVal[],
balanceParams: SorobanClient.xdr.ScVal[],
) => {
const contract = new SorobanClient.Contract(contractId);

// Right now we can only have 1 operation per TX in Soroban
// There is ongoing work to lift this restriction
// but for now we need to do 4 txs to show 1 user balance. :(
const balanceTx = txBuilders.balance
.addOperation(contract.call("balance", ...params))
.setTimeout(SorobanClient.TimeoutInfinite)
.build();

const nameTx = txBuilders.name
.addOperation(contract.call("name"))
.setTimeout(SorobanClient.TimeoutInfinite)
.build();

const symbolTx = txBuilders.symbol
.addOperation(contract.call("symbol"))
.setTimeout(SorobanClient.TimeoutInfinite)
.build();

const decimalsTx = txBuilders.decimals
.addOperation(contract.call("decimals"))
.setTimeout(SorobanClient.TimeoutInfinite)
.build();

const txs: TxToOp = {
balance: {
tx: balanceTx,
decoder: decodei128,
},
name: {
tx: nameTx,
decoder: decodeStr,
},
symbol: {
tx: symbolTx,
decoder: decodeStr,
},
decimals: {
tx: decimalsTx,
decoder: decodeU32,
},
};

const tokenBalanceInfo = Object.keys(txs).reduce(async (prev, curr) => {
const _prev = await prev;
const { tx, decoder } = txs[curr];
const { results } = await server.simulateTransaction(tx);
if (!results || results.length !== 1) {
throw new Error("Invalid response from simulateTransaction");
}
const result = results[0];
_prev[curr] = decoder(result.xdr);

return _prev;
}, Promise.resolve({} as SorobanTokenRecord));
// for now we need to do 4 tx simulations to show 1 user balance. :(
// TODO: figure out how to fetch ledger keys to do this more efficiently
const decimals = await getDecimals(contractId, server, txBuilders.decimals);
const name = await getName(contractId, server, txBuilders.name);
const symbol = await getSymbol(contractId, server, txBuilders.symbol);
const balance = await getBalance(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice - I find this pattern a lot more straightforward!

contractId,
balanceParams,
server,
txBuilders.balance,
);

return tokenBalanceInfo;
return {
balance,
decimals,
name,
symbol,
};
};

export const addTokenId = async (
Expand Down
2 changes: 1 addition & 1 deletion @shared/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@stellar/wallet-sdk": "^0.8.0",
"bignumber.js": "^9.1.1",
"prettier": "^2.0.5",
"soroban-client": "^0.9.1",
"soroban-client": "^0.9.2",
"stellar-sdk": "^10.4.1",
"typescript": "~3.7.2",
"webextension-polyfill": "^0.10.0"
Expand Down
7 changes: 1 addition & 6 deletions @shared/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ export enum ActionStatus {
ERROR = "ERROR",
}

export enum SorobanTxStatus {
PENDING = "pending",
SUCCESS = "success",
}

export interface UserInfo {
publicKey: string;
}
Expand Down Expand Up @@ -148,7 +143,7 @@ export interface SorobanBalance {
total: BigNumber;
name: string;
symbol: string;
decimals: string;
decimals: number;
}

export type AssetType =
Expand Down
17 changes: 17 additions & 0 deletions @shared/constants/soroban/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://github.com/stellar/soroban-examples/blob/main/token/src/contract.rs
export enum SorobanTokenInterface {
transfer = "transfer",
mint = "mint",
}

// TODO: can we generate this at build time using the cli TS generator? Also should we?
export interface SorobanToken {
// only currently holds fields we care about
transfer: (from: string, to: string, amount: number) => void;
mint: (to: string, amount: number) => void;
// values below are in storage
name: string;
balance: number;
symbol: string;
decimals: number;
}
23 changes: 23 additions & 0 deletions @shared/helpers/soroban/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
Transaction,
Memo,
MemoType,
Operation,
Server,
xdr,
scValToNative,
} from "soroban-client";

export const simulateTx = async <ArgType>(
tx: Transaction<Memo<MemoType>, Operation[]>,
server: Server,
): Promise<ArgType> => {
const { results } = await server.simulateTransaction(tx);
if (!results || results.length !== 1) {
throw new Error("Invalid response from simulateTransaction");
}
const result = results[0];
const scVal = xdr.ScVal.fromXDR(result.xdr, "base64");

return scValToNative(scVal);
};
Loading