Skip to content

Commit

Permalink
add store data MATIC support
Browse files Browse the repository at this point in the history
add mint NFT with IPFS storage
  • Loading branch information
Samuel Sramko committed Aug 17, 2021
1 parent 4881be8 commit f0b9d7c
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 271 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tatumio/tatum",
"version": "1.19.17",
"version": "1.19.18",
"description": "Tatum API client allows browsers and Node.js clients to interact with Tatum API.",
"main": "dist/src/index.js",
"repository": "https://github.com/tatumio/tatum-js",
Expand Down Expand Up @@ -59,6 +59,7 @@
"ed25519-hd-key": "^1.1.2",
"elliptic": "^6.5.4",
"ethereumjs-wallet": "^1.0.0",
"form-data": "^4.0.0",
"hdkey": "^2.0.1",
"neon": "^2.0.0",
"reflect-metadata": "^0.1.13",
Expand Down
23 changes: 13 additions & 10 deletions src/connector/tatum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import axiosRetry, {isNetworkOrIdempotentRequestError} from 'axios-retry';
import {plainToClass} from 'class-transformer';
import {ClassType} from 'class-transformer/ClassTransformer';
import {validateOrReject} from 'class-validator';
import FormData from 'form-data';
import http from 'http';
import https from 'https';
import {TATUM_API_URL, TATUM_RETRIES, TATUM_RETRY_DELAY} from '../constants';
Expand All @@ -29,17 +30,19 @@ export const get = async <T>(url: string): Promise<T> => {
}

// eslint-disable-next-line @typescript-eslint/ban-types
export const post = async <T extends object, U, V>(url: string, body?: U, classType?: ClassType<T>): Promise<V> => {
await validateBody(body, classType)
const { data } = await axios.post(`${baseUrl()}${url}`, body, headers())
return data
}
export const post = async <T extends object, U, V>(url: string, body?: U, classType?: ClassType<T>, header?: any): Promise<V> => {
await validateBody(body, classType);
const {data} = await axios.post(`${baseUrl()}${url}`, body, headers());
return data;
};

// eslint-disable-next-line @typescript-eslint/ban-types
export const upload = async <T extends object, U, V>(url: string, body?: U, classType?: ClassType<T>): Promise<V> => {
await validateBody(body, classType)
const { data } = await axios.post(`${baseUrl()}${url}`, body, { headers: { 'x-api-key': process.env.TATUM_API_KEY, 'content-type': 'multipart/form-data'} })
return data
}
export const postMultiForm = async (url: string, body: FormData): Promise<any> => {
const h = headers();
h.headers = {...h.headers, ...body.getHeaders()};
const {data} = await axios.post(`${baseUrl()}${url}`, body, h);
return data;
};

