bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
402 lines • 17.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bson_1 = require("bson");
const chai_1 = require("chai");
const sinon = __importStar(require("sinon"));
const stream_1 = require("stream");
const web3_1 = __importDefault(require("web3"));
const cache_1 = require("../../../src/models/cache");
const wallet_1 = require("../../../src/models/wallet");
const walletAddress_1 = require("../../../src/models/walletAddress");
const csp_1 = require("../../../src/modules/matic/api/csp");
const block_1 = require("../../../src/providers/chain-state/evm/models/block");
const transaction_1 = require("../../../src/providers/chain-state/evm/models/transaction");
const integration_1 = require("../../helpers/integration");
describe('Polygon/MATIC API', function () {
const chain = 'MATIC';
const network = 'regtest';
const suite = this;
this.timeout(30000);
before(integration_1.intBeforeHelper);
after(async () => (0, integration_1.intAfterHelper)(suite));
it('should return undefined for garbage data', () => {
const data = 'garbage';
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.be.undefined;
});
it('should be able to classify ERC20 data', () => {
const data = '0x095ea7b300000000000000000000000052de8d3febd3a06d3c627f59d56e6892b80dcf1200000000000000000000000000000000000000000000000000000000000f4240';
transaction_1.EVMTransactionStorage.abiDecode(data);
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.exist;
(0, chai_1.expect)(decoded.type).to.eq('ERC20');
});
it('should be able to classify ERC721 data', () => {
const data = '0xa22cb465000000000000000000000000efc70a1b18c432bdc64b596838b4d138f6bc6cad0000000000000000000000000000000000000000000000000000000000000001';
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.exist;
(0, chai_1.expect)(decoded.type).to.eq('ERC721');
});
it('should be able to classify Invoice data', () => {
const data = '0xb6b4af0500000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000033ec500800000000000000000000000000000000000000000000000000000016e00f7b3d3c72c929edaf203cfabf7a0513cb8cee277a84ec3fd56bcf3f396b6d665c8abe6c4432f916bacafc94982b45050513de2ee5544aa855d9b5b60e8c1c94e71ffca000000000000000000000000000000000000000000000000000000000000001cfd9150848849c7aff74939535afe5e56dcac5f2f553467ae0e9181d14c0e49c9799433220e288e282376b86aae1bc1d683af4708b38999d59b5d65ff29a85705000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.exist;
(0, chai_1.expect)(decoded.type).to.eq('INVOICE');
});
it('should handle multiple decodes', () => {
const data = '0x095ea7b300000000000000000000000052de8d3febd3a06d3c627f59d56e6892b80dcf1200000000000000000000000000000000000000000000000000000000000f4240';
transaction_1.EVMTransactionStorage.abiDecode(data);
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.exist;
(0, chai_1.expect)(decoded.type).to.eq('ERC20');
const data2 = '0xa22cb465000000000000000000000000efc70a1b18c432bdc64b596838b4d138f6bc6cad0000000000000000000000000000000000000000000000000000000000000001';
transaction_1.EVMTransactionStorage.abiDecode(data);
const decoded2 = transaction_1.EVMTransactionStorage.abiDecode(data2);
(0, chai_1.expect)(decoded2).to.exist;
(0, chai_1.expect)(decoded2.type).to.eq('ERC721');
});
it('should not crash when called with almost correct data', () => {
const data = '0xa9059cbb0000000000000000000000000797350000000000000000000000000000000000000000000005150ac4c39a6f3f0000';
const decoded = transaction_1.EVMTransactionStorage.abiDecode(data);
(0, chai_1.expect)(decoded).to.be.undefined;
});
it('should be able to get the fees', async () => {
const chain = 'MATIC';
const network = 'testnet';
let target = 1;
while (target <= 4) {
const cacheKey = `getFee-${chain}-${network}-${target}`;
const fee = await csp_1.MATIC.getFee({ chain, network, target });
(0, chai_1.expect)(fee).to.exist;
const cached = await cache_1.CacheStorage.getGlobal(cacheKey);
(0, chai_1.expect)(fee).to.deep.eq(cached);
target++;
}
});
it('should estimate fees by most recent transactions', async () => {
const chain = 'MATIC';
const network = 'testnet';
const txs = new Array(4000).fill({}).map(_ => {
return {
chain,
network,
blockHeight: 1,
gasPrice: 10 * 1e9
};
});
await cache_1.CacheStorage.collection.remove({});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
const estimates = await Promise.all([1, 2, 3, 4].map(target => csp_1.MATIC.getFee({ network, target })));
for (const estimate of estimates) {
(0, chai_1.expect)(estimate.feerate).to.be.gt(0);
(0, chai_1.expect)(estimate.feerate).to.be.eq(10000000000);
}
});
it('should return cached fee for a minute', async () => {
const chain = 'MATIC';
const network = 'testnet';
const txs = new Array(4000).fill({}).map(_ => {
return {
chain,
network,
blockHeight: 1,
gasPrice: 10 * 1e9
};
});
await cache_1.CacheStorage.collection.remove({});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
let estimates = await Promise.all([1, 2, 3, 4].map(target => csp_1.MATIC.getFee({ network, target })));
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
estimates = await Promise.all([1, 2, 3, 4].map(target => csp_1.MATIC.getFee({ network, target })));
for (const estimate of estimates) {
(0, chai_1.expect)(estimate.feerate).to.be.gt(0);
(0, chai_1.expect)(estimate.feerate).to.be.eq(10000000000);
}
});
it('should be able to get address token balance', async () => {
const sandbox = sinon.createSandbox();
const address = '0xb8fd14fb0e0848cb931c1e54a73486c4b968be3d';
const token = {
name: 'Test Token',
decimals: 10,
symbol: 'TST'
};
const tokenStub = {
methods: {
name: () => ({ call: sandbox.stub().resolves(token.name) }),
decimals: () => ({ call: sandbox.stub().resolves(token.decimals) }),
symbol: () => ({ call: sandbox.stub().resolves(token.symbol) }),
balanceOf: () => ({ call: sandbox.stub().resolves(0) })
}
};
sandbox.stub(csp_1.MATIC, 'erc20For').resolves(tokenStub);
const balance = await csp_1.MATIC.getBalanceForAddress({ chain, network, address, args: { tokenAddress: address } });
(0, chai_1.expect)(balance).to.deep.eq({ confirmed: 0, unconfirmed: 0, balance: 0 });
sandbox.restore();
});
it('should be able to get address MATIC balance', async () => {
const address = '0xb8fd14fb0e0848cb931c1e54a73486c4b968be3d';
const balance = await csp_1.MATIC.getBalanceForAddress({ chain, network, address, args: {} });
(0, chai_1.expect)(balance).to.deep.eq({ confirmed: 0, unconfirmed: 0, balance: 0 });
});
it('should stream MATIC transactions for address', async () => {
const address = '0xb8fd14fb0e0848cb931c1e54a73486c4b968be3d';
const txCount = 100;
const txs = new Array(txCount).fill({}).map(() => {
return {
chain,
network,
blockHeight: 1,
gasPrice: 10 * 1e9,
data: Buffer.from(''),
from: address
};
});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
const res = new stream_1.Transform({
transform: (data, _, cb) => cb(null, data)
});
res.type = () => res;
const req = new stream_1.Transform({
transform: (_data, _, cb) => cb(null)
});
await csp_1.MATIC.streamAddressTransactions({ chain, network, address, res, req, args: {} });
let counter = 0;
await new Promise(r => {
res
.on('data', () => counter++)
.on('end', r);
});
const commaCount = txCount - 1;
const bracketCount = 2;
const expected = txCount + commaCount + bracketCount;
(0, chai_1.expect)(counter).to.eq(expected);
});
it('should stream MATIC transactions for block', async () => {
const txCount = 100;
const txs = new Array(txCount).fill({}).map(() => {
return {
chain,
network,
blockHeight: 1,
gasPrice: 10 * 1e9,
data: Buffer.from('')
};
});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
const res = new stream_1.Transform({
transform: (data, _, cb) => {
cb(null, data);
}
});
res.type = () => res;
const req = new stream_1.Transform({
transform: (_data, _, cb) => {
cb(null);
}
});
await csp_1.MATIC.streamTransactions({ chain, network, res, req, args: { blockHeight: 1 } });
let counter = 0;
await new Promise(r => {
res
.on('data', () => {
counter++;
})
.on('end', () => {
r();
});
});
const commaCount = txCount - 1;
const bracketCount = 2;
const expected = txCount + commaCount + bracketCount;
(0, chai_1.expect)(counter).to.eq(expected);
});
it('should stream MATIC transactions for blockHash', async () => {
const txCount = 100;
const txs = new Array(txCount).fill({}).map(() => {
return {
chain,
network,
blockHash: '12345',
gasPrice: 10 * 1e9,
data: Buffer.from('')
};
});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
const res = new stream_1.Transform({
transform: (data, _, cb) => {
cb(null, data);
}
});
res.type = () => res;
const req = new stream_1.Transform({
transform: (_data, _, cb) => {
cb(null);
}
});
await csp_1.MATIC.streamTransactions({ chain, network, res, req, args: { blockHash: '12345' } });
let counter = 0;
await new Promise(r => {
res
.on('data', () => {
counter++;
})
.on('end', () => {
r();
});
});
const commaCount = txCount - 1;
const bracketCount = 2;
const expected = txCount + commaCount + bracketCount;
(0, chai_1.expect)(counter).to.eq(expected);
});
describe('#streamWalletTransactions', () => {
let sandbox = sinon.createSandbox();
let chain = 'MATIC';
let network = 'mainnet';
let address = '0x1Eee23160Db790ee48Fd39871A64b13e76Fc2C3C';
let wallet = {
chain,
network,
pubKey: '',
name: 'this-name',
singleAddress: false,
path: 'm/0/0'
};
let web3 = new web3_1.default();
before(async () => {
const res = await wallet_1.WalletStorage.collection.findOneAndUpdate({ name: wallet.name }, { $set: wallet }, { returnOriginal: false, upsert: true });
wallet = res.value;
await walletAddress_1.WalletAddressStorage.collection.updateOne({ network, address }, { $set: { chain, network, wallet: wallet._id, processed: true, address } }, { upsert: true });
sandbox.stub(csp_1.MATIC, 'getWeb3').resolves({ web3 });
});
afterEach(async () => {
await block_1.EVMBlockStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
});
after(async () => {
sandbox.restore();
});
it('should stream wallet\'s valid MATIC transactions', async () => await streamWalletTransactionsTest(chain, network));
it('should stream wallet\'s valid & invalid MATIC transactions', async () => await streamWalletTransactionsTest(chain, network, true));
});
});
const streamWalletTransactionsTest = async (chain, network, includeInvalidTxs = false) => {
const sandbox = sinon.createSandbox();
// Constants
const address = '0x7F17aF79AABC4A297A58D389ab5905fEd4Ec9502';
const objectId = bson_1.ObjectId.createFromHexString('60f9abed0e32086bf9903bb5');
const wallet = {
_id: objectId,
chain,
network,
name: 'Ganache',
pubKey: '0x029ec2ebdebe6966259cf3c6f35c4f126b82fe072bf9d0e81dad375f1d6d2d9054',
path: 'm/44\'/60\'/0\'/0/0',
singleAddress: true
};
const txCount = 100;
// Valid Transactions
const txs = new Array(txCount).fill({}).map(() => {
return {
chain,
network,
blockHeight: 1,
gasPrice: 10 * 1e9,
data: Buffer.from(''),
from: address
};
});
// Invalid Transactions
for (let i = 0; i < txCount; i++) {
txs.push({
...txs[0],
blockHeight: -3
});
}
// Add wallet object ID to transactions
for (const tx of txs) {
tx.wallets = [objectId];
}
// Stubs
sandbox.stub(csp_1.MATIC, 'getWalletAddresses').resolves([address]);
sandbox.stub(csp_1.MATIC, 'isP2p').returns(true);
// Test
await transaction_1.EVMTransactionStorage.collection.deleteMany({});
await transaction_1.EVMTransactionStorage.collection.insertMany(txs);
let counter = 0;
const req = new stream_1.Writable({
write: function (data, _, cb) {
data && counter++;
cb();
}
});
const res = new stream_1.Writable({
write: function (data, _, cb) {
data && counter++;
cb();
}
});
res.type = () => res;
const err = await new Promise(r => {
res
.on('error', r)
.on('finish', r);
csp_1.MATIC.streamWalletTransactions({
chain,
network,
wallet,
req,
res,
args: {
includeInvalidTxs
}
})
.catch(e => r(e));
});
(0, chai_1.expect)(err).to.not.exist;
(0, chai_1.expect)(counter).to.eq(includeInvalidTxs ? txCount * 2 : txCount);
sandbox.restore();
};
//# sourceMappingURL=csp.spec.js.map