diff --git a/constant/index.js b/constant/index.js index 6a7a056be..25103b003 100644 --- a/constant/index.js +++ b/constant/index.js @@ -75,10 +75,6 @@ export const FINAL_TOKENSALE_PERCENTAGE = 180; export const BONUS_LOCK_UNTIL_DATE = 1540267200000; -export const GAS_ESTIMATOR_BUFFER = 50000; -export const GAS_ESTIMATOR_SLOP = 3.58; -export const GAS_ESTIMATOR_INTERCEPT = 99443.87; - export const TRUST_URL = 'https://links.trustwalletapp.com/a/key_live_lfvIpVeI9TFWxPCqwU8rZnogFqhnzs4D?&event=openURL&url='; export const LIKER_LAND_APP_LINK = { diff --git a/package-lock.json b/package-lock.json index 0dc37b172..b2649d1fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3188,6 +3188,43 @@ "web3": "^1.0.0-beta.34" } }, + "@likecoin/iscn-js": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@likecoin/iscn-js/-/iscn-js-0.0.7.tgz", + "integrity": "sha512-hUnUiSuOFl/j+2UckKuEp8lKfpCmmggO4vXm8r0cY4N5ut9moApJmHq0nkcSDzbLtIbRSxWd8u9ZMp+MsiHsuA==", + "requires": { + "@likecoin/iscn-message-types": "^0.0.1", + "bignumber.js": "^9.0.1", + "buffer": "^6.0.3", + "fast-json-stable-stringify": "^2.1.0" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bignumber.js": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", + "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + } + } + }, "@likecoin/iscn-message-types": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@likecoin/iscn-message-types/-/iscn-message-types-0.0.1.tgz", @@ -8949,13 +8986,13 @@ } }, "chromedriver": { - "version": "92.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-92.0.1.tgz", - "integrity": "sha512-LptlDVCs1GgyFNVbRoHzzy948JDVzTgGiVPXjNj385qXKQP3hjAVBIgyvb/Hco0xSEW8fjwJfsm1eQRmu6t4pQ==", + "version": "94.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-94.0.0.tgz", + "integrity": "sha512-x4hK7R7iOyAhdLHJEcOyGBW/oa2kno6AqpHVLd+n3G7c2Vk9XcAXMz84XhNItqykJvTc6E3z/JRIT1eHYH//Eg==", "dev": true, "requires": { "@testim/chrome-version": "^1.0.7", - "axios": "^0.21.1", + "axios": "^0.21.2", "del": "^6.0.0", "extract-zip": "^2.0.1", "https-proxy-agent": "^5.0.0", @@ -8984,6 +9021,15 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.0" + } + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -9049,10 +9095,16 @@ "to-regex-range": "^5.0.1" } }, + "follow-redirects": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "dev": true + }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -9087,9 +9139,9 @@ } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, "https-proxy-agent": { @@ -9109,9 +9161,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -12434,9 +12486,9 @@ "dev": true }, "fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" diff --git a/package.json b/package.json index 8c7fd3759..8f8e2d258 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@cosmjs/proto-signing": "^0.25.6", "@cosmjs/stargate": "^0.25.6", "@likecoin/abi-decoder": "^3.0.0", + "@likecoin/iscn-js": "0.0.7", "@likecoin/iscn-message-types": "0.0.1", "@likecoin/nuxt-google-optimize": "^0.5.3-like.10", "@likecoin/secretd-js": "^0.2.0", @@ -97,7 +98,7 @@ "axiosist": "^0.1.0", "babel-eslint": "^10.0.1", "backpack-core": "^0.8.3", - "chromedriver": "^92.0.1", + "chromedriver": "^94.0.0", "eslint": "^5.11.1", "eslint-config-airbnb-base": "^13.1.0", "eslint-import-resolver-webpack": "^0.10.1", diff --git a/util/cosmos/iscn/constant.js b/util/cosmos/iscn/constant.js index 748c26684..337b48e77 100644 --- a/util/cosmos/iscn/constant.js +++ b/util/cosmos/iscn/constant.js @@ -1,7 +1,5 @@ import { EXTERNAL_URL } from '@/constant'; -export const ISCN_GAS = 200000; -export const ISCN_REGISTRY_NAME = 'likecoin-chain'; export const ISCN_PUBLISHERS = { matters: { license: 'matters', diff --git a/util/cosmos/iscn/iscnQueryExtension.js b/util/cosmos/iscn/iscnQueryExtension.js deleted file mode 100644 index a99f55f7e..000000000 --- a/util/cosmos/iscn/iscnQueryExtension.js +++ /dev/null @@ -1,31 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import Long from 'long'; -import { createProtobufRpcClient } from '@cosmjs/stargate'; -import { - QueryClientImpl, -} from '@likecoin/iscn-message-types/dist/iscn/query'; - -export default function setupISCNExtension(base) { - const rpc = createProtobufRpcClient(base); - const queryService = new QueryClientImpl(rpc); - return { - iscn: { - recordsById: (iscnId, fromVersion = 0, toVersion = 0) => queryService.RecordsById({ - iscnId, - fromVersion: Long.fromNumber(fromVersion, true), - toVersion: Long.fromNumber(toVersion, true), - }), - recordsByFingerprint: (fingerprint, fromSequence = 0) => queryService.RecordsByFingerprint({ - fingerprint, - fromSequence: Long.fromNumber(fromSequence, true), - }), - recordsByOwner: (owner, fromSequence = 0) => queryService.RecordsByOwner({ - owner, fromSequence: Long.fromNumber(fromSequence, true), - }), - params: () => queryService.Params({}), - getCid: cid => queryService.GetCid({ cid }), - hasCid: cid => queryService.HasCid({ cid }), - getCidSize: cid => queryService.GetCidSize({ cid }), - }, - }; -} diff --git a/util/cosmos/iscn/query.js b/util/cosmos/iscn/query.js index c7f418d99..8ebae4626 100644 --- a/util/cosmos/iscn/query.js +++ b/util/cosmos/iscn/query.js @@ -1,103 +1,18 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import { Tendermint34Client } from '@cosmjs/tendermint-rpc'; -// eslint-disable-next-line import/no-extraneous-dependencies -import { decodeTxRaw } from '@cosmjs/proto-signing'; -import { QueryClient, StargateClient } from '@cosmjs/stargate'; -import { MsgCreateIscnRecord } from '@likecoin/iscn-message-types/dist/iscn/tx'; -import BigNumber from 'bignumber.js'; +import { StargateClient } from '@cosmjs/stargate'; +import { parseISCNTxInfoFromIndexedTx } from '@likecoin/iscn-js'; -import setupISCNExtension from './iscnQueryExtension'; import { timeout } from '@/util/misc'; import { EXTERNAL_URL } from '@/constant'; const ISCN_RPC_URL = `${EXTERNAL_URL}/api/cosmos/rpc`; -let queryClient; let stargateClient; -async function initQueryClient() { - const tendermintClient = await Tendermint34Client.connect(ISCN_RPC_URL); - const client = QueryClient.withExtensions( - tendermintClient, - setupISCNExtension, - ); - return client; -} - -export async function getQueryClient() { - if (!queryClient) queryClient = await initQueryClient(); - return queryClient; -} - export async function getApiClient() { if (!stargateClient) stargateClient = await StargateClient.connect(ISCN_RPC_URL); return stargateClient; } -function parseISCNRecordFields(record) { - const { - stakeholders, - contentMetadata, - } = record; - return { - ...record, - stakeholders: stakeholders.map((s) => { - if (s) { - const sString = Buffer.from(s).toString('utf-8'); - if (sString) return JSON.parse(sString); - } - return s; - }), - contentMetadata: JSON.parse(Buffer.from(contentMetadata).toString('utf-8')), - }; -} - -export function parseISCNTxInfoFromTxSuccess(tx) { - const { transactionHash } = tx; - let iscnId; - if (tx.rawLog) { - const [log] = JSON.parse(tx.rawLog); - if (log) { - const ev = log.events.find(e => e.type === 'iscn_record'); - if (ev) iscnId = ev.attributes[0].value; - } - } - return { - txHash: transactionHash, - iscnId, - }; -} - -export function parseISCNTxInfoFromIndexedTx(tx) { - const { tx: txBytes, rawLog } = tx; - const decodedTx = decodeTxRaw(txBytes); - const messages = decodedTx.body.messages.map((m) => { - if (m.typeUrl === '/likechain.iscn.MsgCreateIscnRecord') { - const msg = MsgCreateIscnRecord.decode(m.value); - if (msg.record) { - msg.record = parseISCNRecordFields(msg.record); - } - return { - ...m, - value: msg, - }; - } - return null; - }); - - return { - ...tx, - logs: JSON.parse(rawLog), - tx: { - ...decodedTx, - body: { - ...decodedTx.body, - messages: messages.filter(m => m), - }, - }, - }; -} - export async function getISCNTransferInfo(txHash, opt) { const apiClient = await getApiClient(); const { blocking } = opt; @@ -206,19 +121,3 @@ export async function getISCNTransactionCompleted(txHash) { isFailed: (code && code !== '0') || !success, }; } - -export async function queryFeePerByte() { - queryClient = await getQueryClient(); - const res = await queryClient.iscn.params(); - if (res && res.params && res.params.feePerByte) { - const { - denom, - amount, - } = res.params.feePerByte; - return { - denom, - amount: new BigNumber(amount).shiftedBy(-18).toFixed(), - }; - } - return 0; -} diff --git a/util/cosmos/iscn/sign.js b/util/cosmos/iscn/sign.js index 797be1d23..908a408d9 100644 --- a/util/cosmos/iscn/sign.js +++ b/util/cosmos/iscn/sign.js @@ -1,32 +1,28 @@ // eslint-disable-next-line import/no-extraneous-dependencies import BigNumber from 'bignumber.js'; -import { Registry } from '@cosmjs/proto-signing'; -import { MsgCreateIscnRecord } from '@likecoin/iscn-message-types/dist/iscn/tx'; -import { - defaultRegistryTypes, - assertIsBroadcastTxSuccess, - SigningStargateClient, -} from '@cosmjs/stargate'; -import jsonStringify from 'fast-json-stable-stringify'; +import { ISCNSigningClient } from '@likecoin/iscn-js'; -import { - ISCN_RPC_URL, ISCN_PUBLISHERS, ISCN_LICENSES, - ISCN_REGISTRY_NAME, -} from './constant'; -import { - EXTERNAL_URL, - GAS_ESTIMATOR_BUFFER, - GAS_ESTIMATOR_SLOP, - GAS_ESTIMATOR_INTERCEPT, - COSMOS_DENOM, -} from '../../../constant'; -import { queryFeePerByte } from './query'; -import { DEFAULT_GAS_PRICE_NUMBER } from '../../CosmosHelper'; +import { ISCN_RPC_URL, ISCN_PUBLISHERS, ISCN_LICENSES } from './constant'; +import { EXTERNAL_URL } from '../../../constant'; -const registry = new Registry([ - ...defaultRegistryTypes, - ['/likechain.iscn.MsgCreateIscnRecord', MsgCreateIscnRecord], // Replace with your own type URL and Msg class -]); +let isConnected; +let iscnClient; + +function getISCNEstimationClient() { + if (!iscnClient) { + iscnClient = new ISCNSigningClient(ISCN_RPC_URL); + } + return iscnClient; +} + +async function getISCNSigningClient(signer) { + if (!isConnected) { + const client = getISCNEstimationClient(); + await client.connectWithSigner(ISCN_RPC_URL, signer); + isConnected = true; + } + return iscnClient; +} function getPublisherISCNPayload(user, { publisher, license }) { const { @@ -43,7 +39,7 @@ function getPublisherISCNPayload(user, { publisher, license }) { name, license: mattersLicense, } = ISCN_PUBLISHERS.matters; - stakeholders.push(Buffer.from(JSON.stringify({ + stakeholders.push({ entity: { id, name, @@ -51,7 +47,7 @@ function getPublisherISCNPayload(user, { publisher, license }) { }, rewardProportion: 0, contributionType: 'http://schema.org/publisher', - }), 'utf8')); + }); usageInfo = `ipfs://${ISCN_LICENSES[mattersLicense]['/']}`; } // eslint-disable-next-line no-fallthrough @@ -60,14 +56,14 @@ function getPublisherISCNPayload(user, { publisher, license }) { default: if (!usageInfo) usageInfo = `ipfs://${ISCN_LICENSES.default['/']}`; } - stakeholders.unshift(Buffer.from(JSON.stringify({ + stakeholders.unshift({ entity: { id: `${EXTERNAL_URL}/${userId}`, name: displayName, }, rewardProportion: 1, contributionType: 'http://schema.org/author', - }), 'utf8')); + }); break; } } @@ -77,7 +73,7 @@ function getPublisherISCNPayload(user, { publisher, license }) { }; } -function formatISCNPayload(payload, version = 1) { +function preformatISCNPayload(payload) { const { userId, displayName, @@ -112,143 +108,34 @@ function formatISCNPayload(payload, version = 1) { const contentFingerprints = []; if (fingerprint) contentFingerprints.push(`ipfs://${fingerprint}`); - const contentMetadata = { - '@context': 'http://schema.org/', - '@type': actualType, + + const preformatedPayload = { name, description, - version, url, - keywords: tags.join(','), + keywords: tags, + type: actualType, usageInfo, - }; - return { recordNotes: '', contentFingerprints, stakeholders, - contentMetadata: Buffer.from(JSON.stringify(contentMetadata), 'utf8'), - }; -} - -export async function estimateISCNTxFee(tx, { - version = 1, -} = {}) { - const record = formatISCNPayload(tx); - const feePerByte = await queryFeePerByte(); - const feePerByteAmount = feePerByte ? parseInt(feePerByte.amount, 10) : 1; - const { - recordNotes, - contentFingerprints, - stakeholders, - contentMetadata, - } = record; - const now = new Date(); - const obj = { - '@context': { - '@vocab': 'http://iscn.io/', - recordParentIPLD: { - '@container': '@index', - }, - stakeholders: { - '@context': { - '@vocab': 'http://schema.org/', - entity: 'http://iscn.io/entity', - rewardProportion: 'http://iscn.io/rewardProportion', - contributionType: 'http://iscn.io/contributionType', - footprint: 'http://iscn.io/footprint', - }, - }, - contentMetadata: { - '@context': null, - }, - }, - '@type': 'Record', - '@id': `iscn://${ISCN_REGISTRY_NAME}/btC7CJvMm4WLj9Tau9LAPTfGK7sfymTJW7ORcFdruCU/1`, - recordTimestamp: now.toISOString(), - recordVersion: version, - recordNotes, - contentFingerprints, - recordParentIPLD: {}, }; - if (version > 1) { - obj.recordParentIPLD = { - '/': 'bahuaierav3bfvm4ytx7gvn4yqeu4piiocuvtvdpyyb5f6moxniwemae4tjyq', - }; - } - - const byteSize = Buffer.from(jsonStringify(obj), 'utf-8').length - + stakeholders.reduce((acc, s) => acc + s.length, 0) - + contentMetadata.length; - const iscnFee = byteSize * feePerByteAmount; - - return { iscnFee }; + return preformatedPayload; } -export async function estimateISCNTxGas(tx) { - const record = await formatISCNPayload(tx); - const msg = { - type: Buffer.from('likecoin-chain/MsgCreateIscnRecord', 'utf-8'), - value: { - from: Buffer.from(tx.cosmosWallet, 'utf-8'), - record, - }, - }; - const value = { - msg: [msg], - fee: { - amount: [ - { - denom: 'nanolike', - amount: '200000', // temp number here for estimation - }, - ], - gas: '200000', // temp number here for estimation - }, - }; - const obj = { - type: 'cosmos-sdk/StdTx', - value: Buffer.from(jsonStringify(value), 'utf-8'), - }; - const interceptWithBuffer = BigNumber(GAS_ESTIMATOR_INTERCEPT).plus(GAS_ESTIMATOR_BUFFER); - const txBytes = Buffer.from(jsonStringify(obj), 'utf-8'); - const byteSize = BigNumber(txBytes.length); - const gasUsedEstimation = byteSize.multipliedBy(GAS_ESTIMATOR_SLOP).plus(interceptWithBuffer); - return { - gasFee: { - amount: [{ - amount: gasUsedEstimation.multipliedBy(DEFAULT_GAS_PRICE_NUMBER).toFixed(0, 0), - denom: COSMOS_DENOM, - }], - gas: gasUsedEstimation.toFixed(0, 0), - }, - }; -} - -export async function calculateISCNTotalFee(tx, version = 1) { - const { gasFee } = await estimateISCNTxGas(tx); - const { iscnFee } = await estimateISCNTxFee(tx, { version }); - const totalFee = new BigNumber(iscnFee).plus(gasFee.amount[0].amount).shiftedBy(-9); - const ISCNTotalFee = totalFee.toFixed(2); +export async function calculateISCNTotalFee(tx) { + const payload = preformatISCNPayload(tx); + const client = await getISCNEstimationClient(); + const { gas, iscnFee } = await client.esimateISCNTxGasAndFee(payload); + const ISCNFeeAmount = iscnFee.amount; + const gasFeeAmount = gas.fee.amount[0].amount; + const ISCNTotalFee = new BigNumber(ISCNFeeAmount).plus(gasFeeAmount).shiftedBy(-9).toFixed(2); return { ISCNTotalFee }; } export async function signISCNTx(tx, signer, address, memo) { - const record = formatISCNPayload(tx); - const client = await SigningStargateClient.connectWithSigner( - ISCN_RPC_URL, - signer, - { registry }, - ); - - const message = { - typeUrl: '/likechain.iscn.MsgCreateIscnRecord', - value: { - from: address, - record, - }, - }; - const { gasFee } = await estimateISCNTxGas(tx); - const response = await client.signAndBroadcast(address, [message], gasFee, memo); - assertIsBroadcastTxSuccess(response); - return response; + const payload = preformatISCNPayload(tx); + const client = await getISCNSigningClient(signer); + const res = await client.createISCNRecord(address, payload, { memo }); + return res; }