// eslint-disable-next-line @typescript-eslint/ban-types
export const put = async <T extends object, U, V>(url: string, body?: U, classType?: ClassType<T>): Promise<V> => {
Expand Down
20 changes: 11 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'reflect-metadata'
export * from './wallet'
export * from './model'
export * from './ledger'
export * from './tatum'
export * from './security'
export * from './offchain'
export * from './blockchain'
export * from './transaction'
import 'reflect-metadata';

export * from './wallet';
export * from './model';
export * from './ledger';
export * from './tatum';
export * from './security';
export * from './storage';
export * from './offchain';
export * from './blockchain';
export * from './transaction';
export * from './record'
export * from './constants'
export * from './nft'
Expand Down
6 changes: 3 additions & 3 deletions src/model/request/CreateRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export class CreateRecord {

@IsNotEmpty()
@ValidateIf(o => o.chain === Currency.QUORUM)
@Length(42, 42)
@Length(42, 43)
public from: string;

@ValidateIf(o => o.chain === Currency.QUORUM)
@Length(42, 42)
@ValidateIf(o => o.chain === Currency.QUORUM || o.signatureId)
@Length(42, 43)
public to?: string;

@ValidateIf(o => o.chain === Currency.FABRIC)
Expand Down
323 changes: 174 additions & 149 deletions src/nft/nft.spec.ts

Large diffs are not rendered by default.

212 changes: 140 additions & 72 deletions src/nft/nft.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ipfs';
10 changes: 10 additions & 0 deletions src/storage/ipfs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {readFileSync} from 'fs';
import {ipfsUpload} from './ipfs';

describe('IPFS storage', () => {

jest.setTimeout(99999);
it('should store IPFS record', async () => {
await ipfsUpload(readFileSync('/Users/ssramko/Downloads/logo_tatum.png'), 'logo_tatum.png');
});
});
18 changes: 14 additions & 4 deletions src/storage/ipfs.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { get, upload, httpDelete } from '../connector/tatum'
import FormData from 'form-data';
import {get, httpDelete, postMultiForm} from '../connector/tatum';

/**
* Stores file on the IPFS. This operation is available only for paid plans.
* For more details, see <a href="https://tatum.io/apidoc#operation/StoreIPFS" target="_blank">Tatum API documentation</a>
*/

export const ipfsUpload = async (file: Buffer): Promise<{ ipfsHash: string }> => upload(`/v3/ipfs`, { file: Uint8Array.from(file) },)
/**
* Upload file to the IPFS storage.
* @param file Data buffer of the file
* @param fileName Name of the file to upload.
*/
export const ipfsUpload = async (file: Buffer, fileName: string): Promise<{ ipfsHash: string }> => {
const body = new FormData();
body.append('file', file, fileName);
return await postMultiForm('/v3/ipfs', body);
};
/**
* Gets data from the IPFS. Every 100 kB of data costs 1 additional credit.
* For more details, see <a href="https://tatum.io/apidoc#operation/StoreIPFS" target="_blank">Tatum API documentation</a>
*/
export const ipfsGet = async (id: string): Promise<any> => get(`/v3/ipfs/${id}`)
export const ipfsGet = async (id: string): Promise<any> => get(`/v3/ipfs/${id}`);
/**
* Unpin the data from the IPFS. After this operation, credits won't be charged for a storage, but file will keep exists on the IPFS.
* For more details, see <a href="https://tatum.io/apidoc#operation/StoreIPFS" target="_blank">Tatum API documentation</a>
*/
export const ipfsDelete = async (id: string): Promise<any> => httpDelete(`/v3/ipfs/${id}`)
export const ipfsDelete = async (id: string): Promise<void> => httpDelete(`/v3/ipfs/${id}`);
2 changes: 2 additions & 0 deletions src/transaction/abstraction.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {validateBody} from '../connector/tatum';
import {CreateRecord, Currency, TransferBscBep20, TransferCeloOrCeloErc20Token, TransferEthErc20, TransferTron} from '../model';
import {sendBscOrBep20Transaction, sendBscStoreDataTransaction} from './bsc';
import {sendCeloOrcUsdTransaction, sendCeloStoreDataSignedTransaction} from './celo';
Expand All @@ -14,6 +15,7 @@ import {sendXdcStoreDataTransaction} from './xdc';
* @param provider Optional provider to use for broadcasting signed tx to the blockchain.
*/
export const storeData = async (testnet: boolean, body: CreateRecord, provider?: string) => {
await validateBody(body, CreateRecord);
switch (body.chain) {
case Currency.ETH:
return await sendStoreDataTransaction(body, provider);
Expand Down
9 changes: 5 additions & 4 deletions src/transaction/one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ export const prepareOneSignedTransaction = async (testnet: boolean, body: OneTra
* @returns transaction data to be broadcast to blockchain.
*/
export const prepareOneStoreDataTransaction = async (testnet: boolean, body: CreateRecord, provider?: string) => {
await validateBody(body, CreateRecord)
const client = await prepareOneClient(testnet, provider, body.fromPrivateKey)
return prepareGeneralTx(client, testnet, body.fromPrivateKey, body.signatureId, body.to, undefined, body.nonce, body.data,
body.ethFee?.gasLimit, body.ethFee?.gasPrice)
await validateBody(body, CreateRecord);
const client = await prepareOneClient(testnet, provider, body.fromPrivateKey);
const hexData = client.utils.isHex(body.data) ? client.utils.stringToHex(body.data) : client.utils.toHex(body.data);
return prepareGeneralTx(client, testnet, body.fromPrivateKey, body.signatureId, body.to || client.eth.accounts.wallet[0].address, undefined, body.nonce, hexData,
body.ethFee?.gasLimit, body.ethFee?.gasPrice);
}

/**
Expand Down
40 changes: 26 additions & 14 deletions src/transaction/polygon.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Web3 from 'web3'
import {Currency, DeployErc20, TransferCustomErc20, TransferEthErc20} from '../model'
import Web3 from 'web3';
import {CreateRecord, Currency, DeployErc20, TransferCustomErc20, TransferEthErc20} from '../model';
import {
polygonGetGasPriceInWei,
preparePolygonBurnErc721SignedTransaction,
Expand All @@ -9,10 +9,11 @@ import {
preparePolygonMintMultipleErc721SignedTransaction,
preparePolygonSignedTransaction,
preparePolygonSmartContractWriteMethodInvocation,
preparePolygonStoreDataTransaction,
preparePolygonTransferErc20SignedTransaction,
preparePolygonTransferErc721SignedTransaction,
sendPolygonSmartContractReadMethodInvocationTransaction
} from './polygon'
} from './polygon';

describe('MATIC transactions', () => {
jest.setTimeout(19999)
Expand All @@ -28,20 +29,31 @@ describe('MATIC transactions', () => {
describe('MATIC common transactions', () => {
it('should test valid transaction MATIC data', async () => {
const body = new TransferEthErc20()
body.fromPrivateKey = '0x1a4344e55c562db08700dd32e52e62e7c40b1ef5e27c6ddd969de9891a899b29'
body.amount = '0.0001'
body.currency = Currency.MATIC
body.to = '0x811DfbFF13ADFBC3Cf653dCc373C03616D3471c9'
const txData = await preparePolygonSignedTransaction(true, body, 'https://matic-mumbai.chainstacklabs.com/')
expect(txData).toContain('0x')

console.log(await broadcast(txData))
body.fromPrivateKey = '0x1a4344e55c562db08700dd32e52e62e7c40b1ef5e27c6ddd969de9891a899b29';
body.amount = '0.0001';
body.currency = Currency.MATIC;
body.to = '0x811DfbFF13ADFBC3Cf653dCc373C03616D3471c9';
const txData = await preparePolygonSignedTransaction(true, body, 'https://matic-mumbai.chainstacklabs.com/');
expect(txData).toContain('0x');

console.log(await broadcast(txData));
})

it('should test valid transaction MATIC store data', async () => {
const body = new CreateRecord();
body.fromPrivateKey = '0x1a4344e55c562db08700dd32e52e62e7c40b1ef5e27c6ddd969de9891a899b29';
body.data = 'Hello hi';
body.chain = Currency.MATIC;
const txData = await preparePolygonStoreDataTransaction(true, body, 'https://matic-mumbai.chainstacklabs.com/');
expect(txData).toContain('0x');

console.log(await broadcast(txData));
});

it('should test ethGetGasPriceInWei', async () => {
const gasPrice = await polygonGetGasPriceInWei()
expect(gasPrice).not.toBeNull()
})
const gasPrice = await polygonGetGasPriceInWei();
expect(gasPrice).not.toBeNull();
});

it('should test read smart contract method invocation', async () => {
const result = await sendPolygonSmartContractReadMethodInvocationTransaction(true, {
Expand Down
11 changes: 6 additions & 5 deletions src/transaction/polygon.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {BigNumber} from 'bignumber.js';
import Web3 from 'web3';
import {TransactionConfig} from 'web3-core';
import {toWei} from 'web3-utils';
import {isHex, stringToHex, toHex, toWei} from 'web3-utils';
import {polygonBroadcast} from '../blockchain';
import {axios, validateBody} from '../connector/tatum';
import {CONTRACT_ADDRESSES, CONTRACT_DECIMALS, TATUM_API_URL, TRANSFER_METHOD_ABI} from '../constants';
Expand Down Expand Up @@ -179,10 +179,11 @@ export const preparePolygonSignedTransaction = async (testnet: boolean, body: Tr
* @returns transaction data to be broadcast to blockchain.
*/
export const preparePolygonStoreDataTransaction = async (testnet: boolean, body: CreateRecord, provider?: string) => {
await validateBody(body, CreateRecord)
const client = await preparePolygonClient(testnet, provider, body.fromPrivateKey)
return prepareGeneralTx(client, testnet, body.fromPrivateKey, body.signatureId, body.to, undefined, body.nonce, body.data,
body.ethFee?.gasLimit, body.ethFee?.gasPrice)
await validateBody(body, CreateRecord);
const client = await preparePolygonClient(testnet, provider, body.fromPrivateKey);
const hexData = isHex(body.data) ? stringToHex(body.data) : toHex(body.data);
return prepareGeneralTx(client, testnet, body.fromPrivateKey, body.signatureId, body.to || client.eth.accounts.wallet[0].address, undefined, body.nonce, hexData,
body.ethFee?.gasLimit, body.ethFee?.gasPrice);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4738,6 +4738,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"

form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"

form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
Expand Down

0 comments on commit f0b9d7c

Please sign in to comment.