From 9d10584d788e83fa76938b03231b2d5d8987dcb8 Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Mon, 20 Dec 2021 14:28:19 -0300 Subject: [PATCH 01/13] Fix Network Config saving on a different profile --- packages/extension/src/background/config.ts | 55 +++++++++++++------ .../background/messaging/internalMethods.ts | 50 +++++++++-------- .../ui/src/components/LedgerNetworkModify.ts | 3 + 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/packages/extension/src/background/config.ts b/packages/extension/src/background/config.ts index 1f3809fc..cfb29290 100644 --- a/packages/extension/src/background/config.ts +++ b/packages/extension/src/background/config.ts @@ -92,9 +92,9 @@ export class Settings { } // Setup port splits for algod and indexer - used in sandbox installs - const parsedAlgodUrlObj = parseUrlServerAndPort(ledger.algodUrl) + const parsedAlgodUrlObj = parseUrlServerAndPort(ledger.algodUrl); const parsedIndexerUrlObj = parseUrlServerAndPort(ledger.indexerUrl); - + // Add algod links const injectedAlgod = { url: parsedAlgodUrlObj.server || `${defaultUrl}/algod`, @@ -111,15 +111,14 @@ export class Settings { headers: headersIndexer || headers, }; - if (isCheckOnly) { + if (isCheckOnly) { return { - 'algod': injectedAlgod, - 'indexer': injectedIndexer - } - } - else { + 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][API.Indexer] = injectedIndexer; this.backend_settings.InjectedNetworks[ledger.name].headers = headers; } } @@ -132,20 +131,40 @@ export class Settings { }; this.setInjectedHeaders(ledger); - logging.log(`Added Network:\n${JSON.stringify(this.backend_settings.InjectedNetworks[ledger.name],null,1)}`,2); + logging.log( + `Added Network:\n${JSON.stringify( + this.backend_settings.InjectedNetworks[ledger.name], + null, + 1 + )}`, + 2 + ); } - public static updateInjectedNetwork(updatedLedger: LedgerTemplate) { - this.backend_settings.InjectedNetworks[updatedLedger.name].genesisId = updatedLedger.genesisId; - this.backend_settings.InjectedNetworks[updatedLedger.name].symbol = updatedLedger.symbol; - this.backend_settings.InjectedNetworks[updatedLedger.name].genesisHash = + public static updateInjectedNetwork(updatedLedger: LedgerTemplate, previousName: string = '') { + const targetName = updatedLedger.uniqueName; + + if (previousName) { + this.deleteInjectedNetwork(previousName); + this.backend_settings.InjectedNetworks[targetName] = {}; + } + this.backend_settings.InjectedNetworks[targetName].genesisId = updatedLedger.genesisId; + this.backend_settings.InjectedNetworks[targetName].symbol = updatedLedger.symbol; + this.backend_settings.InjectedNetworks[targetName].genesisHash = updatedLedger.genesisHash; - this.backend_settings.InjectedNetworks[updatedLedger.name].algodUrl = updatedLedger.algodUrl; - this.backend_settings.InjectedNetworks[updatedLedger.name].indexerUrl = + this.backend_settings.InjectedNetworks[targetName].algodUrl = updatedLedger.algodUrl; + this.backend_settings.InjectedNetworks[targetName].indexerUrl = updatedLedger.indexerUrl; this.setInjectedHeaders(updatedLedger); - logging.log(`Updated Network:\n${JSON.stringify(this.backend_settings.InjectedNetworks[updatedLedger.name],null,1)}`,2); + logging.log( + `Updated Network:\n${JSON.stringify( + this.backend_settings.InjectedNetworks[targetName], + null, + 1 + )}`, + 2 + ); } public static getBackendParams(ledger: string, api: API) { @@ -169,7 +188,7 @@ export class Settings { } public static checkNetwork(ledger: LedgerTemplate) { - const networks = this.setInjectedHeaders(ledger, true); + 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 f1198cda..27c458fe 100644 --- a/packages/extension/src/background/messaging/internalMethods.ts +++ b/packages/extension/src/background/messaging/internalMethods.ts @@ -334,7 +334,7 @@ export class InternalMethods { const sessTxn = session.txnWrap.body.params.transaction; // Set the fee to the estimate we showed on the screen for validation if there is one. - if(session.txnWrap.body.params.estimatedFee) { + if (session.txnWrap.body.params.estimatedFee) { sessTxn['fee'] = session.txnWrap.body.params.estimatedFee; } const sessTxnEntries = Object.entries(sessTxn).sort(); @@ -371,18 +371,18 @@ export class InternalMethods { // If v2 then it needs to return an array if (session.txnWrap?.body?.params?.transactionsOrGroups) { - message.response = [{ - blob: request.body.params.txn - }]; - } - else { + message.response = [ + { + blob: request.body.params.txn, + }, + ]; + } else { message.response = { - blob: request.body.params.txn + blob: request.body.params.txn, }; } sendResponse({ message: message }); - } // If this is a ui transaction then we need to also submit else if (session.txnWrap.source === 'ui') { @@ -891,9 +891,10 @@ export class InternalMethods { public static [JsonRpcMethod.SaveNetwork](request: any, sendResponse: Function) { try { + const params = request.body.params; // If we have a passphrase then we are modifying. // There may be accounts attatched, if we match on a unique name, we should update. - if (request.body.params['passphrase'] !== undefined) { + if (params['passphrase'] !== undefined) { this._encryptionWrap = new encryptionWrap(request.body.params['passphrase']); this._encryptionWrap.unlock((unlockedValue: any) => { if ('error' in unlockedValue) { @@ -902,14 +903,17 @@ export class InternalMethods { // We have evaluated the passphrase and it was valid. }); } + + const previousName = params['previousName'].toLowerCase(); + const targetName = previousName ? previousName : params['name'].toLowerCase(); const addedLedger = new LedgerTemplate({ - name: request.body.params['name'], - genesisId: request.body.params['genesisId'], - genesisHash: request.body.params['genesisHash'], - symbol: request.body.params['symbol'], - algodUrl: request.body.params['algodUrl'], - indexerUrl: request.body.params['indexerUrl'], - headers: request.body.params['headers'], + name: params['name'], + genesisId: params['genesisId'], + genesisHash: params['genesisHash'], + symbol: params['symbol'], + algodUrl: params['algodUrl'], + indexerUrl: params['indexerUrl'], + headers: params['headers'], }); // Specifically get the base ledgers to check and prevent them from being overriden. @@ -919,19 +923,17 @@ export class InternalMethods { const comboLedgers = [...availiableLedgers]; // Add the new ledger if it isn't there. - if (!comboLedgers.some((cledg) => cledg.uniqueName === addedLedger.uniqueName)) { + if (!comboLedgers.some((cledg) => cledg.uniqueName === targetName)) { comboLedgers.push(addedLedger); // Also add the ledger to the injected ledgers in settings Settings.addInjectedNetwork(addedLedger); - } - // If the new ledger name does exist, we sould update the values as long as it is not a default ledger. - else { - const matchingLedger = comboLedgers.find( - (cledg) => cledg.uniqueName === addedLedger.uniqueName - ); + } else { + // If the new ledger name does exist, we sould update the values as long as it is not a default ledger. + const matchingLedger = comboLedgers.find((cledg) => cledg.uniqueName === targetName); if (!defaultLedgers.some((dledg) => dledg.uniqueName === matchingLedger.uniqueName)) { - Settings.updateInjectedNetwork(addedLedger); + Settings.updateInjectedNetwork(addedLedger, previousName); + matchingLedger.name = addedLedger.name; matchingLedger.genesisId = addedLedger.genesisId; matchingLedger.symbol = addedLedger.symbol; matchingLedger.genesisHash = addedLedger.genesisHash; diff --git a/packages/ui/src/components/LedgerNetworkModify.ts b/packages/ui/src/components/LedgerNetworkModify.ts index 7bd3d415..88327c9b 100644 --- a/packages/ui/src/components/LedgerNetworkModify.ts +++ b/packages/ui/src/components/LedgerNetworkModify.ts @@ -26,6 +26,8 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => { const [networkHeaders, setNetworkHeaders] = useState(props.headers || ''); const [checkStatus, setCheckStatus] = useState('gray'); + const previousName = props.name ? props.name : ''; + const deleteNetwork = (pwd: string) => { setLoading(true); setAuthError(''); @@ -94,6 +96,7 @@ const LedgerNetworkModify: FunctionalComponent = (props: any) => { setError(''); const params = { name: networkName, + previousName: previousName, genesisId: networkId, symbol: networkSymbol, algodUrl: networkAlgodUrl, From a43ce85d1a77a96b205d8ec8b49cd858754874da Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Tue, 21 Dec 2021 10:51:29 -0300 Subject: [PATCH 02/13] Affix 'Add account' button to bottom of window --- packages/ui/src/pages/Wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/pages/Wallet.ts b/packages/ui/src/pages/Wallet.ts index 2b42efb0..ac859401 100644 --- a/packages/ui/src/pages/Wallet.ts +++ b/packages/ui/src/pages/Wallet.ts @@ -21,7 +21,7 @@ const Wallet: FunctionalComponent = () => { const { ledger } = store; return html`
-
+
${ store[ledger] && store[ledger].length !== 0 && From ad18b4469d9b0fb59caf70fd07a16508e65dc408 Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Tue, 21 Dec 2021 11:03:31 -0300 Subject: [PATCH 03/13] Break long account names in two lines --- packages/ui/src/pages/Account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/pages/Account.ts b/packages/ui/src/pages/Account.ts index 81669391..3c713946 100644 --- a/packages/ui/src/pages/Account.ts +++ b/packages/ui/src/pages/Account.ts @@ -57,7 +57,7 @@ const Account: FunctionalComponent = (props: any) => { -

${account.name}

+

${account.name}

-
- `; - else - return html` - <${Authenticate} error=${authError} loading=${loading} nextStep=${deleteAccount} /> - `; -}; - -export default AccountDetails; diff --git a/packages/ui/src/components/Account/__snapshots__/AccountDetails.test.ts.snap b/packages/ui/src/components/Account/__snapshots__/AccountDetails.test.ts.snap deleted file mode 100644 index 51bda112..00000000 --- a/packages/ui/src/components/Account/__snapshots__/AccountDetails.test.ts.snap +++ /dev/null @@ -1,44 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AccountDetails should match snapshot 1`] = ` -
- - Address - - ( - - ) -

- PBZHOKKNBUCCDJB7KB2KLHUMWCGAMBXZKGBFGGBHYNNXFIBOYI7ONYBWK4 -

-
- -
- -
-`; diff --git a/packages/ui/src/index.js b/packages/ui/src/index.js index 98b842c2..a71268f9 100644 --- a/packages/ui/src/index.js +++ b/packages/ui/src/index.js @@ -17,6 +17,7 @@ import CreateAccount from 'pages/CreateAccount'; import ImportAccount from 'pages/ImportAccount'; import Wallet from 'pages/Wallet'; import Account from 'pages/Account'; +import AccountDetails from 'pages/AccountDetails'; import SendAlgos from 'pages/SendAlgos'; import AddAsset from 'pages/AddAsset'; import SignTransaction from 'pages/SignTransaction'; @@ -62,6 +63,7 @@ const App = () => { <${CreateAccount} path="/:ledger/create-account" /> <${ImportAccount} path="/:ledger/import-account" /> <${Account} path="/:ledger/:address" /> + <${AccountDetails} path="/:ledger/:address/details" /> <${AddAsset} path="/:ledger/:address/add-asset" /> <${SendAlgos} path="/:ledger/:address/send" /> <${LedgerHardwareConnector} path="/:ledger/ledger-hardware-connector" /> diff --git a/packages/ui/src/pages/Account.ts b/packages/ui/src/pages/Account.ts index e4daa307..e85f11b0 100644 --- a/packages/ui/src/pages/Account.ts +++ b/packages/ui/src/pages/Account.ts @@ -10,7 +10,6 @@ import { numFormat } from 'services/common'; import { StoreContext } from 'services/StoreContext'; import TransactionsList from 'components/Account/TransactionsList'; import AssetsList from 'components/Account/AssetsList'; -import AccountDetails from 'components/Account/AccountDetails'; import algo from 'assets/algo.png'; const Account: FunctionalComponent = (props: any) => { @@ -18,7 +17,6 @@ const Account: FunctionalComponent = (props: any) => { const { url, ledger, address } = props; const [account, setAccount] = useState({}); const [details, setDetails] = useState({}); - const [showDetails, setShowDetails] = useState(false); const [error, setError] = useState(null); const rewardsTooltip = @@ -66,12 +64,12 @@ const Account: FunctionalComponent = (props: any) => {

${account.name}

-

+
${details === null && error && html`${error}`} @@ -112,23 +110,6 @@ const Account: FunctionalComponent = (props: any) => {
<${TransactionsList} address=${address} ledger=${ledger}/> - - ${ - showDetails && - html` - - ` - } `; }; diff --git a/packages/ui/src/pages/AccountDetails.ts b/packages/ui/src/pages/AccountDetails.ts new file mode 100644 index 00000000..aaa30c7b --- /dev/null +++ b/packages/ui/src/pages/AccountDetails.ts @@ -0,0 +1,190 @@ +import { FunctionalComponent } from 'preact'; +import { html } from 'htm/preact'; +import { useContext, useState, useEffect } from 'preact/hooks'; +import { route } from 'preact-router'; +import qrcode from 'qrcode-generator'; +import { JsonRpcMethod } from '@algosigner/common/messaging/types'; + +import { sendMessage } from 'services/Messaging'; +import { StoreContext } from 'services/StoreContext'; + +import Authenticate from 'components/Authenticate'; +import ToClipboard from 'components/ToClipboard'; + +const AccountDetails: FunctionalComponent = (props: any) => { + const { ledger, address } = props; + const store: any = useContext(StoreContext); + const [details, setDetails] = useState({}); + const [modal, setModal] = useState(''); + const [loading, setLoading] = useState(false); + const [authError, setAuthError] = useState(''); + + const deleteAccount = (pwd: string) => { + const params = { + ledger: ledger, + address: address, + passphrase: pwd, + }; + setLoading(true); + setAuthError(''); + sendMessage(JsonRpcMethod.DeleteAccount, params, function (response) { + if ('error' in response) { + setLoading(false); + switch (response.error) { + case 'Login Failed': + setAuthError('Wrong passphrase'); + break; + default: + setModal(''); + alert(`There was an unkown error: ${response.error}`); + break; + } + } else { + store.updateWallet(response, () => { + route('/wallet'); + }); + } + }); + }; + + useEffect(() => { + for (let i = store[ledger].length - 1; i >= 0; i--) { + if (store[ledger][i].address === address) { + setDetails(store[ledger][i].details); + break; + } + } + }, []); + + const typeNumber: TypeNumber = 4; + const errorCorrectionLevel: ErrorCorrectionLevel = 'L'; + const qr = qrcode(typeNumber, errorCorrectionLevel); + qr.addData(address); + qr.make(); + const qrImg = qr.createDataURL(10, 1); + + return html` +
+
+ route(`/${ledger}/${address}`)} + > + + + + +

+ Address: +
+ ${address} + (<${ToClipboard} data=${address} />) +

+
+
+
+ ${details && + details['apps-local-state'] && + details['apps-local-state'].length > 0 && + html` + Opted-in Apps +
+ ${details['apps-local-state'].map( + (ca) => html` + + ${ca.id} + + ` + )} +
+ `} + ${details && + details['created-assets'] && + details['created-assets'].length > 0 && + html` + Created Assets +
+ ${details['created-assets'].map( + (cas) => html` + + ${cas.params.name && + cas.params.name.length > 0 && + html` + ${cas.params.name} + ${cas.index} + `} + ${(!cas.params.name || cas.params.name.length === 0) && html`${cas.index}`} + + ` + )} +
+ `} + ${details && + details['created-apps'] && + details['created-apps'].length > 0 && + html` + Created Apps +
+ ${details['created-apps'].map( + (cap) => html` + + ${cap.params.name && + cap.params.name.length > 0 && + html` + ${cap.params.name} + ${cap.index} + `} + ${(!cap.params.name || cap.params.name.length === 0) && html`${cap.index}`} + + ` + )} +
+ `} +
+
+ + +
+ ${modal && + html` + + `} + `; +}; + +export default AccountDetails; From 6d3ba1b241b5d64b0dd03d9f8a40047bc859e3b9 Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Thu, 20 Jan 2022 15:18:03 -0300 Subject: [PATCH 06/13] Support opting-out from ASAs from the UI --- packages/common/src/messaging/types.ts | 1 + .../background/messaging/internalMethods.ts | 124 ++++++++++++++++++ .../src/background/messaging/task.ts | 3 + .../ui/src/components/Account/AssetDetails.ts | 5 +- .../ui/src/components/Account/AssetsList.ts | 83 ++++++++---- packages/ui/src/pages/Account.ts | 2 +- 6 files changed, 190 insertions(+), 28 deletions(-) diff --git a/packages/common/src/messaging/types.ts b/packages/common/src/messaging/types.ts index 0436d04c..3f1cdff1 100644 --- a/packages/common/src/messaging/types.ts +++ b/packages/common/src/messaging/types.ts @@ -36,6 +36,7 @@ export enum JsonRpcMethod { AssetDetails = 'asset-details', AssetsAPIList = 'assets-api-list', AssetsVerifiedList = 'assets-verified-list', + AssetOptOut = 'asset-opt-out', SignSendTransaction = 'sign-send-transaction', ChangeLedger = 'change-ledger', SaveNetwork = 'save-network', diff --git a/packages/extension/src/background/messaging/internalMethods.ts b/packages/extension/src/background/messaging/internalMethods.ts index 27c458fe..94674e8e 100644 --- a/packages/extension/src/background/messaging/internalMethods.ts +++ b/packages/extension/src/background/messaging/internalMethods.ts @@ -678,6 +678,130 @@ export class InternalMethods { return true; } + public static [JsonRpcMethod.AssetOptOut](request: any, sendResponse: Function) { + const { ledger, address, passphrase, id } = request.body.params; + this._encryptionWrap = new encryptionWrap(passphrase); + const algod = this.getAlgod(ledger); + + this._encryptionWrap.unlock(async (unlockedValue: any) => { + if ('error' in unlockedValue) { + sendResponse(unlockedValue); + return false; + } + let account; + + // Find address to send algos from + for (var i = unlockedValue[ledger].length - 1; i >= 0; i--) { + if (unlockedValue[ledger][i].address === address) { + account = unlockedValue[ledger][i]; + break; + } + } + + const params = await algod.getTransactionParams().do(); + const txn = { + type: 'axfer', + from: address, + to: address, + closeRemainderTo: address, + assetIndex: id, + fee: params.fee, + firstRound: params.firstRound, + lastRound: params.lastRound, + genesisID: params.genesisID, + genesisHash: params.genesisHash, + }; + + let transactionWrap: BaseValidatedTxnWrap = undefined; + try { + transactionWrap = getValidatedTxnWrap(txn, txn['type']); + } catch (e) { + logging.log(`Validation failed. ${e}`); + sendResponse({ error: `Validation failed. ${e}` }); + return; + } + if (!transactionWrap) { + // We don't have a transaction wrap. We have an unknow error or extra fields, reject the transaction. + logging.log( + 'A transaction has failed because of an inability to build the specified transaction type.' + ); + sendResponse({ + error: + 'A transaction has failed because of an inability to build the specified transaction type.', + }); + return; + } else if ( + transactionWrap.validityObject && + Object.values(transactionWrap.validityObject).some( + (value) => value['status'] === ValidationStatus.Invalid + ) + ) { + // We have a transaction that contains fields which are deemed invalid. We should reject the transaction. + const e = + 'One or more fields are not valid. Please check and try again.\n' + + Object.values(transactionWrap.validityObject) + .filter((value) => value['status'] === ValidationStatus.Invalid) + .map((vo) => vo['info']); + sendResponse({ error: e }); + return; + } else { + // We have a transaction which does not contain invalid fields, + // but may still contain fields that are dangerous + // or ones we've flagged as needing to be reviewed. + // Perform a change based on if this is a ledger device account + if (account.isHardware) { + // TODO: Temporary workaround by adding min-fee for estimate calculations since it's not in the sdk get params. + params['min-fee'] = 1000; + calculateEstimatedFee(transactionWrap, params); + + // Pass the transaction wrap we can pass to the + // central sign ledger function for consistency + this[JsonRpcMethod.LedgerSignTransaction]( + { source: 'ui', body: { params: transactionWrap } }, + (response) => { + // We only have to worry about possible errors here so we can ignore the created tab + if ('error' in response) { + sendResponse(response); + } else { + // Respond with a 0 tx id so that the page knows not to try and show it. + sendResponse({ txId: 0 }); + } + } + ); + + // Return to close connection + return true; + } else { + // We can use a modified popup to allow the normal flow, but require extra scrutiny. + const recoveredAccount = algosdk.mnemonicToSecretKey(account.mnemonic); + let signedTxn; + try { + const builtTx = buildTransaction(txn); + signedTxn = { + txID: builtTx.txID().toString(), + blob: builtTx.signTxn(recoveredAccount.sk), + }; + } catch (e) { + sendResponse({ error: e.message }); + return false; + } + + algod + .sendRawTransaction(signedTxn.blob) + .do() + .then((resp: any) => { + sendResponse({ txId: resp.txId }); + }) + .catch((e: any) => { + sendResponse({ error: e.message }); + }); + } + } + }); + + return true; + } + public static [JsonRpcMethod.SignSendTransaction](request: any, sendResponse: Function) { const { ledger, address, passphrase, txnParams } = request.body.params; this._encryptionWrap = new encryptionWrap(passphrase); diff --git a/packages/extension/src/background/messaging/task.ts b/packages/extension/src/background/messaging/task.ts index 24166291..f507d45c 100644 --- a/packages/extension/src/background/messaging/task.ts +++ b/packages/extension/src/background/messaging/task.ts @@ -1463,6 +1463,9 @@ export class Task { [JsonRpcMethod.AssetsVerifiedList]: (request: any, sendResponse: Function) => { return InternalMethods[JsonRpcMethod.AssetsVerifiedList](request, sendResponse); }, + [JsonRpcMethod.AssetOptOut]: (request: any, sendResponse: Function) => { + return InternalMethods[JsonRpcMethod.AssetOptOut](request, sendResponse); + }, [JsonRpcMethod.SignSendTransaction]: (request: any, sendResponse: Function) => { return InternalMethods[JsonRpcMethod.SignSendTransaction](request, sendResponse); }, diff --git a/packages/ui/src/components/Account/AssetDetails.ts b/packages/ui/src/components/Account/AssetDetails.ts index e85a8610..dcf61ee2 100644 --- a/packages/ui/src/components/Account/AssetDetails.ts +++ b/packages/ui/src/components/Account/AssetDetails.ts @@ -8,7 +8,7 @@ import { sendMessage } from 'services/Messaging'; import ToClipboard from 'components/ToClipboard'; const AssetDetails: FunctionalComponent = (props: any) => { - const { asset, ledger } = props; + const { asset, ledger, optOutFn } = props; const [, setResults] = useState(0); const fetchApi = async () => { @@ -74,7 +74,7 @@ const AssetDetails: FunctionalComponent = (props: any) => { ${asset.creator.slice(0, 8)}.....${asset.creator.slice(-8)}

-
+
{ > See details in GoalSeeker + ${!(asset.amount > 0) && html`Opt-out of this asset`}
`}
diff --git a/packages/ui/src/components/Account/AssetsList.ts b/packages/ui/src/components/Account/AssetsList.ts index 9d12095a..e33e8949 100644 --- a/packages/ui/src/components/Account/AssetsList.ts +++ b/packages/ui/src/components/Account/AssetsList.ts @@ -2,7 +2,11 @@ import { FunctionalComponent } from 'preact'; import { html } from 'htm/preact'; import { useState } from 'preact/hooks'; +import { JsonRpcMethod } from '@algosigner/common/messaging/types'; +import { sendMessage } from 'services/Messaging'; + import AssetDetails from 'components/Account/AssetDetails'; +import Authenticate from 'components/Authenticate'; const AssetPreview: FunctionalComponent = (props: any) => { const { asset, setShowAsset } = props; @@ -27,8 +31,7 @@ const AssetPreview: FunctionalComponent = (props: any) => { ${asset.name} ${asset['asset-id']} `} - ${(!asset.name || asset.name.length === 0) && - html` ${asset['asset-id']} `} + ${(!asset.name || asset.name.length === 0) && html` ${asset['asset-id']} `} ${getAmount()} ${asset['unit-name'] && @@ -40,30 +43,55 @@ const AssetPreview: FunctionalComponent = (props: any) => { }; const AssetsList: FunctionalComponent = (props: any) => { - const { ledger, assets } = props; + const { ledger, assets, address } = props; const [showAsset, setShowAsset] = useState(null); const [fullList, setFullList] = useState(false); + const [askAuth, setAskAuth] = useState(false); + const [authError, setAuthError] = useState(''); + const [loading, setLoading] = useState(false); + + const sendOptOut = async (pwd: string) => { + setLoading(true); + setAuthError(''); + const params = { + ledger: ledger, + passphrase: pwd, + address: address, + id: showAsset['asset-id'], + }; + sendMessage(JsonRpcMethod.AssetOptOut, params, function (response) { + setLoading(false); + if ('error' in response) { + switch (response.error) { + case 'Login Failed': + setAuthError('Wrong passphrase'); + break; + default: + setAuthError(response.error); + break; + } + } else { + setAskAuth(false); + setShowAsset(null); + setFullList(false); + } + }); + }; // Only show the first 10 assets, and a link to the assets view // if needed. return html` ${assets .slice(0, 10) - .map( - (asset: any) => html` - <${AssetPreview} asset=${asset} setShowAsset=${setShowAsset} /> - ` - )} + .map((asset: any) => html`<${AssetPreview} asset=${asset} setShowAsset=${setShowAsset} />`)} ${assets.length > 10 && html` `} @@ -72,31 +100,36 @@ const AssetsList: FunctionalComponent = (props: any) => { - ${getWrapUI(transactionWraps[activeTx], accountNames[activeTx])} -
+
${approvedAmount} out of ${transactionWraps.length} transactions approved. Date: Fri, 21 Jan 2022 12:33:45 -0300 Subject: [PATCH 09/13] Update tests --- .../background/transaction/actions.test.ts | 24 +++++++++++++++++++ .../__snapshots__/AssetDetails.test.ts.snap | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/background/transaction/actions.test.ts b/packages/extension/src/background/transaction/actions.test.ts index ecfbef6d..c4a50736 100644 --- a/packages/extension/src/background/transaction/actions.test.ts +++ b/packages/extension/src/background/transaction/actions.test.ts @@ -2,6 +2,7 @@ import { getValidatedTxnWrap } from './actions'; import { BaseValidatedTxnWrap } from './baseValidatedTxnWrap'; import { AssetConfigTransaction } from './acfgTransaction'; import { AssetTransferTransaction } from './axferTransaction'; +import { AssetCloseTransaction } from './axferCloseTransaction'; import { AssetFreezeTransaction } from './afrzTransaction'; test('Validate build of pay transaction', () => { @@ -96,10 +97,33 @@ test('Validate build of axfer transaction', () => { }; const result = getValidatedTxnWrap(preTransaction, 'axfer'); + console.log(result) expect(result instanceof BaseValidatedTxnWrap).toBe(true); expect(result instanceof AssetTransferTransaction).toBe(true); }); +test('Validate build of axfer close transaction', () => { + const preTransaction = { + type: 'axfer', + from: 'NM2MBC673SL7TQIKUXD4JOBR3XQITDCHIMIEODQBUGFMAN54QV2VUYWZNQ', + to: 'NM2MBC673SL7TQIKUXD4JOBR3XQITDCHIMIEODQBUGFMAN54QV2VUYWZNQ', + closeRemainderTo: 'NM2MBC673SL7TQIKUXD4JOBR3XQITDCHIMIEODQBUGFMAN54QV2VUYWZNQ', + fee: 1000, + assetIndex: 1, + amount: 12345, + firstRound: 1, + lastRound: 1001, + genesisID: 'testnet-v1.0', + genesisHash: 'SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=', + note: new Uint8Array(0), + }; + + const result = getValidatedTxnWrap(preTransaction, 'axfer'); + console.log(result); + expect(result instanceof BaseValidatedTxnWrap).toBe(true); + expect(result instanceof AssetCloseTransaction).toBe(true); +}); + test('Validate build of transaction', () => { const preTransaction = { type: 'faketype', diff --git a/packages/ui/src/components/Account/__snapshots__/AssetDetails.test.ts.snap b/packages/ui/src/components/Account/__snapshots__/AssetDetails.test.ts.snap index 5bbeb8df..43289e96 100644 --- a/packages/ui/src/components/Account/__snapshots__/AssetDetails.test.ts.snap +++ b/packages/ui/src/components/Account/__snapshots__/AssetDetails.test.ts.snap @@ -77,7 +77,7 @@ exports[`AssetDetails should match snapshot 1`] = `

- + diff --git a/packages/ui/src/components/ContactList.ts b/packages/ui/src/components/ContactList.ts index 1a7e116d..ea36fd32 100644 --- a/packages/ui/src/components/ContactList.ts +++ b/packages/ui/src/components/ContactList.ts @@ -6,6 +6,7 @@ import { JsonRpcMethod } from '@algosigner/common/messaging/types'; import { sendMessage } from 'services/Messaging'; import ContactPreview from 'components/ContactPreview'; +import goalseekerIcon from 'assets/goalseeker.svg'; import algosdk from 'algosdk'; const ContactList: FunctionalComponent = () => { @@ -227,14 +228,14 @@ const ContactList: FunctionalComponent = () => { onClick=${() => openEditModal(c.name, c.address)} > - + <${ContactPreview} contact="${c}" diff --git a/packages/ui/src/pages/AccountDetails.ts b/packages/ui/src/pages/AccountDetails.ts index aaa30c7b..dd400198 100644 --- a/packages/ui/src/pages/AccountDetails.ts +++ b/packages/ui/src/pages/AccountDetails.ts @@ -62,6 +62,7 @@ const AccountDetails: FunctionalComponent = (props: any) => { qr.addData(address); qr.make(); const qrImg = qr.createDataURL(10, 1); + const ledgerName = ledger.toLowerCase(); return html`
@@ -91,9 +92,19 @@ const AccountDetails: FunctionalComponent = (props: any) => { Opted-in Apps
${details['apps-local-state'].map( - (ca) => html` + (oia) => html` - ${ca.id} + ${oia.id} + + + ` )} @@ -115,6 +126,16 @@ const AccountDetails: FunctionalComponent = (props: any) => { ${cas.index} `} ${(!cas.params.name || cas.params.name.length === 0) && html`${cas.index}`} + + + ` )} @@ -129,13 +150,17 @@ const AccountDetails: FunctionalComponent = (props: any) => { ${details['created-apps'].map( (cap) => html` - ${cap.params.name && - cap.params.name.length > 0 && - html` - ${cap.params.name} - ${cap.index} - `} - ${(!cap.params.name || cap.params.name.length === 0) && html`${cap.index}`} + ${cap.id} + + + ` )} From a6d4619883a896eab995c38995624c6f9c5ef18b Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Wed, 26 Jan 2022 15:26:45 -0300 Subject: [PATCH 11/13] Update tests --- packages/test-project/tests/basic-e2e-dapp.test.js | 6 +++--- packages/test-project/tests/common/tests.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/test-project/tests/basic-e2e-dapp.test.js b/packages/test-project/tests/basic-e2e-dapp.test.js index 863405b9..3bf8b175 100644 --- a/packages/test-project/tests/basic-e2e-dapp.test.js +++ b/packages/test-project/tests/basic-e2e-dapp.test.js @@ -438,9 +438,9 @@ describe('dApp POST Txn Tests (plus Teal compile)', () => { var popup = pages[pages.length - 1]; await appPage.waitForTimeout(500); await popup.waitForSelector('#txAlerts'); - await expect( - popup.$eval('#danger-tx-list b', (e) => e.innerText) - ).resolves.toContain('Deprecate'); + await expect(popup.$eval('#danger-tx-list', (e) => e.innerText)).resolves.toContain( + 'Deprecated' + ); await popup.waitForSelector('#approveTx'); await popup.click('#approveTx', { waitUntil: 'networkidle' }); await popup.waitForSelector('#enterPassword'); diff --git a/packages/test-project/tests/common/tests.js b/packages/test-project/tests/common/tests.js index e9476117..169781e3 100644 --- a/packages/test-project/tests/common/tests.js +++ b/packages/test-project/tests/common/tests.js @@ -65,7 +65,7 @@ function VerifyAccount(account) { await expect(extensionPage.$eval('#accountAddress', (e) => e.innerText)).resolves.toBe( account.address ); - await closeModal(); + await goBack(); await goBack(); }); } From 9406594a3a094f61e481ec32fefefc343c4b66bf Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Thu, 27 Jan 2022 16:13:40 -0300 Subject: [PATCH 12/13] Adjust network tests --- packages/test-project/tests/ui-networks-e2e.test.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/test-project/tests/ui-networks-e2e.test.js b/packages/test-project/tests/ui-networks-e2e.test.js index 04dce1c8..70c57bb1 100644 --- a/packages/test-project/tests/ui-networks-e2e.test.js +++ b/packages/test-project/tests/ui-networks-e2e.test.js @@ -98,16 +98,6 @@ describe('Create and Test Custom Networks', () => { test('Test Deleting Networks', async () => { await openNetworkMenu(); - // Delete E2ENet - await extensionPage.waitForSelector(e2eNetSelector); - await extensionPage.click(e2eNetSelector); - await extensionPage.click('#deleteNetwork'); - await inputPassword(); - - // Check network was deleted - await openNetworkMenu(); - await expect(extensionPage.select(e2eNetSelector)).rejects.toThrow(); - // Delete OtherNet await extensionPage.waitForSelector(otherNetSelector); await extensionPage.click(otherNetSelector); From 3fa4487fc39fd411edd6edff05fdbd87d1229f2f Mon Sep 17 00:00:00 2001 From: Jan Marcano Date: Thu, 27 Jan 2022 16:48:01 -0300 Subject: [PATCH 13/13] Update build scripts --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6ade33af..53a69084 100644 --- a/package.json +++ b/package.json @@ -12,17 +12,17 @@ "scripts": { "install:extension": "(cd ./packages/common && npm install); (cd ./packages/crypto && npm install); (cd ./packages/storage && npm install); (cd ./packages/ui && npm install); (cd ./packages/extension && npm install); (cd ./packages/dapp && npm install);", "install:test": "(cd ./packages/test-project && npm install);", - "removelocks": "rm -rf ./package-lock.json && find -path \"./packages/*\" -name \"package-lock.json\" -not -path \"*/node_modules/*\" -exec rm -rf {} \\;", - "update": "npm update && find -maxdepth 2 -path \"./packages/*\" -exec npm update {} \\;", + "removelocks": "rm -rf ./package-lock.json && find . -path \"./packages/*\" -name \"package-lock.json\" -not -path \"*/node_modules/*\" -exec rm -rf {} \\;", + "removemodules": "rm -rf node_modules && find . -path \"./packages/*\" -name \"node_modules*\" -exec rm -rf {} \\;", "build": "(cd ./packages/common && npm run build); (cd ./packages/crypto && npm run build); (cd ./packages/dapp && npm run build); (cd ./packages/storage && npm run build); (cd ./packages/ui && npm run build); (cd ./packages/extension && npm run build);", "build:ui": "cd ./packages/ui && npm run build && cp -r ./dist/* ../../dist/", "build:extension": "cd ./packages/extension && npm run build && cp -r ./dist/* ../../dist/", - "clean": "rm -rf ./dist/* && rm -rf ./packages/common/dist/* && rm -rf ./packages/crypto/dist/* && rm -rf ./packages/dapp/dist/* && rm -rf ./packages/extension/dist/* && rm -rf ./packages/storage/dist/* && rm -rf ./packages/ui/dist/* && rm -rf ./packages/dapp/lib/*", + "clean": "rm -rf ./dist && rm -rf ./packages/common/dist && rm -rf ./packages/crypto/dist && rm -rf ./packages/dapp/dist && rm -rf ./packages/extension/dist && rm -rf ./packages/storage/dist && rm -rf ./packages/ui/dist && rm -rf ./packages/dapp/dist", "copy": "mkdir -p ./dist && cp -r ./packages/extension/dist/* dist/ && cp -r ./packages/ui/dist/* dist/ && cp -r ./packages/dapp/dist/* dist/ && mkdir -p ./dist/docs && cp -r ./docs/* dist/docs/", "prebuild": "rm -rf ./dist/*", "postbuild": "npm run copy", "postinstall": "npm run install:extension && npm run install:test", - "rebuild": "npm run clean && npm run removelocks && npm install && npm run update && npm run build", + "rebuild": "npm run clean && npm run removelocks && npm run removemodules && npm install && npm run build", "coveragetest": "(cd ./packages/test-project && npm run coveragetest)", "test": "npm run test:unit && npm run test:e2e", "test:unit": "(cd ./packages/crypto && npm run test) && (cd ./packages/extension && npm run test) && (cd ./packages/ui && npm run test) && (cd ./packages/dapp && npm run test) && (cd ./packages/common && npm run test)",