mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-11-24 00:46:27 +03:00
Merge pull request #2755 from ecency/sa/improve-hive-uri-support
[WIP] Improve hive uri QR support
This commit is contained in:
commit
522893056b
@ -16,10 +16,10 @@ import {
|
||||
import { deepLinkParser } from '../../utils/deepLinkParser';
|
||||
import RootNavigation from '../../navigation/rootNavigation';
|
||||
import getWindowDimensions from '../../utils/getWindowDimensions';
|
||||
import { isHiveUri } from '../../utils/hive-uri';
|
||||
import { handleHiveUriOperation } from '../../providers/hive/dhive';
|
||||
import { isHiveUri, getFormattedTx } from '../../utils/hive-uri';
|
||||
import { handleHiveUriOperation, resolveTransaction } from '../../providers/hive/dhive';
|
||||
import bugsnagInstance from '../../config/bugsnag';
|
||||
import { get, isArray } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
import showLoginAlert from '../../utils/showLoginAlert';
|
||||
import authType from '../../constants/authType';
|
||||
import { delay } from '../../utils/editor';
|
||||
@ -156,55 +156,70 @@ export const QRModal = ({}: QRModalProps) => {
|
||||
}
|
||||
|
||||
const parsed = hiveuri.decode(uri);
|
||||
// resolve the decoded tx and params to a signable tx
|
||||
let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, {
|
||||
signers: currentAccount.name,
|
||||
preferred_signer: currentAccount.name,
|
||||
});
|
||||
const operations = get(tx, 'operations', []);
|
||||
if (!_checkOpsArray(operations)) {
|
||||
Alert.alert(
|
||||
intl.formatMessage({
|
||||
id: 'qr.multi_array_ops_alert',
|
||||
}),
|
||||
intl.formatMessage({
|
||||
id: 'qr.multi_array_ops_aler_desct',
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
showActionModal({
|
||||
title: intl.formatMessage({
|
||||
id: 'qr.confirmTransaction',
|
||||
}),
|
||||
bodyContent: _checkOpsArray(operations) ? _renderActionModalBody(operations[0]) : null,
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({
|
||||
id: 'qr.cancel',
|
||||
const authoritiesMap = new Map();
|
||||
authoritiesMap.set('active', currentAccount?.local?.activeKey ? true : false);
|
||||
authoritiesMap.set('posting', currentAccount?.local?.postingKey ? true : false);
|
||||
authoritiesMap.set('owner', currentAccount?.local?.ownerKey ? true : false);
|
||||
authoritiesMap.set('memo', currentAccount?.local?.memoKey ? true : false);
|
||||
|
||||
getFormattedTx(parsed.tx, authoritiesMap)
|
||||
.then(async (formattedTx) => {
|
||||
// resolve the decoded tx and params to a signable tx
|
||||
const tx = await resolveTransaction(formattedTx.tx, parsed.params, currentAccount.name);
|
||||
const ops = get(tx, 'operations', []);
|
||||
const op = ops[0];
|
||||
|
||||
dispatch(
|
||||
showActionModal({
|
||||
title: intl.formatMessage({
|
||||
id: 'qr.confirmTransaction',
|
||||
}),
|
||||
onPress: () => {},
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({
|
||||
id: 'qr.approve',
|
||||
}),
|
||||
onPress: () => {
|
||||
handleHiveUriOperation(currentAccount, pinCode, uri)
|
||||
.then(() => {
|
||||
dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' })));
|
||||
})
|
||||
.catch((err) => {
|
||||
bugsnagInstance.notify(err);
|
||||
dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' })));
|
||||
});
|
||||
bodyContent: _renderActionModalBody(op, formattedTx.opName),
|
||||
buttons: [
|
||||
{
|
||||
text: intl.formatMessage({
|
||||
id: 'qr.cancel',
|
||||
}),
|
||||
onPress: () => {},
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({
|
||||
id: 'qr.approve',
|
||||
}),
|
||||
onPress: () => {
|
||||
handleHiveUriOperation(currentAccount, pinCode, tx)
|
||||
.then(() => {
|
||||
dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' })));
|
||||
})
|
||||
.catch((err) => {
|
||||
bugsnagInstance.notify(err);
|
||||
if (err) {
|
||||
dispatch(toastNotification(intl.formatMessage({ id: err })));
|
||||
} else {
|
||||
dispatch(
|
||||
toastNotification(intl.formatMessage({ id: 'qr.transaction_failed' })),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch((errObj) => {
|
||||
Alert.alert(
|
||||
intl.formatMessage({ id: errObj.errorKey1 }, { key: errObj.authorityKeyType }),
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: errObj.errorKey2,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
{ key: errObj.authorityKeyType },
|
||||
),
|
||||
);
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
const _handleDeepLink = async (url) => {
|
||||
@ -221,11 +236,6 @@ export const QRModal = ({}: QRModalProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
// check operation array is valid and is a single operation array
|
||||
const _checkOpsArray = (ops) => {
|
||||
return ops && isArray(ops) && ops.length === 1 && isArray(ops[0]) && ops[0].length === 2;
|
||||
};
|
||||
|
||||
const _renderTransactionInfoRow = (item: any) => (
|
||||
<View style={styles.transactionRow}>
|
||||
<Text numberOfLines={1} style={styles.transactionItem1}>
|
||||
@ -236,10 +246,10 @@ export const QRModal = ({}: QRModalProps) => {
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
const _renderActionModalBody = (operations: any) => (
|
||||
const _renderActionModalBody = (operations: any, opName: string) => (
|
||||
<View style={styles.transactionBodyContainer}>
|
||||
<View style={styles.transactionHeadingContainer}>
|
||||
<Text style={styles.transactionHeading}>{operations[0]}</Text>
|
||||
<Text style={styles.transactionHeading}>{opName}</Text>
|
||||
</View>
|
||||
<View style={styles.transactionItemsContainer}>
|
||||
{Object.entries(operations[1]).map((item) => _renderTransactionInfoRow(item))}
|
||||
|
@ -1041,11 +1041,30 @@
|
||||
"confirmTransaction": "Confirm transaction",
|
||||
"approve": "Approve",
|
||||
"cancel": "Cancel",
|
||||
"multi_array_ops_alert": "Multiple operations array detected!",
|
||||
"multi_array_ops_aler_desct": "Ecency does not support signing multiple operations, yet"
|
||||
"multi_array_ops_alert": "Operation not supported by Ecency",
|
||||
"multi_array_ops_aler_desct": "Ecency does not support signing multiple operations yet",
|
||||
"invalid_op": "Invalid operation data",
|
||||
"invalid_op_desc": "try contacting QR/link author",
|
||||
"invalid_amount": "Invalid Amount",
|
||||
"invalid_amount_desc": "Enter valid amount in proper format",
|
||||
"transaction_failed": "Transaction Failed!",
|
||||
"invalid_key": "{key} key is required to perform this transaction",
|
||||
"invalid_key_desc": "kindly login with {key} key or master key to perform this transaction"
|
||||
},
|
||||
"history": {
|
||||
"edit": "Edit History",
|
||||
"version": "Version"
|
||||
},
|
||||
"chain-error": {
|
||||
"min-root-comment": "You may only post once every five minutes.",
|
||||
"identical-vote": "Your current vote on this content is identical to this vote.",
|
||||
"insufficient-resource": "Insufficient Resource Credits.",
|
||||
"delete-comment-with-vote": "Cannot delete a content with positive pending rewards.",
|
||||
"comment-cashout": "Content after their payout cannot be deleted.",
|
||||
"comment-children": "Cannot delete a content with replies.",
|
||||
"paid-out-post-forbidden": "Voting for paid out content is not available.",
|
||||
"missing-authority": "This operation requires Active private key or authority.",
|
||||
"missing-owner-authority": "This operation requires Owner private key or authority.",
|
||||
"insufficient_fund": "Insufficient Funds"
|
||||
}
|
||||
}
|
||||
|
@ -2082,59 +2082,77 @@ export const votingPower = (account) => {
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
export const resolveTransaction = async (parsedTx, parsedParams, signer) => {
|
||||
const EXPIRE_TIME = 60 * 1000;
|
||||
const props = await client.database.getDynamicGlobalProperties();
|
||||
|
||||
// resolve the decoded tx and params to a signable tx
|
||||
const { tx } = hiveuri.resolveTransaction(parsedTx, parsedParams, {
|
||||
/* eslint-disable no-bitwise */
|
||||
ref_block_num: props.head_block_number & 0xffff,
|
||||
ref_block_prefix: Buffer.from(props.head_block_id, 'hex').readUInt32LE(4),
|
||||
expiration: new Date(Date.now() + client.broadcast.expireTime + EXPIRE_TIME)
|
||||
.toISOString()
|
||||
.slice(0, -5),
|
||||
signers: [signer],
|
||||
preferred_signer: signer,
|
||||
});
|
||||
tx.ref_block_num = parseInt(tx.ref_block_num + '', 10);
|
||||
tx.ref_block_prefix = parseInt(tx.ref_block_prefix + '', 10);
|
||||
|
||||
return tx;
|
||||
};
|
||||
|
||||
const handleChainError = (strErr: string) => {
|
||||
if (/You may only post once every/.test(strErr)) {
|
||||
return 'chain-error.min-root-comment';
|
||||
} else if (/Your current vote on this comment is identical/.test(strErr)) {
|
||||
return 'chain-error.identical-vote';
|
||||
} else if (/Please wait to transact, or power up/.test(strErr)) {
|
||||
return 'chain-error.insufficient-resource';
|
||||
} else if (/Cannot delete a comment with net positive/.test(strErr)) {
|
||||
return 'chain-error.delete-comment-with-vote';
|
||||
} else if (/children == 0/.test(strErr)) {
|
||||
return 'chain-error.comment-children';
|
||||
} else if (/comment_cashout/.test(strErr)) {
|
||||
return 'chain-error.comment-cashout';
|
||||
} else if (/Votes evaluating for comment that is paid out is forbidden/.test(strErr)) {
|
||||
return 'chain-error.paid-out-post-forbidden';
|
||||
} else if (/Missing Active Authority/.test(strErr)) {
|
||||
return 'chain-error.missing-authority';
|
||||
} else if (/Missing Owner Authority/.test(strErr)) {
|
||||
return 'chain-error.missing-owner-authority';
|
||||
} else if (/does not have sufficient funds/.test(strErr)) {
|
||||
return 'chain-error.insufficient_fund';
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const handleHiveUriOperation = async (
|
||||
currentAccount: any,
|
||||
pin: any,
|
||||
hiveUri: string,
|
||||
tx: any,
|
||||
): Promise<TransactionConfirmation> => {
|
||||
try {
|
||||
const digitPinCode = getDigitPinCode(pin);
|
||||
const key = getAnyPrivateKey(currentAccount.local, digitPinCode);
|
||||
const privateKey = PrivateKey.fromString(key);
|
||||
|
||||
const { head_block_number, head_block_id, time } = await getDynamicGlobalProperties();
|
||||
const ref_block_num = head_block_number & 0xffff;
|
||||
const ref_block_prefix = Buffer.from(head_block_id, 'hex').readUInt32LE(4);
|
||||
const expireTime = 60 * 1000;
|
||||
const chainId = Buffer.from(
|
||||
'beeab0de00000000000000000000000000000000000000000000000000000000',
|
||||
'hex',
|
||||
);
|
||||
const expiration = new Date(new Date(`${time}Z`).getTime() + expireTime)
|
||||
.toISOString()
|
||||
.slice(0, -5);
|
||||
const extensions = [];
|
||||
|
||||
|
||||
const parsed = hiveuri.decode(hiveUri)
|
||||
// resolve the decoded tx and params to a signable tx
|
||||
let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, {
|
||||
|
||||
expiration,
|
||||
// accounts we are able to sign for
|
||||
signers: currentAccount.name,
|
||||
// selected signer if none is asked for by the params
|
||||
preferred_signer: currentAccount.name,
|
||||
});
|
||||
|
||||
|
||||
//inject raw ref_block_num and ref_block_prefex to avoid string converstion by hiveuri.resolveTransaction
|
||||
// e.g. from a get_dynamic_global_properties call
|
||||
tx.ref_block_num = ref_block_num;
|
||||
tx.ref_block_prefix = ref_block_prefix;
|
||||
|
||||
// console.log('tx : ', JSON.stringify(tx, null, 2));
|
||||
// console.log('tx : ', tx);
|
||||
const transaction = cryptoUtils.signTransaction(tx, privateKey, chainId);
|
||||
const trxId = generateTrxId(transaction);
|
||||
const resultHive = await client.broadcast.call('broadcast_transaction', [transaction]);
|
||||
const result = Object.assign({ id: trxId }, resultHive);
|
||||
// console.log('result : ', JSON.stringify(result, null, 2));
|
||||
return result;
|
||||
} catch (err) {
|
||||
const errString = handleChainError(err.toString());
|
||||
bugsnagInstance.notify(err, (event) => {
|
||||
event.context = 'handle-hive-uri-operations';
|
||||
event.setMetaData('hiveUri', hiveUri);
|
||||
event.context = 'handle-hive-uri-operation';
|
||||
event.setMetaData('tx', tx);
|
||||
});
|
||||
throw err;
|
||||
return Promise.reject(errString);
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,129 @@
|
||||
import { get, isArray } from 'lodash';
|
||||
const operationsData = require('./operations.json');
|
||||
|
||||
/**
|
||||
* checks if uri entered is valid hive uri
|
||||
* Accepts a string
|
||||
* Returns boolean if uri starts with 'hive://'
|
||||
* */
|
||||
|
||||
export const isHiveUri = (uri: string) => {
|
||||
let trimUri = uri.trim();
|
||||
return trimUri.startsWith('hive://');
|
||||
};
|
||||
|
||||
// check operation array is valid and is a single operation array
|
||||
const _checkOpsArray = (ops: any) => {
|
||||
return ops && isArray(ops) && ops.length === 1 && isArray(ops[0]) && ops[0].length === 2;
|
||||
};
|
||||
|
||||
const findParentKey = (obj, value, parentKey = null) => {
|
||||
for (let key in obj) {
|
||||
if (obj[key] === value) {
|
||||
return parentKey;
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
const foundKey = findParentKey(obj[key], value, key);
|
||||
if (foundKey) {
|
||||
return foundKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// get operation name and signer field from operation object
|
||||
const getOperationProps = (opName: string) => {
|
||||
const op = get(operationsData, opName, null);
|
||||
if (op) {
|
||||
const signerField = findParentKey(op, '__signer');
|
||||
return {
|
||||
opName: op.name,
|
||||
opAuthority: op.authority || '',
|
||||
signerField,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// validate and format amount field in operation to 3 decimal places
|
||||
const _formatAmount = (amount: string) => {
|
||||
const splitAmt = amount.split(' ');
|
||||
if (
|
||||
splitAmt.length === 2 &&
|
||||
parseFloat(splitAmt[0]) &&
|
||||
(splitAmt[1] === 'HIVE' || splitAmt[1] === 'HBD')
|
||||
) {
|
||||
return `${parseFloat(splitAmt[0]).toFixed(3)} ${splitAmt[1]}`;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates tx from parsed data from hive-uri, checks if operation length is not greater than one and __signer is present in operation
|
||||
* Accepts tx object of parsed uri from decode method of hive-uri
|
||||
* Returns promise with keys for showing errors, and operation name parsed from operations.json and in case of success returns formatted tx
|
||||
*
|
||||
* */
|
||||
export const getFormattedTx = (tx: any, authoritiesMap: Map<string, boolean>) => {
|
||||
let opName;
|
||||
let errorObj = {
|
||||
errorKey1: '',
|
||||
errorKey2: '',
|
||||
authorityKeyType: '',
|
||||
};
|
||||
|
||||
const ops = get(tx, 'operations', []);
|
||||
const isValidOp = _checkOpsArray(ops);
|
||||
if (!isValidOp) {
|
||||
errorObj.errorKey1 = 'qr.multi_array_ops_alert';
|
||||
errorObj.errorKey2 = 'qr.multi_array_ops_aler_desct';
|
||||
return Promise.reject(errorObj);
|
||||
}
|
||||
const op = ops[0]; // single operation
|
||||
const operationName = op[0]; // operation name
|
||||
let operationObj = op[1]; // operation object
|
||||
|
||||
if (!operationName) {
|
||||
errorObj.errorKey1 = 'qr.invalid_op';
|
||||
errorObj.errorKey2 = 'qr.invalid_op_desc';
|
||||
return Promise.reject(errorObj);
|
||||
}
|
||||
const opProps = getOperationProps(operationName); // get operation props from operations.json file i-e signer field and operation name
|
||||
errorObj.authorityKeyType = opProps?.opAuthority || ''; // set key type to validate object
|
||||
|
||||
if (!opProps) {
|
||||
errorObj.errorKey1 = 'qr.invalid_op';
|
||||
errorObj.errorKey2 = 'qr.invalid_op_desc';
|
||||
return Promise.reject(errorObj);
|
||||
}
|
||||
if (authoritiesMap && !authoritiesMap.get(opProps.opAuthority)) {
|
||||
errorObj.errorKey1 = 'qr.invalid_key';
|
||||
errorObj.errorKey2 = 'qr.invalid_key_desc';
|
||||
return Promise.reject(errorObj);
|
||||
}
|
||||
// if amount field present in operation, validate and check for proper formatting and format to 3 decimal places
|
||||
if (operationObj.hasOwnProperty('amount')) {
|
||||
const amount = _formatAmount(operationObj.amount);
|
||||
operationObj.amount = amount;
|
||||
if (!amount) {
|
||||
errorObj.errorKey1 = 'qr.invalid_amount';
|
||||
errorObj.errorKey2 = 'qr.invalid_amount_desc';
|
||||
return Promise.reject(errorObj);
|
||||
}
|
||||
}
|
||||
const opSignerValue = get(op[1], opProps.signerField, '');
|
||||
// if signer field contains empty value, fill it with __signer
|
||||
if (!opSignerValue) {
|
||||
operationObj[opProps.signerField] = '__signer';
|
||||
}
|
||||
|
||||
opName = opProps.opName;
|
||||
tx = {
|
||||
...tx,
|
||||
operations: [[operationName, operationObj]],
|
||||
};
|
||||
// resolve with formatted tx and opName
|
||||
return Promise.resolve({ tx: tx, opName: opName });
|
||||
};
|
||||
|
741
src/utils/operations.json
Normal file
741
src/utils/operations.json
Normal file
@ -0,0 +1,741 @@
|
||||
{
|
||||
"transfer": {
|
||||
"name": "Transfer",
|
||||
"authority": "active",
|
||||
"description": "Transfers asset from one account to another.",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to": {
|
||||
"type": "account"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
},
|
||||
"memo": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"maxLength": 2048
|
||||
}
|
||||
}
|
||||
},
|
||||
"recurrent_transfer": {
|
||||
"name": "Recurring Transfers",
|
||||
"authority": "active",
|
||||
"description": "Recurring transfers of asset from one account to another.",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to": {
|
||||
"type": "account"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
},
|
||||
"memo": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"maxLength": 2048
|
||||
},
|
||||
"recurrence": {
|
||||
"type": "int"
|
||||
},
|
||||
"executions": {
|
||||
"type": "int"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"delegate_vesting_shares": {
|
||||
"name": "Delegate Hive Power",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"delegator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"delegatee": {
|
||||
"type": "account"
|
||||
},
|
||||
"vesting_shares": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transfer_to_vesting": {
|
||||
"name": "Power up",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_withdraw_vesting_route": {
|
||||
"name": "Set withdraw vesting route",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"from_account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to_account": {
|
||||
"type": "account"
|
||||
},
|
||||
"percent": {
|
||||
"type": "int"
|
||||
},
|
||||
"auto_vest": {
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"withdraw_vesting": {
|
||||
"name": "Power down",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"vesting_shares": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"transfer_to_savings": {
|
||||
"name": "Transfer to saving",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
},
|
||||
"memo": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"maxLength": 2048
|
||||
}
|
||||
}
|
||||
},
|
||||
"transfer_from_savings": {
|
||||
"name": "Transfer from saving",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"to": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
},
|
||||
"memo": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"maxLength": 2048
|
||||
},
|
||||
"request_id": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cancel_transfer_from_savings": {
|
||||
"name": "Cancel transfer from saving",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"from": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"request_id": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"convert": {
|
||||
"name": "Convert",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"requestid": {
|
||||
"type": "int"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collateralized_convert": {
|
||||
"name": "Collateralized Convert",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"requestid": {
|
||||
"type": "int"
|
||||
},
|
||||
"amount": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"account_witness_vote": {
|
||||
"name": "Witness vote",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"witness": {
|
||||
"type": "account"
|
||||
},
|
||||
"approve": {
|
||||
"type": "bool",
|
||||
"defaultValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"witness_update": {
|
||||
"name": "Witness update",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"defaultValue": "https://ecency.com"
|
||||
},
|
||||
"block_signing_key": {
|
||||
"type": "string",
|
||||
"defaultValue": "STM1111111111111111111111111111111114T1Anm"
|
||||
},
|
||||
"props": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"account_creation_fee": "1.000 HIVE",
|
||||
"maximum_block_size": 131072,
|
||||
"hbd_interest_rate": 2000
|
||||
}
|
||||
},
|
||||
"fee": {
|
||||
"type": "string",
|
||||
"defaultValue": "0.000 HIVE"
|
||||
}
|
||||
}
|
||||
},
|
||||
"witness_set_properties": {
|
||||
"name": "Witness set properties",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"props": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"account_creation_fee": "1.000 HIVE",
|
||||
"account_subsidy_budget": 10000,
|
||||
"account_subsidy_decay": 330782,
|
||||
"maximum_block_size": 65536,
|
||||
"hbd_interest_rate": 2000,
|
||||
"hbd_exchange_rate": {"base": "0.000 HBD", "quote": "0.000 HIVE"},
|
||||
"url": "https://ecency.com",
|
||||
"new_signing_key": "STM1111111111111111111111111111111114T1Anm"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"account_witness_proxy": {
|
||||
"name": "Governance proxy",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"proxy": {
|
||||
"type": "account"
|
||||
}
|
||||
}
|
||||
},
|
||||
"claim_account": {
|
||||
"name": "Claim account",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"creator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"fee": {
|
||||
"type": "amount",
|
||||
"defaultValue": "0.000 HIVE"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"account_create": {
|
||||
"name": "Create account",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"creator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"fee": {
|
||||
"type": "amount",
|
||||
"defaultValue": "3.000 HIVE"
|
||||
},
|
||||
"new_account_name": {
|
||||
"type": "account"
|
||||
},
|
||||
"memo_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"json_metadata": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"type": "object"
|
||||
},
|
||||
"active": {
|
||||
"type": "object"
|
||||
},
|
||||
"posting": {
|
||||
"type": "object"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"create_claimed_account": {
|
||||
"name": "Create account with account credits",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"creator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"new_account_name": {
|
||||
"type": "account"
|
||||
},
|
||||
"memo_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"json_metadata": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"type": "object"
|
||||
},
|
||||
"active": {
|
||||
"type": "object"
|
||||
},
|
||||
"posting": {
|
||||
"type": "object"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"vote": {
|
||||
"name": "Vote",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"voter": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"author": {
|
||||
"type": "account"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"weight": {
|
||||
"type": "int",
|
||||
"defaultValue": 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
"limit_order_create": {
|
||||
"name": "Create limit order",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"orderid": {
|
||||
"type": "int"
|
||||
},
|
||||
"amount_to_sell": {
|
||||
"type": "amount"
|
||||
},
|
||||
"min_to_receive": {
|
||||
"type": "amount"
|
||||
},
|
||||
"fill_or_kill": {
|
||||
"type": "bool"
|
||||
},
|
||||
"expiration": {
|
||||
"type": "time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"limit_order_create2": {
|
||||
"name": "Create limit order",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"orderid": {
|
||||
"type": "int"
|
||||
},
|
||||
"amount_to_sell": {
|
||||
"type": "amount"
|
||||
},
|
||||
"exchange_rate": {
|
||||
"type": "object"
|
||||
},
|
||||
"fill_or_kill": {
|
||||
"type": "bool"
|
||||
},
|
||||
"expiration": {
|
||||
"type": "time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"limit_order_cancel": {
|
||||
"name": "Cancel limit order",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"orderid": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"claim_reward_balance": {
|
||||
"name": "Redeem rewards",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"reward_hive": {
|
||||
"type": "amount"
|
||||
},
|
||||
"reward_hbd": {
|
||||
"type": "amount"
|
||||
},
|
||||
"reward_vests": {
|
||||
"type": "amount"
|
||||
}
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"name": "Post or comment",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"parent_author": {
|
||||
"type": "account",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"parent_permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"author": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"json_metadata": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"comment_options": {
|
||||
"name": "Post or comment options",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"author": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"allow_curation_rewards": {
|
||||
"type": "bool",
|
||||
"defaultValue": true
|
||||
},
|
||||
"allow_votes": {
|
||||
"type": "bool",
|
||||
"defaultValue": true
|
||||
},
|
||||
"max_accepted_payout": {
|
||||
"type": "amount",
|
||||
"defaultValue": "1000000.000 SBD"
|
||||
},
|
||||
"percent_hbd": {
|
||||
"type": "int",
|
||||
"defaultValue": 10000
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"custom_json": {
|
||||
"name": "Custom operation",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"required_auths": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
},
|
||||
"required_posting_auths": {
|
||||
"name": "posting auths",
|
||||
"type": "array",
|
||||
"defaultValue": ["__signer"]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"json": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete_comment": {
|
||||
"name": "Delete comment",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"author": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"account_update": {
|
||||
"name": "Update account (active)",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"owner": {
|
||||
"type": "object"
|
||||
},
|
||||
"active": {
|
||||
"type": "object"
|
||||
},
|
||||
"posting": {
|
||||
"type": "object"
|
||||
},
|
||||
"memo_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"json_metadata": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"account_update2": {
|
||||
"name": "Update account (posting)",
|
||||
"authority": "posting",
|
||||
"schema": {
|
||||
"account": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"json_metadata": {
|
||||
"type": "json",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"posting_json_metadata": {
|
||||
"type": "json"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"change_recovery_account": {
|
||||
"name": "Change recovery account",
|
||||
"authority": "owner",
|
||||
"schema": {
|
||||
"account_to_recover": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"new_recovery_account": {
|
||||
"type": "account"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"create_proposal": {
|
||||
"name": "Create proposal",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"creator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"receiver": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"start_date": {
|
||||
"type": "time"
|
||||
},
|
||||
"end_date": {
|
||||
"type": "time"
|
||||
},
|
||||
"daily_pay": {
|
||||
"type": "amount"
|
||||
},
|
||||
"subject": {
|
||||
"type": "string"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove_proposal": {
|
||||
"name": "Remove proposal",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"proposal_owner": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"proposal_ids": {
|
||||
"type": "array"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_proposal_votes": {
|
||||
"name": "Update proposal votes",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"voter": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"proposal_ids": {
|
||||
"type": "array"
|
||||
},
|
||||
"approve": {
|
||||
"type": "bool",
|
||||
"defaultValue": true
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_proposal": {
|
||||
"name": "Update proposal",
|
||||
"authority": "active",
|
||||
"schema": {
|
||||
"creator": {
|
||||
"type": "account",
|
||||
"defaultValue": "__signer"
|
||||
},
|
||||
"proposal_id": {
|
||||
"type": "int"
|
||||
},
|
||||
"daily_pay": {
|
||||
"type": "amount"
|
||||
},
|
||||
"subject": {
|
||||
"type": "string"
|
||||
},
|
||||
"permlink": {
|
||||
"type": "string"
|
||||
},
|
||||
"extensions": {
|
||||
"type": "array",
|
||||
"defaultValue": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user