From 71da2a872f8e6a7b62478017b1a682cde3a1919d Mon Sep 17 00:00:00 2001
From: Jan Marcano
Date: Thu, 7 Oct 2021 11:39:17 -0300
Subject: [PATCH 1/7] Display Info for Inner Transactions
---
.../components/Account/TransactionsList.ts | 56 +++++++++++++++----
.../components/TransactionDetail/TxAppl.ts | 7 +++
2 files changed, 53 insertions(+), 10 deletions(-)
diff --git a/packages/ui/src/components/Account/TransactionsList.ts b/packages/ui/src/components/Account/TransactionsList.ts
index 4839704f..2fd1a94c 100644
--- a/packages/ui/src/components/Account/TransactionsList.ts
+++ b/packages/ui/src/components/Account/TransactionsList.ts
@@ -14,6 +14,14 @@ import TxAfrz from 'components/TransactionDetail/TxAfrz';
import TxAppl from 'components/TransactionDetail/TxAppl';
const BACKGROUND_REFRESH_TIMER: number = 10000;
+const INNER_TXN_DESCRIPTIONS = {
+ axfer: 'Asset Transfer',
+ pay: 'Payment',
+ afrz: 'Asset Freeze',
+ keyreg: 'Key Registration',
+ acfg: 'Asset Config',
+ appl: 'Application',
+};
const TransactionsList: FunctionalComponent = (props: any) => {
const store: any = useContext(StoreContext);
@@ -111,7 +119,7 @@ const TransactionsList: FunctionalComponent = (props: any) => {
fetchApi();
};
- const getTxInfo = (tx, date) => {
+ const getTxInfo = (tx, date): any => {
function getTime(date, roundTime) {
const MINUTESTHRESHOLD = 60000;
const HOURSTHRESHOLD = 3600000;
@@ -139,7 +147,10 @@ const TransactionsList: FunctionalComponent = (props: any) => {
}
}
+ const id = tx.id;
+ const time = getTime(date, tx['round-time']);
let title, subtitle, info;
+
switch (tx['tx-type']) {
case 'pay':
info = tx['payment-transaction'].amount / 1e6 + ' Algos';
@@ -161,15 +172,14 @@ const TransactionsList: FunctionalComponent = (props: any) => {
case 'axfer':
info = tx['asset-transfer-transaction']['amount'];
store.getAssetDetails(ledger, address, (assets) => {
- const id = tx['asset-transfer-transaction']['asset-id'];
+ const id = tx['asset-transfer-transaction']['asset-id'];
// Check if the asset has not been deleted before getting info about it
- if (assets[id]) {
+ if (assets[id]) {
const dec = assets[id].decimals || 0;
const amount = info / Math.pow(10, dec);
info = `${amount} ${assets[id].unitName}`;
}
-
});
// TODO Close-to txs
// Clawback if there is a sender in the transfer object
@@ -211,21 +221,47 @@ const TransactionsList: FunctionalComponent = (props: any) => {
break;
}
+ return {
+ title,
+ subtitle,
+ info,
+ id,
+ time,
+ };
+ };
+
+ const getTxTemplate = (tx, date) => {
+ const { title, subtitle, info, id, time } = getTxInfo(tx, date);
return html`
-
+
-
${subtitle}
+ ${subtitle}
${title}
-
- ${getTime(date, tx['round-time'])}
-
+ ${time}
${info}
+ ${tx['inner-txns'] &&
+ html`
+
Inner Transaction(s):
+
+ ${tx['inner-txns'].map(
+ (inner: any) => {
+ const innerTxInfo = getTxInfo(inner, date);
+ return html`
+
+
+
${INNER_TXN_DESCRIPTIONS[inner['tx-type']]}
+
${innerTxInfo.info}
+
+ `;}
+ )}
+
+ `}
`;
};
@@ -324,7 +360,7 @@ const TransactionsList: FunctionalComponent = (props: any) => {
style="border-top: 1px solid rgba(138, 159, 168, 0.2); cursor: pointer;"
onClick=${() => handleClick(tx)}
>
- ${getTxInfo(tx, date)}
+ ${getTxTemplate(tx, date)}
`
)}
diff --git a/packages/ui/src/components/TransactionDetail/TxAppl.ts b/packages/ui/src/components/TransactionDetail/TxAppl.ts
index a90ae0c3..b39afe22 100644
--- a/packages/ui/src/components/TransactionDetail/TxAppl.ts
+++ b/packages/ui/src/components/TransactionDetail/TxAppl.ts
@@ -23,6 +23,13 @@ const TxAppl: FunctionalComponent = (props: any) => {
On Completion:
${tx['application-transaction']['on-completion']}
+ ${tx['inner-txns'].length &&
+ html`
+
+ Inner Transactions:
+ ${tx['inner-txns'].length}
+
+ `}
Block:
${tx['confirmed-round']}
From 7b9441c55c73d4f3f0cdf7c33b34e5b10c3782c1 Mon Sep 17 00:00:00 2001
From: Jan Marcano
Date: Thu, 7 Oct 2021 11:50:39 -0300
Subject: [PATCH 2/7] Add Null-safe validation
---
packages/ui/src/components/TransactionDetail/TxAppl.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/ui/src/components/TransactionDetail/TxAppl.ts b/packages/ui/src/components/TransactionDetail/TxAppl.ts
index b39afe22..9d485fd0 100644
--- a/packages/ui/src/components/TransactionDetail/TxAppl.ts
+++ b/packages/ui/src/components/TransactionDetail/TxAppl.ts
@@ -23,7 +23,7 @@ const TxAppl: FunctionalComponent = (props: any) => {
On Completion:
${tx['application-transaction']['on-completion']}
- ${tx['inner-txns'].length &&
+ ${tx['inner-txns'] && tx['inner-txns'].length &&
html`
Inner Transactions:
From 4a7f6850bd558f0c77f4079bcaf8c392842a4ce8 Mon Sep 17 00:00:00 2001
From: Brent
Date: Tue, 12 Oct 2021 15:57:32 -0400
Subject: [PATCH 3/7] Various network and ledger fixes
---
packages/common/src/messaging/types.ts | 1 +
packages/extension/src/background/config.ts | 32 +++-
.../background/messaging/internalMethods.ts | 10 ++
.../src/background/messaging/task.ts | 152 ++++++++++++++++--
.../background/utils/networkUrlParser.test.ts | 25 ++-
.../src/background/utils/networkUrlParser.ts | 20 ++-
.../LedgerDevice/LedgerHardwareSign.ts | 21 ++-
.../LedgerDevice/structure/ledgerActions.ts | 23 +++
.../ui/src/components/LedgerNetworkModify.ts | 38 +++++
9 files changed, 284 insertions(+), 38 deletions(-)
diff --git a/packages/common/src/messaging/types.ts b/packages/common/src/messaging/types.ts
index c4e8ea55..bb41ef86 100644
--- a/packages/common/src/messaging/types.ts
+++ b/packages/common/src/messaging/types.ts
@@ -37,6 +37,7 @@ export enum JsonRpcMethod {
SignSendTransaction = 'sign-send-transaction',
ChangeLedger = 'change-ledger',
SaveNetwork = 'save-network',
+ CheckNetwork = 'check-network',
DeleteNetwork = 'delete-network',
GetLedgers = 'get-ledgers',
diff --git a/packages/extension/src/background/config.ts b/packages/extension/src/background/config.ts
index 17906626..1f3809fc 100644
--- a/packages/extension/src/background/config.ts
+++ b/packages/extension/src/background/config.ts
@@ -1,3 +1,4 @@
+import logging from '@algosigner/common/logging';
import { LedgerTemplate } from '@algosigner/common/types/ledgers';
import { Ledger, Backend, API } from './messaging/types';
import { parseUrlServerAndPort } from './utils/networkUrlParser';
@@ -49,8 +50,8 @@ export class Settings {
return injectedNetworks;
}
- private static setInjectedHeaders(ledger: LedgerTemplate) {
- if (!this.backend_settings.InjectedNetworks[ledger.name]) {
+ private static setInjectedHeaders(ledger: LedgerTemplate, isCheckOnly?: boolean) {
+ if (!this.backend_settings.InjectedNetworks[ledger.name] && !isCheckOnly) {
console.log('Error: Ledger headers can not be updated. Ledger not available.');
return;
}
@@ -93,8 +94,9 @@ export class Settings {
// Setup port splits for algod and indexer - used in sandbox installs
const parsedAlgodUrlObj = parseUrlServerAndPort(ledger.algodUrl)
const parsedIndexerUrlObj = parseUrlServerAndPort(ledger.indexerUrl);
-
- this.backend_settings.InjectedNetworks[ledger.name][API.Algod] = {
+
+ // Add algod links
+ const injectedAlgod = {
url: parsedAlgodUrlObj.server || `${defaultUrl}/algod`,
port: parsedAlgodUrlObj.port,
apiKey: headersAlgod || headers,
@@ -102,14 +104,24 @@ export class Settings {
};
// Add the indexer links
- this.backend_settings.InjectedNetworks[ledger.name][API.Indexer] = {
+ const injectedIndexer = {
url: parsedIndexerUrlObj.server || `${defaultUrl}/indexer`,
port: parsedIndexerUrlObj.port,
apiKey: headersIndexer || headers,
headers: headersIndexer || headers,
};
- this.backend_settings.InjectedNetworks[ledger.name].headers = headers;
+ if (isCheckOnly) {
+ return {
+ 'algod': injectedAlgod,
+ 'indexer': injectedIndexer
+ }
+ }
+ else {
+ this.backend_settings.InjectedNetworks[ledger.name][API.Algod] = injectedAlgod;
+ this.backend_settings.InjectedNetworks[ledger.name][API.Indexer] = injectedIndexer;
+ this.backend_settings.InjectedNetworks[ledger.name].headers = headers;
+ }
}
public static addInjectedNetwork(ledger: LedgerTemplate) {
@@ -120,6 +132,7 @@ export class Settings {
};
this.setInjectedHeaders(ledger);
+ logging.log(`Added Network:\n${JSON.stringify(this.backend_settings.InjectedNetworks[ledger.name],null,1)}`,2);
}
public static updateInjectedNetwork(updatedLedger: LedgerTemplate) {
@@ -131,6 +144,8 @@ export class Settings {
this.backend_settings.InjectedNetworks[updatedLedger.name].indexerUrl =
updatedLedger.indexerUrl;
this.setInjectedHeaders(updatedLedger);
+
+ logging.log(`Updated Network:\n${JSON.stringify(this.backend_settings.InjectedNetworks[updatedLedger.name],null,1)}`,2);
}
public static getBackendParams(ledger: string, api: API) {
@@ -152,4 +167,9 @@ export class Settings {
headers: this.backend_settings.InjectedNetworks[ledger][api].headers,
};
}
+
+ public static checkNetwork(ledger: LedgerTemplate) {
+ const networks = this.setInjectedHeaders(ledger, true);
+ return networks;
+ }
}
diff --git a/packages/extension/src/background/messaging/internalMethods.ts b/packages/extension/src/background/messaging/internalMethods.ts
index 50bf87ea..0f863b23 100644
--- a/packages/extension/src/background/messaging/internalMethods.ts
+++ b/packages/extension/src/background/messaging/internalMethods.ts
@@ -865,6 +865,16 @@ export class InternalMethods {
return true;
}
+ public static [JsonRpcMethod.CheckNetwork](request: any, sendResponse: Function) {
+ try {
+ const networks = Settings.checkNetwork(request.body.params)
+ sendResponse(networks);
+ }
+ catch (e) {
+ sendResponse({ error: e.message });
+ }
+ }
+
public static [JsonRpcMethod.SaveNetwork](request: any, sendResponse: Function) {
try {
// If we have a passphrase then we are modifying.
diff --git a/packages/extension/src/background/messaging/task.ts b/packages/extension/src/background/messaging/task.ts
index aca50fb2..563095cb 100644
--- a/packages/extension/src/background/messaging/task.ts
+++ b/packages/extension/src/background/messaging/task.ts
@@ -1174,6 +1174,7 @@ export class Task {
const transactionObjs = walletTransactions.map((walletTx) =>
algosdk.decodeUnsignedTransaction(base64ToByteArray(walletTx.txn))
);
+ let holdResponse = false;
const signedTxs = [];
const signErrors = [];
@@ -1223,14 +1224,22 @@ export class Task {
message.error = RequestErrors.UnsupportedLedger;
MessageApi.send(message);
}
+
+ const hardwareAccounts = [];
+
// Find addresses to send algos from
// We store them using the public address as dictionary key
for (let i = unlockedValue[ledger].length - 1; i >= 0; i--) {
- const address = unlockedValue[ledger][i].address;
- if (neededAccounts.includes(address)) {
- recoveredAccounts[address] = algosdk.mnemonicToSecretKey(
- unlockedValue[ledger][i].mnemonic
- );
+ const account = unlockedValue[ledger][i];
+ if (neededAccounts.includes(account.address)) {
+ if (!account.isHardware) {
+ recoveredAccounts[account.address] = algosdk.mnemonicToSecretKey(
+ unlockedValue[ledger][i].mnemonic
+ );
+ }
+ else {
+ hardwareAccounts.push(account.address);
+ }
}
}
@@ -1270,14 +1279,39 @@ export class Task {
}
} else {
const address = wrap.transaction.from;
- signedBlob = tx.signTxn(recoveredAccounts[address].sk);
+ if(recoveredAccounts[address]) {
+ signedBlob = tx.signTxn(recoveredAccounts[address].sk);
+ const b64Obj = byteArrayToBase64(signedBlob);
+
+ signedTxs[index] = {
+ txID: txID,
+ blob: b64Obj,
+ };
+ }
+ else if(hardwareAccounts.some((a) => a === address)) {
+ // Limit to single group transactions
+ if(!singleGroup){
+ throw Error("Ledger hardware device signing not available for multiple transactions.");
+ }
+
+ // Now that we know it is a single group adjust the transaction property to be the current wrap
+ // This will be where the transaction presented to the user
+ message.body.params.transaction = wrap;
+
+ // The account is hardware based. We need to open the extension in tab to connect.
+ // We will need to hold the response to dApps
+ holdResponse = true;
+
+ InternalMethods[JsonRpcMethod.LedgerSignTransaction](message, (response) => {
+ // We only have to worry about possible errors here
+ if ('error' in response) {
+ // Cancel the hold response since errors needs to be returned
+ holdResponse = false;
+ message.error = response.error;
+ }
+ });
+ }
}
- const b64Obj = byteArrayToBase64(signedBlob);
-
- signedTxs[index] = {
- txID: txID,
- blob: b64Obj,
- };
} catch (e) {
logging.log(`Signing failed. ${e.message}`);
signErrors[index] = e.message;
@@ -1341,7 +1375,11 @@ export class Task {
message.response = response;
// Clean class saved request
delete Task.requests[responseOriginTabID];
- MessageApi.send(message);
+
+ // Hardware signing will defer the response
+ if (!holdResponse) {
+ MessageApi.send(message);
+ }
}
});
} catch {
@@ -1422,6 +1460,49 @@ export class Task {
[JsonRpcMethod.SaveNetwork]: (request: any, sendResponse: Function) => {
return InternalMethods[JsonRpcMethod.SaveNetwork](request, sendResponse);
},
+ [JsonRpcMethod.CheckNetwork]: (request: any, sendResponse: Function) => {
+ InternalMethods[JsonRpcMethod.CheckNetwork](request, async (networks) => {
+ let urlAlgod = networks.algod.url;
+ if (networks.algod.port.length > 0) urlAlgod += ':' + networks.algod.port;
+ let urlIndexer = networks.indexer.url;
+ if (networks.indexer.port.length > 0) urlIndexer += ':' + networks.indexer.port;
+ const sendPathAlgod = `/v2/status/`;
+ const sendPathIndexer = '/v2/transactions?limit=1';
+ const paramsAlgod: any = {
+ headers: {
+ ...networks.algod.headers,
+ },
+ method: 'GET',
+ };
+ const paramsIndexer: any = {
+ headers: {
+ ...networks.indexer.headers,
+ },
+ method: 'GET',
+ };
+
+ const responseAlgod = {};
+ const responseIndexer = {};
+
+ await Task.fetchAPI(`${urlAlgod}${sendPathAlgod}`, paramsAlgod)
+ .then((response) => {
+ responseAlgod['message'] = response['message'] || response;
+ })
+ .catch((error) => {
+ responseAlgod['error'] = error.message || error;
+ });
+
+ await Task.fetchAPI(`${urlIndexer}${sendPathIndexer}`, paramsIndexer)
+ .then((response) => {
+ responseIndexer['message'] = response['message'] || response;
+ })
+ .catch((error) => {
+ responseIndexer['error'] = error.message || error;
+ });
+ sendResponse({'algod': responseAlgod, 'indexer': responseIndexer});
+ });
+ return true;
+ },
[JsonRpcMethod.DeleteNetwork]: (request: any, sendResponse: Function) => {
return InternalMethods[JsonRpcMethod.DeleteNetwork](request, sendResponse);
},
@@ -1432,7 +1513,50 @@ export class Task {
return InternalMethods[JsonRpcMethod.LedgerLinkAddress](request, sendResponse);
},
[JsonRpcMethod.LedgerGetSessionTxn]: (request: any, sendResponse: Function) => {
- return InternalMethods[JsonRpcMethod.LedgerGetSessionTxn](request, sendResponse);
+ InternalMethods[JsonRpcMethod.LedgerGetSessionTxn](request, (internalResponse) => {
+ if (internalResponse.error) {
+ sendResponse(internalResponse);
+ return;
+ }
+
+ // V1 style transactions will only have 1 transaction and we can use the response.
+ // V2 style transactions will have a transaction in the response.transaction object
+ // and wek only need the one transaction since Ledger doesn't multisign
+ let txWrap = internalResponse;
+ if(txWrap.transaction && txWrap.transaction.transaction){
+ txWrap = txWrap.transaction;
+ }
+
+ // Send response or grab params to calculate an estimated fee if there isn't one
+ if(txWrap.estimatedFee) {
+ sendResponse(txWrap);
+ }
+ else {
+ const conn = Settings.getBackendParams(
+ getLedgerFromGenesisId(txWrap.transaction.genesisID),
+ API.Algod
+ );
+ const sendPath = '/v2/transactions/params';
+ const fetchParams: any = {
+ headers: {
+ ...conn.headers,
+ },
+ method: 'GET',
+ };
+
+ let url = conn.url;
+ if (conn.port.length > 0) url += ':' + conn.port;
+ Task.fetchAPI(`${url}${sendPath}`, fetchParams).then((params) => {
+ if(txWrap.transaction.fee === params['min-fee']) {
+ // This object was built on front end and fee should be 0 to prevent higher fees.
+ txWrap.transaction.fee = 0;
+ }
+ calculateEstimatedFee(txWrap, params);
+ sendResponse(txWrap);
+ });
+ }
+ });
+ return true;
},
[JsonRpcMethod.LedgerSendTxnResponse]: (request: any, sendResponse: Function) => {
InternalMethods[JsonRpcMethod.LedgerSendTxnResponse](request, function (response) {
diff --git a/packages/extension/src/background/utils/networkUrlParser.test.ts b/packages/extension/src/background/utils/networkUrlParser.test.ts
index 90131c9b..da81f434 100644
--- a/packages/extension/src/background/utils/networkUrlParser.test.ts
+++ b/packages/extension/src/background/utils/networkUrlParser.test.ts
@@ -15,8 +15,8 @@ const portURLs = [
['https://[::/128]:4001','https://[::/128]','4001'],
['https://[0:0:0:0:0:0:0:0]:4001','https://[::]','4001'],
['https://subdomain.domain.com:4001','https://subdomain.domain.com','4001'],
- ['https://subdomain.domain.com:4001/foo/bar','https://subdomain.domain.com','4001'],
- ['https://user:pass@subdomain.domain.com:4001/foo/bar?index=1&limit=10','https://user:pass@subdomain.domain.com','4001']
+ ['https://subdomain.domain.com:4001/foo/bar','https://subdomain.domain.com/foo/bar','4001'],
+ ['https://user:pass@subdomain.domain.com:4001/foo/bar?index=1&limit=10','https://user:pass@subdomain.domain.com/foo/bar','4001']
];
test.each(portURLs)(
'Validate URLs with ports (%s)',
@@ -27,6 +27,19 @@ test.each(portURLs)(
}
);
+const pathURLs = [
+ ['https://betanet-algorand.api.purestake.io/ps2','https://betanet-algorand.api.purestake.io/ps2',''],
+ ['https://betanet-algorand.api.purestake.io/idx2','https://betanet-algorand.api.purestake.io/idx2',''],
+];
+test.each(pathURLs)(
+ 'Validate URLs with paths (%s)',
+ (inputUrl, expectedServer, expectedPort) => {
+ const result = parseUrlServerAndPort(inputUrl);
+ expect(result["server"]).toBe(expectedServer);
+ expect(result["port"]).toBe(expectedPort);
+ }
+);
+
const noPortURLs = [
['https://localhost','https://localhost',''],
['https://127.0.0.1','https://127.0.0.1',''],
@@ -36,8 +49,8 @@ const noPortURLs = [
['https://[::/128]','https://[::/128]',''],
['https://[0:0:0:0:0:0:0:0]','https://[::]',''],
['https://subdomain.domain.com','https://subdomain.domain.com',''],
- ['https://subdomain.domain.com/foo/bar','https://subdomain.domain.com',''],
- ['https://user:pass@subdomain.domain.com/foo/bar?index=1&limit=10','https://user:pass@subdomain.domain.com','']
+ ['https://subdomain.domain.com/foo/bar','https://subdomain.domain.com/foo/bar',''],
+ ['https://user:pass@subdomain.domain.com/foo/bar?index=1&limit=10','https://user:pass@subdomain.domain.com/foo/bar','']
];
test.each(noPortURLs)(
'Validate URLs without ports (%s)',
@@ -72,8 +85,8 @@ const noProtocolURLs = [
['[::/128]:4001','[::/128]','4001'],
['[0:0:0:0:0:0:0:0]:4001','[0:0:0:0:0:0:0:0]','4001'],
['subdomain.domain.com:4001','subdomain.domain.com','4001'],
- ['subdomain.domain.com:4001/foo/bar','subdomain.domain.com','4001'],
- ['user:pass@subdomain.domain.com:4001/foo/bar?index=1&limit=10','user:pass@subdomain.domain.com','4001']
+ ['subdomain.domain.com:4001/foo/bar','subdomain.domain.com/foo/bar','4001'],
+ ['user:pass@subdomain.domain.com:4001/foo/bar?index=1&limit=10','user:pass@subdomain.domain.com/foo/bar','4001']
];
test.each(noProtocolURLs)(
'Validate URLs without protocols (%s)',
diff --git a/packages/extension/src/background/utils/networkUrlParser.ts b/packages/extension/src/background/utils/networkUrlParser.ts
index ebffbd45..f540e543 100644
--- a/packages/extension/src/background/utils/networkUrlParser.ts
+++ b/packages/extension/src/background/utils/networkUrlParser.ts
@@ -41,8 +41,12 @@ export function parseUrlServerAndPort(urlInput:string):any {
}
if (urlObj.protocol) {
returnUrlObj.server = urlObj.protocol + '//' + returnUrlObj.server;
- }
+ }
+ if (urlObj.pathname && urlObj.pathname.replace(/^\//, '').length > 0) {
+ returnUrlObj.server = returnUrlObj.server + '/' + urlObj.pathname.replace(/^\//, '');
+ }
}
+ logging.log(`Parsed URL:${JSON.stringify(returnUrlObj)}`,2);
}
catch {
urlSplit = true;
@@ -52,6 +56,7 @@ export function parseUrlServerAndPort(urlInput:string):any {
if (urlSplit) {
try {
const urlArr = urlInput.split(':');
+ let modifiedPath = '';
// A url with a 2 or 3 length may have either a user:pass or port
// If there is an 8 length split it is an IPv6 address without a port or protocol
if (urlArr.length > 1) {
@@ -61,9 +66,22 @@ export function parseUrlServerAndPort(urlInput:string):any {
const potentialPort = urlArr.pop();
// Quick cast to ensure port is numeric but a string
returnUrlObj.port = (parseInt(potentialPort) || undefined).toString();
+
+ // If we fail to pull off the port or the last component had more info
+ // add the remainder back to the url without the querystring
+ if(!returnUrlObj.port || returnUrlObj.port.length < potentialPort.length) {
+ modifiedPath = potentialPort.replace(returnUrlObj.port,'').split('?')[0];
+ }
}
}
returnUrlObj.server = urlArr.join(':');
+
+ // If we have a path after the port removal add it
+ if(modifiedPath) {
+ returnUrlObj.server += modifiedPath;
+ }
+
+ logging.log(`Split URL:${JSON.stringify(returnUrlObj)}`,2);
}
catch {
returnUrlObj.server = urlInput;
diff --git a/packages/ui/src/components/LedgerDevice/LedgerHardwareSign.ts b/packages/ui/src/components/LedgerDevice/LedgerHardwareSign.ts
index ac267f3e..44a4ca28 100644
--- a/packages/ui/src/components/LedgerDevice/LedgerHardwareSign.ts
+++ b/packages/ui/src/components/LedgerDevice/LedgerHardwareSign.ts
@@ -32,8 +32,8 @@ const LedgerHardwareSign: FunctionalComponent = () => {
if (response.error) {
setError(response.error);
} else {
- getBaseSupportedLedgers().forEach((l) => {
- if (response.transaction.genesisID === l['genesisId']) {
+ getBaseSupportedLedgers().forEach((l) => {
+ if (response.genesisID === l['genesisId']) {
setLedger(l['name']);
// Update the ledger dropdown to the signing one
@@ -44,9 +44,8 @@ const LedgerHardwareSign: FunctionalComponent = () => {
});
// Update account value to the signer
- setAccount(response.transaction.from);
+ setAccount(response.from);
- // Set the visible transaction
setTxn(response);
}
});
@@ -55,7 +54,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
logging.log(`${JSON.stringify(ex)}`, 2);
}
}
- });
+ }, []);
const ledgerSignTransaction = () => {
setLoading(true);
@@ -124,7 +123,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
<${TxPay}
tx=${txn.transaction}
vo=${txn.validityObject}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
account=${account}
ledger=${ledger}
/>
@@ -134,7 +133,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
<${TxKeyreg}
tx=${txn.transaction}
vo=${txn.validityObject}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
account=${account}
ledger=${ledger}
/>
@@ -145,7 +144,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
tx=${txn.transaction}
vo=${txn.validityObject}
dt=${txn.txDerivedTypeText}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
account=${account}
ledger=${ledger}
/>
@@ -156,7 +155,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
tx=${txn.transaction}
vo=${txn.validityObject}
dt=${txn.txDerivedTypeText}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
da=${txn.displayAmount}
un=${txn.unitName}
account=${account}
@@ -168,7 +167,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
<${TxAfrz}
tx=${txn.transaction}
vo=${txn.validityObject}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
account=${account}
ledger=${ledger}
/>
@@ -178,7 +177,7 @@ const LedgerHardwareSign: FunctionalComponent = () => {
<${TxAppl}
tx=${txn.transaction}
vo=${txn.validityObject}
- fee=${txn.estimatedFee}
+ estFee=${txn.estimatedFee}
account=${account}
ledger=${ledger}
/>
diff --git a/packages/ui/src/components/LedgerDevice/structure/ledgerActions.ts b/packages/ui/src/components/LedgerDevice/structure/ledgerActions.ts
index 6567612a..fc59332f 100644
--- a/packages/ui/src/components/LedgerDevice/structure/ledgerActions.ts
+++ b/packages/ui/src/components/LedgerDevice/structure/ledgerActions.ts
@@ -99,6 +99,29 @@ function cleanseBuildEncodeUnsignedTransaction(transaction: any): any {
}
}
+ // Remap of BigInt values from strings creates issues in this cast
+ // So forcing the two affected fields (amount,assetTotal) back to numeric
+ if ('amount' in txn) {
+ const parsed = parseInt(txn['amount']);
+ // Soft check on the result mating the txn amount because it is an expect int to string compare
+ if (isNaN(parsed) || parsed != txn['amount']) {
+ errors.push('Ledger transaction amount must be an integer.');
+ }
+ else {
+ txn['amount'] = parsed;
+ }
+ }
+ if ('assetTotal' in txn) {
+ const parsed = parseInt(txn['assetTotal']);
+ // Soft check on the result mating the txn assetTotal because it is an expect int to string compare
+ if (isNaN(parsed) || parsed != txn['assetTotal']) {
+ errors.push('Ledger transaction assetTotal must be an integer.');
+ }
+ else {
+ txn['assetTotal'] = parsed;
+ }
+ }
+
const builtTxn = new algosdk.Transaction(txn);
if ('group' in txn && txn['group']) {
diff --git a/packages/ui/src/components/LedgerNetworkModify.ts b/packages/ui/src/components/LedgerNetworkModify.ts
index 8e332256..ecb30612 100644
--- a/packages/ui/src/components/LedgerNetworkModify.ts
+++ b/packages/ui/src/components/LedgerNetworkModify.ts
@@ -24,6 +24,7 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
const [networkAlgodUrl, setNetworkAlgodUrl] = useState(props.algodUrl || '');
const [networkIndexerUrl, setNetworkIndexerUrl] = useState(props.indexerUrl || '');
const [networkHeaders, setNetworkHeaders] = useState(props.headers || '');
+ const [checkStatus, setCheckStatus] = useState('gray');
const deleteNetwork = (pwd: string) => {
setLoading(true);
@@ -56,6 +57,32 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
});
};
+ const checkNetwork = () => {
+ setLoading(true);
+ setAuthError('');
+ setCheckStatus('');
+ setError('');
+ const params = {
+ name: networkName,
+ genesisId: networkId,
+ symbol: networkSymbol,
+ algodUrl: networkAlgodUrl,
+ indexerUrl: networkIndexerUrl,
+ headers: networkHeaders
+ };
+
+ sendMessage(JsonRpcMethod.CheckNetwork, params, (response) => {
+ setLoading(false);
+ if (response.algod.error || response.indexer.error) {
+ // Error display
+ setCheckStatus('red');
+ setError(JSON.stringify({'algod': response.algod.error || 'Success', 'indexer': response.indexer.error || 'Success'}));
+ } else {
+ setCheckStatus('green');
+ }
+ });
+ }
+
const saveNetwork = (pwd) => {
setLoading(true);
setAuthError('');
@@ -71,6 +98,7 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
};
sendMessage(JsonRpcMethod.SaveNetwork, params, function (response) {
+ setLoading(false);
if (response.error) {
// Error display
console.log(response.error);
@@ -95,6 +123,16 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
() => html`
+
+
+
Date: Wed, 13 Oct 2021 15:21:13 -0300
Subject: [PATCH 4/7] UI Improvements
---
.../ui/src/components/LedgerNetworkModify.ts | 39 ++++++++++++-------
packages/ui/src/styles.scss | 4 --
2 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/packages/ui/src/components/LedgerNetworkModify.ts b/packages/ui/src/components/LedgerNetworkModify.ts
index ecb30612..7bd3d415 100644
--- a/packages/ui/src/components/LedgerNetworkModify.ts
+++ b/packages/ui/src/components/LedgerNetworkModify.ts
@@ -68,20 +68,25 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
symbol: networkSymbol,
algodUrl: networkAlgodUrl,
indexerUrl: networkIndexerUrl,
- headers: networkHeaders
+ headers: networkHeaders,
};
-
+
sendMessage(JsonRpcMethod.CheckNetwork, params, (response) => {
setLoading(false);
if (response.algod.error || response.indexer.error) {
// Error display
setCheckStatus('red');
- setError(JSON.stringify({'algod': response.algod.error || 'Success', 'indexer': response.indexer.error || 'Success'}));
+ setError(
+ JSON.stringify({
+ algod: response.algod.error || 'Success',
+ indexer: response.indexer.error || 'Success',
+ })
+ );
} else {
setCheckStatus('green');
}
});
- }
+ };
const saveNetwork = (pwd) => {
setLoading(true);
@@ -110,7 +115,7 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
});
};
- const evaluateNextStep = (pwd) => {
+ const evaluateNextStep = (pwd: string) => {
if (isDeleting) {
deleteNetwork(pwd);
} else {
@@ -127,10 +132,15 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
@@ -172,7 +182,6 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
href="https://github.com/PureStake/algosigner/blob/develop/docs/add-network.md#network-headers"
>
@@ -194,10 +203,11 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => {
${isEditable &&
html`
-
+