Skip to content

Commit

Permalink
Constrain token payment by auth entries (#31)
Browse files Browse the repository at this point in the history
* adds checks for address credentials auth entries, auth entries contractId mismatch, and sub invocations to token payment simulation

* capitalize token sim error strings

* remove check for sorobanCredentialsAddress, already covered by the previous check

* adds explicit check for op type in token sim, updates jwt expired error from mercury, removes unused token sim error

* reorganized token details getter to continue on error to get a tokens details

* tweak error display text for auth sub invocations
  • Loading branch information
aristidesstaffieri authored Feb 8, 2024
1 parent db55b6b commit 4ffc981
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/helper/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const ERROR = {
ACCOUNT_NOT_SOURCE:
"Transfer contains authorization entry for a different account",
AUTH_SUB_INVOCATIONS:
"Transfer authorizes sub-invocations to another contract",
};
2 changes: 1 addition & 1 deletion src/helper/horizon-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ export const submitTransaction = async (
error: null,
};
} catch (e: any) {
if (e.response.status === 504) {
if (e.response?.status === 504) {
// in case of 504, keep retrying this tx until submission succeeds or we get a different error
// https://developers.stellar.org/api/errors/http-status-codes/horizon-specific/timeout
// https://developers.stellar.org/docs/encyclopedia/error-handling
Expand Down
30 changes: 28 additions & 2 deletions src/route/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import {
Transaction,
TransactionBuilder,
XdrLargeInt,
xdr,
} from "stellar-sdk";
import { buildTransfer, simulateTx } from "../helper/soroban-rpc";
import { ERROR } from "../helper/error";

const API_VERSION = "v1";

Expand Down Expand Up @@ -545,17 +547,41 @@ export async function initApiServer(
const simulationResponse = (await server.simulateTransaction(
tx
)) as SorobanRpc.Api.SimulateTransactionSuccessResponse;

const preparedTransaction = SorobanRpc.assembleTransaction(
tx,
simulationResponse
);

const built = preparedTransaction.build();
switch (built.operations[0].type) {
case "invokeHostFunction": {
const sorobanOp = built
.operations[0] as Operation.InvokeHostFunction;
const auths = sorobanOp.auth || [];

for (const auth of auths) {
if (
auth.credentials().switch() !==
xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount()
) {
throw new Error(ERROR.ACCOUNT_NOT_SOURCE);
}

if (auth.rootInvocation().subInvocations().length) {
throw new Error(ERROR.AUTH_SUB_INVOCATIONS);
}
}
}
}

const data = {
simulationResponse,
preparedTransaction: preparedTransaction.build().toXDR(),
preparedTransaction: built.toXDR(),
};
reply.code(200).send(data);
} catch (error) {
reply.code(400).send(JSON.stringify(error));
reply.code(400).send(error);
}
},
});
Expand Down
18 changes: 10 additions & 8 deletions src/service/mercury/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ enum NETWORK_URLS {
}

const ERROR_MESSAGES = {
JWT_EXPIRED: "jwt expired",
JWT_EXPIRED: "1_kJdMBB7ytvgRIqF1clh2iz2iI",
};

function getGraphQlError(error?: CombinedError) {
Expand Down Expand Up @@ -179,7 +179,7 @@ export class MercuryClient {
} catch (error: unknown) {
// renew and retry 0n 401, otherwise throw the error back up to the caller
if (error instanceof Error) {
if (error.message === ERROR_MESSAGES.JWT_EXPIRED) {
if (error.message.includes(ERROR_MESSAGES.JWT_EXPIRED)) {
await this.renewMercuryToken();
this.logger.info("renewed expired jwt");
return await method();
Expand Down Expand Up @@ -518,9 +518,10 @@ export class MercuryClient {
) => {
const balances = [];
const balanceMap = {} as Record<string, any>;
try {
const server = await getServer(network, customSorobanRpcUrl);
for (const id of contractIds) {

const server = await getServer(network, customSorobanRpcUrl);
for (const id of contractIds) {
try {
const builder = await getTxBuilder(pubKey, network, server);
const params = [new Address(pubKey).toScVal()];
const balance = await getTokenBalance(id, params, server, builder);
Expand All @@ -530,11 +531,12 @@ export class MercuryClient {
balance,
...tokenDetails,
});
} catch (error) {
this.logger.error(error);
continue;
}
} catch (error) {
this.logger.error(error);
return balanceMap;
}

for (const balance of balances) {
balanceMap[`${balance.symbol}:${balance.id}`] = {
token: {
Expand Down

0 comments on commit 4ffc981

Please sign in to comment.