diff --git a/.vscode/settings.json b/.vscode/settings.json index 867cce5..b62da21 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { - "editor.insertSpaces": false, "tslint.enable": true, "typescript.tsc.autoDetect": "off", "typescript.preferences.quoteStyle": "single", diff --git a/client/package-lock.json b/client/package-lock.json index 7a2260b..493861d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1325,9 +1325,9 @@ "integrity": "sha512-Vwhc3ObxmDZmA5hY8mfsau2rJ4vGPvzbj20QSZ2/E1GDPF61QVyjLfNHak9xmel6pW4heRt3v1fHa6np9Ehfeg==" }, "@obsidians/ui-components": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@obsidians/ui-components/-/ui-components-0.2.1.tgz", - "integrity": "sha512-7x/XuFvzgVj3S00IVVNppPehL9aPLUvkB2aIi10hrWvtBEuwP/qle1g/waK65zbg8+m1QxmorKgF6jRZv8XZIw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@obsidians/ui-components/-/ui-components-0.2.2.tgz", + "integrity": "sha512-ZAwukj1HjW1bKX0obhASn4klVv2mk78X3Ifn7IS+DijsMFYFz0gVXVD1j8hnGOdAfjsxyfTleY2qTl4fXwKXgQ==", "requires": { "lodash": "^4.17.15", "react-select": "^3.0.8", diff --git a/client/package.json b/client/package.json index d412810..2749643 100644 --- a/client/package.json +++ b/client/package.json @@ -20,10 +20,11 @@ }, "dependencies": { "@fortawesome/fontawesome-pro": "^5.14.0", - "@obsidians/ui-components": "^0.2.1", + "@obsidians/ui-components": "^0.2.2", "algosdk": "^1.7.2", "bootstrap": "^4.5.2", "classnames": "^2.2.6", + "lodash": "^4.17.20", "moment": "^2.28.0", "node-fetch": "^2.6.1", "path-browserify": "^1.0.1", diff --git a/client/src/view/AlgoSdk/AlgoTransaction/index.js b/client/src/view/AlgoSdk/AlgoTransaction/index.js index 110a257..4a28dff 100644 --- a/client/src/view/AlgoSdk/AlgoTransaction/index.js +++ b/client/src/view/AlgoSdk/AlgoTransaction/index.js @@ -66,7 +66,8 @@ export default class AlgoTransaction { } parseSingleTxn (txn) { - const { type, params, ...rest } = txn + const { type, params, ...rest } = txn + rest.lease = this.getLease(rest.lease) let algoTxn switch (type) { case 'pay': @@ -83,64 +84,54 @@ export default class AlgoTransaction { to: this.getAddress(params.to), amount, closeRemainderTo: this.getAddress(params.closeRemainderTo), - note: params.note || undefined, - lease: this.getLease(params.lease), ...rest, } break; case 'asset-create': algoTxn = { type: 'acfg', - from: this.getAddress(params.from || params.manager), - note: params.note || undefined, + from: this.getAddress(params.from), assetTotal: params.total, assetDecimals: params.decimals, assetName: params.name, assetUnitName: params.unit, assetURL: params.url, assetMetadataHash: params.meta, - assetManager: this.getAddress(params.manager), - assetReserve: this.getAddress(params.reserve || params.manager), - assetFreeze: this.getAddress(params.freeze || params.manager), - assetClawback: this.getAddress(params.clawback || params.manager), + assetManager: this.getAddress(params.manager || params.from), + assetReserve: this.getAddress(params.reserve), + assetFreeze: this.getAddress(params.freeze), + assetClawback: this.getAddress(params.clawback), assetDefaultFrozen: params.defaultFrozen, - lease: this.getLease(params.lease), ...rest, } break; case 'asset-modify': algoTxn = { type: 'acfg', - assetIndex: params.assetId, from: this.getAddress(params.from), - note: params.note || undefined, + assetIndex: params.assetId, assetManager: this.getAddress(params.manager), assetReserve: this.getAddress(params.reserve), assetFreeze: this.getAddress(params.freeze), assetClawback: this.getAddress(params.clawback), - lease: this.getLease(params.lease), ...rest, } break case 'asset-freeze': algoTxn = { type: 'afrz', - assetIndex: params.assetId, from: this.getAddress(params.from), - note: params.note || undefined, + assetIndex: params.assetId, freezeAccount: this.getAddress(params.target), freezeState: params.state, - lease: this.getLease(params.lease), ...rest, } break case 'asset-destroy': algoTxn = { type: 'acfg', - assetIndex: params.assetId, from: this.getAddress(params.from), - note: params.note || undefined, - lease: this.getLease(params.lease), + assetIndex: params.assetId, ...rest, } break @@ -150,8 +141,6 @@ export default class AlgoTransaction { from: this.getAddress(params.from), to: this.getAddress(params.from), assetIndex: params.assetId, - note: params.note || undefined, - lease: this.getLease(params.lease), ...rest, } break @@ -163,9 +152,7 @@ export default class AlgoTransaction { amount: params.amount, assetIndex: params.assetId, closeRemainderTo: this.getAddress(params.closeRemainderTo), - assetRevocationTarget: this.getAddress(params.assetRevocationTarget), - note: params.note || undefined, - lease: this.getLease(params.lease), + assetRevocationTarget: this.getAddress(params.clawback), ...rest, } break @@ -173,13 +160,11 @@ export default class AlgoTransaction { algoTxn = { type: 'keyreg', from: this.getAddress(params.from), - note: params.note || undefined, voteKey: this.getAddress(params.vote), selectionKey: this.getAddress(params.selection), voteFirst: params.first, voteLast: params.last, voteKeyDilution: params.dilution, - lease: this.getLease(params.lease), ...rest, } break diff --git a/client/src/view/components/Transaction/TransactionModal.jsx b/client/src/view/components/Transaction/TransactionModal.jsx index 18a955a..8d3be67 100644 --- a/client/src/view/components/Transaction/TransactionModal.jsx +++ b/client/src/view/components/Transaction/TransactionModal.jsx @@ -8,72 +8,111 @@ import { Label, } from '@obsidians/ui-components' +import pick from 'lodash/pick' + const fieldsByType = { pay: [ - { label: 'From', name: 'from' }, - { label: 'To', name: 'to' }, - { label: 'Amount', name: 'amount' }, - { label: 'Note', name: 'note' }, - { label: 'Close Remainder To', name: 'closeRemainderTo' }, + { label: 'From', name: 'from', tooltip: 'The address that sends the amount' }, + { label: 'To', name: 'to', tooltip: 'The address that receives the amount' }, + { label: 'Amount', name: 'amount', tooltip: 'The total amount to be sent in ALGO' }, + { + label: 'Close Remainder To', + name: 'closeRemainderTo', + tooltip: 'When set, all remaining balance will be transferred to this address and the sender account will be closed' + }, ], 'asset-create': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Asset Name', name: 'name' }, - { label: 'Asset Unit', name: 'unit' }, - { label: 'Asset Total', name: 'total' }, - { label: 'Asset Decimals', name: 'decimals' }, - { label: 'Asset URL', name: 'url' }, - { label: 'Asset Meta', name: 'meta' }, - { label: 'Asset Manager', name: 'manager' }, - { label: 'Asset Reserve', name: 'reserve' }, - { label: 'Asset Frozen', name: 'freeze' }, - { label: 'Asset Clawback', name: 'clawback' }, - { label: 'Asset Default Frozen', name: 'defaultFrozen' }, + { label: 'From', name: 'from', tooltip: 'The address that sends the transaction' }, + { label: 'Asset Name', name: 'name', tooltip: 'Name of the asset (e.g. Tether)' }, + { label: 'Asset Unit', name: 'unit', tooltip: 'Name of unit of the asset (e.g. USDT)' }, + { label: 'Asset Total', name: 'total', tooltip: 'Total number of the asset in base units' }, + { label: 'Asset Decimals', name: 'decimals', tooltip: 'Number of digits to use after the decimal point' }, + { label: 'Asset URL', name: 'url', tooltip: 'A URL where more info can be retrieved; max 32 bytes' }, + { label: 'Asset Meta', name: 'meta', tooltip: 'A 32-byte hash of some metadata that is relevant to your asset' }, + { + label: 'Asset Manager', + name: 'manager', + placeholder: 'Default: From', + tooltip: 'An address that can manage the asset and destroy it.', + }, + { + label: 'Asset Reserve', + name: 'reserve', + tooltip: 'An address that holds the non-minted units of the asset', + }, + { + label: 'Asset Frozen', + name: 'freeze', + tooltip: 'An address that holds frozen asset. If empty, freezing is not permitted.', + }, + { + label: 'Asset Clawback', + name: 'clawback', + tooltip: 'An address that can clawback holdings of this asset. If empty, clawback is not permitted.', + }, + { label: 'Asset Default Frozen', name: 'defaultFrozen', type: 'boolean' }, ], 'asset-modify': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Asset ID', name: 'assetId' }, - { label: 'Asset Manager', name: 'manager' }, - { label: 'Asset Reserve', name: 'reserve' }, - { label: 'Asset Frozen', name: 'freeze' }, - { label: 'Asset Clawback', name: 'clawback' }, + { label: 'From', name: 'from', tooltip: `The address that sends the transaction; must be the assets's manager address` }, + { label: 'Asset ID', name: 'assetId', tooltip: 'ID of the asset' }, + { + label: 'Asset Manager', + name: 'manager', + tooltip: 'An address that can manage the asset and destroy it.', + }, + { + label: 'Asset Reserve', + name: 'reserve', + tooltip: 'An address that holds the non-minted units of the asset', + }, + { + label: 'Asset Frozen', + name: 'freeze', + tooltip: 'An address that holds frozen asset. If empty, freezing is not permitted.', + }, + { + label: 'Asset Clawback', + name: 'clawback', + tooltip: 'An address that can clawback holdings of this asset. If empty, clawback is not permitted.', + }, ], 'asset-freeze': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Asset ID', name: 'assetId' }, - { label: 'Freeze Account', name: 'target' }, - { label: 'Freeze State', name: 'state' }, + { label: 'From', name: 'from', tooltip: `The address that sends the transaction; must be the asset's freeze address` }, + { label: 'Asset ID', name: 'assetId', tooltip: 'ID of the asset' }, + { label: 'Freeze Account', name: 'target', tooltip: 'The address whose asset will be frozen or unfrozen' }, + { label: 'Freeze State', name: 'state', type: 'boolean' }, ], 'asset-destroy': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Asset ID', name: 'assetId' }, + { label: 'From', name: 'from', tooltip: `The address that sends the transaction; must be the assets's manager address` }, + { label: 'Asset ID', name: 'assetId', tooltip: 'ID of the asset' }, ], 'asset-opt-in': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Asset ID', name: 'assetId' }, + { label: 'From', name: 'from', tooltip: 'The address that sends the transaction' }, + { label: 'Asset ID', name: 'assetId', tooltip: 'ID of the asset' }, ], 'asset-transfer': [ - { label: 'From', name: 'from' }, - { label: 'To', name: 'to' }, - { label: 'Asset ID', name: 'assetId' }, - { label: 'Amount', name: 'amount' }, - { label: 'Close Remainder To', name: 'closeRemainderTo' }, - { label: 'Asset Revocation Target', name: 'assetRevocationTarget' }, - { label: 'Note', name: 'note' }, + { label: 'From', name: 'from', tooltip: `The address that sends the transaction; must be the asset's clawback address if Clawback Target is given` }, + { label: 'To', name: 'to', tooltip: 'The address that receives the asset' }, + { label: 'Asset ID', name: 'assetId', tooltip: 'ID of the asset' }, + { label: 'Amount', name: 'amount', tooltip: 'The total amount to be sent in base units' }, + { + label: 'Close Remainder To', + name: 'closeRemainderTo', + tooltip: 'When set, all remaining asset balance will be transferred to this address' + }, + { + label: 'Clawback Target', + name: 'clawback', + tooltip: `Indicates a clawback if set. This value is the address from which assets will be withdrawn.` + }, ], 'keyreg': [ - { label: 'From', name: 'from' }, - { label: 'Note', name: 'note' }, - { label: 'Vote Key', name: 'vote' }, - { label: 'Selection Key', name: 'selection' }, - { label: 'Vote First', name: 'first' }, - { label: 'Vote Last', name: 'last' }, - { label: 'Vote Key Dilution', name: 'dilution' }, + { label: 'From', name: 'from', tooltip: 'The address that sends the transaction' }, + { label: 'Vote Key', name: 'vote', tooltip: 'The root participation public key' }, + { label: 'Selection Key', name: 'selection', tooltip: 'The VRF public key' }, + { label: 'Vote First', name: 'first', tooltip: 'The first round that the participation key is valid' }, + { label: 'Vote Last', name: 'last', tooltip: 'The last round that the participation key is valid' }, + { label: 'Vote Key Dilution', name: 'dilution', tooltip: 'The dilution for the 2-level participation key' }, ], } @@ -89,6 +128,7 @@ export default class TransactionModal extends PureComponent { signers: '', fee: '', flatFee: false, + note: undefined, lease: '', } } @@ -104,6 +144,7 @@ export default class TransactionModal extends PureComponent { program: data.program, fee: data.fee, flatFee: data.flatFee, + note: data.note, lease: data.lease, }) } else { @@ -115,6 +156,7 @@ export default class TransactionModal extends PureComponent { program: '', fee: '', flatFee: false, + note: undefined, lease: '', }) } @@ -123,9 +165,10 @@ export default class TransactionModal extends PureComponent { } onConfirm = () => { + const fieldNames = fieldsByType[this.state.type].map(f => f.name) this.onResolve({ ...this.state, - values: { ...this.state.values } + values: pick(this.state.values, fieldNames) }) this.modal.current.closeModal() } @@ -135,7 +178,7 @@ export default class TransactionModal extends PureComponent { } renderTransactionForm = fields => { - return fields.map(({ label, name }, index) => { + return fields.map(({ label, name, tooltip, placeholder, type }) => { const value = this.state.values[name] const onChange = value => { this.setState({ values: { @@ -143,10 +186,25 @@ export default class TransactionModal extends PureComponent { [name]: value, }}) } + if (type) { + return ( + onChange(event.target.checked)} + /> + ) + } return ( this.setState({ signer })} /> @@ -234,7 +292,8 @@ export default class TransactionModal extends PureComponent { this.setState({ fee })} @@ -250,7 +309,16 @@ export default class TransactionModal extends PureComponent { this.setState({ note })} + /> + this.setState({ lease })} /> diff --git a/client/src/view/components/Transaction/index.jsx b/client/src/view/components/Transaction/index.jsx index 17de234..3f386e6 100644 --- a/client/src/view/components/Transaction/index.jsx +++ b/client/src/view/components/Transaction/index.jsx @@ -78,6 +78,9 @@ export default class Transaction extends PureComponent { if (data.flatFee) { txn.flatFee = data.flatFee } + if (data.note) { + txn.note = data.note + } if (data.lease) { txn.lease = data.lease } @@ -129,6 +132,7 @@ export default class Transaction extends PureComponent { values: txn.params, fee: txn.fee, flatFee: txn.flatFee, + note: txn.note, lease: txn.lease, } if (txn.lsig) { diff --git a/client/src/view/components/scss/index.scss b/client/src/view/components/scss/index.scss index 4e8aabf..dc532a3 100644 --- a/client/src/view/components/scss/index.scss +++ b/client/src/view/components/scss/index.scss @@ -177,3 +177,11 @@ html, body, #root, .h-100 { vertical-align: middle; } } + +.tooltip-inner { + max-width: 320px; +} + +.custom-checkbox>.custom-control-label { + line-height: 1.5rem; +} \ No newline at end of file