Skip to content

Commit

Permalink
feat: add fee estimation nfts (#594)
Browse files Browse the repository at this point in the history
* feat: add fee estimation nfts

* fix: resolve PR issues

* chore: changeset for NFT fee calculations
  • Loading branch information
skeremidchiev authored Aug 19, 2022
1 parent e6b7155 commit 23c964d
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 69 deletions.
18 changes: 18 additions & 0 deletions .changeset/sharp-camels-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@chainify/client': patch
'@chainify/evm': patch
'@chainify/solana': patch
'@chainify/bitcoin': patch
'@chainify/bitcoin-ledger': patch
'@chainify/errors': patch
'@chainify/evm-contracts': patch
'@chainify/evm-ledger': patch
'@chainify/hw-ledger': patch
'@chainify/logger': patch
'@chainify/near': patch
'@chainify/terra': patch
'@chainify/types': patch
'@chainify/utils': patch
---

fee estimation for NFTs
19 changes: 19 additions & 0 deletions packages/client/lib/Nft.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UnsupportedMethodError } from '@chainify/errors';
import { AddressType, BigNumber, FeeType, NFTAsset, Transaction } from '@chainify/types';
import Wallet from './Wallet';

Expand Down Expand Up @@ -33,5 +34,23 @@ export default abstract class Nft<T, S> {

public abstract isApprovedForAll(contract: AddressType, operator: AddressType): Promise<boolean>;

public estimateTransfer(
_contractAddress: AddressType,
_receiver: AddressType,
_tokenIDs: string[],
_amounts?: number[],
_data?: string
): Promise<BigNumber> {
throw new UnsupportedMethodError('Method not supported');
}

public estimateApprove(_contract: AddressType, _operator: AddressType, _tokenID: number): Promise<BigNumber> {
throw new UnsupportedMethodError('Method not supported');
}

public estimateApproveAll(_contract: AddressType, _operator: AddressType, _state: boolean): Promise<BigNumber> {
throw new UnsupportedMethodError('Method not supported');
}

public abstract fetch(): Promise<NFTAsset[]>;
}
177 changes: 116 additions & 61 deletions packages/evm/lib/nft/EvmNftProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export abstract class EvmNftProvider extends Nft<BaseProvider, Signer> {

protected schemas: Record<string, NftContract>;
protected cache: Record<string, NftInfo>;
protected walletProvider: EvmBaseWalletProvider<BaseProvider>;

constructor(walletProvider: EvmBaseWalletProvider<BaseProvider>) {
super(walletProvider);
Expand All @@ -36,37 +37,7 @@ export abstract class EvmNftProvider extends Nft<BaseProvider, Signer> {
data = '0x',
fee?: FeeType
): Promise<Transaction<EthersTransactionResponse>> {
const { schema, contract } = await this._cacheGet(contractAddress);
const owner = (await this.walletProvider.getAddress()).toString();
const to = receiver.toString();

let tx: EthersPopulatedTransaction;

switch (schema) {
case NftTypes.ERC721: {
if (tokenIDs.length !== 1) {
throw new Error(`nft.transfer supports exactly 1 tokenID transfer for ERC721. received ${tokenIDs.join(', ')}`);
}
const _contract: ERC721 = contract as ERC721;
tx = await _contract.populateTransaction['safeTransferFrom(address,address,uint256,bytes)'](owner, to, tokenIDs[0], data);
break;
}

case NftTypes.ERC1155: {
const _contract: ERC1155 = contract as ERC1155;
if (tokenIDs.length > 1) {
tx = await _contract.populateTransaction.safeBatchTransferFrom(owner, to, tokenIDs, amounts, data);
} else {
tx = await _contract.populateTransaction.safeTransferFrom(owner, to, tokenIDs[0], amounts[0], data);
}
break;
}

default: {
throw new UnsupportedMethodError(`Unsupported NFT type: ${schema}`);
}
}

const tx = await this.populateTrasnfer(contractAddress, receiver, tokenIDs, amounts, data);
return await this.walletProvider.sendTransaction(toEthereumTxRequest(tx, fee));
}

Expand Down Expand Up @@ -103,29 +74,17 @@ export abstract class EvmNftProvider extends Nft<BaseProvider, Signer> {
tokenID: number,
fee?: FeeType
): Promise<Transaction<EthersTransactionResponse>> {
const { schema, contract } = await this._cacheGet(contractAddress);
const _operator = operator.toString();

let tx: EthersPopulatedTransaction;

switch (schema) {
case NftTypes.ERC721: {
const _contract: ERC721 = contract as ERC721;
tx = await _contract.populateTransaction.approve(_operator, tokenID);
break;
}

case NftTypes.ERC1155: {
const _contract: ERC1155 = contract as ERC1155;
tx = await _contract.populateTransaction.setApprovalForAll(_operator, true);
break;
}

default: {
throw new UnsupportedMethodError(`Unsupported NFT type: ${schema}`);
}
}
const tx = await this.populateApprove(contractAddress, operator, tokenID);
return this.walletProvider.sendTransaction(toEthereumTxRequest(tx, fee));
}

public async approveAll(
contractAddress: AddressType,
operator: AddressType,
state: boolean,
fee?: FeeType
): Promise<Transaction<EthersTransactionResponse>> {
const tx = await this.populateApproveAll(contractAddress, operator, state);
return this.walletProvider.sendTransaction(toEthereumTxRequest(tx, fee));
}

Expand All @@ -135,15 +94,28 @@ export abstract class EvmNftProvider extends Nft<BaseProvider, Signer> {
return await contract.isApprovedForAll(owner.toString(), operator.toString());
}

public async approveAll(
public async estimateTransfer(
contractAddress: AddressType,
operator: AddressType,
state: boolean,
fee?: FeeType
): Promise<Transaction<EthersTransactionResponse>> {
const { contract } = await this._cacheGet(contractAddress);
const tx = await contract.populateTransaction.setApprovalForAll(operator.toString(), state);
return this.walletProvider.sendTransaction(toEthereumTxRequest(tx, fee));
receiver: AddressType,
tokenIDs: string[],
amounts?: number[],
data = '0x'
): Promise<BigNumber> {
const tx = await this.populateTrasnfer(contractAddress, receiver, tokenIDs, amounts, data);
const estimation = await this.walletProvider.estimateGas(tx);
return new BigNumber(estimation.toString());
}

public async estimateApprove(contractAddress: AddressType, operator: AddressType, tokenID: number): Promise<BigNumber> {
const tx = await this.populateApprove(contractAddress, operator, tokenID);
const estimation = await this.walletProvider.estimateGas(tx);
return new BigNumber(estimation.toString());
}

public async estimateApproveAll(contractAddress: AddressType, operator: AddressType, state: boolean): Promise<BigNumber> {
const tx = await this.populateApproveAll(contractAddress, operator, state);
const estimation = await this.walletProvider.estimateGas(tx);
return new BigNumber(estimation.toString());
}

async fetch(): Promise<NFTAsset[]> {
Expand Down Expand Up @@ -181,4 +153,87 @@ export abstract class EvmNftProvider extends Nft<BaseProvider, Signer> {

throw new UnsupportedMethodError(`Cannot find the data for ${_contractAddress}`);
}

private async populateApprove(
contractAddress: AddressType,
operator: AddressType,
tokenID: number
): Promise<EthersPopulatedTransaction> {
const { schema, contract } = await this._cacheGet(contractAddress);
const _operator = operator.toString();

let tx: EthersPopulatedTransaction;

switch (schema) {
case NftTypes.ERC721: {
const _contract: ERC721 = contract as ERC721;
tx = await _contract.populateTransaction.approve(_operator, tokenID);
break;
}

case NftTypes.ERC1155: {
const _contract: ERC1155 = contract as ERC1155;
tx = await _contract.populateTransaction.setApprovalForAll(_operator, true);
break;
}

default: {
throw new UnsupportedMethodError(`Unsupported NFT type: ${schema}`);
}
}

return tx;
}

private async populateTrasnfer(
contractAddress: AddressType,
receiver: AddressType,
tokenIDs: string[],
amounts?: number[],
data = '0x'
): Promise<EthersPopulatedTransaction> {
const { schema, contract } = await this._cacheGet(contractAddress);
const owner = (await this.walletProvider.getAddress()).toString();
const to = receiver.toString();

let tx: EthersPopulatedTransaction;

switch (schema) {
case NftTypes.ERC721: {
if (tokenIDs.length !== 1) {
throw new Error(`nft.transfer supports exactly 1 tokenID transfer for ERC721. received ${tokenIDs.join(', ')}`);
}
const _contract: ERC721 = contract as ERC721;
tx = await _contract.populateTransaction['safeTransferFrom(address,address,uint256,bytes)'](owner, to, tokenIDs[0], data);
break;
}

case NftTypes.ERC1155: {
const _contract: ERC1155 = contract as ERC1155;
if (tokenIDs.length > 1) {
tx = await _contract.populateTransaction.safeBatchTransferFrom(owner, to, tokenIDs, amounts, data);
} else {
tx = await _contract.populateTransaction.safeTransferFrom(owner, to, tokenIDs[0], amounts[0], data);
}
break;
}

default: {
throw new UnsupportedMethodError(`Unsupported NFT type: ${schema}`);
}
}

return tx;
}

private async populateApproveAll(
contractAddress: AddressType,
operator: AddressType,
state: boolean
): Promise<EthersPopulatedTransaction> {
const { contract } = await this._cacheGet(contractAddress);
const tx = await contract.populateTransaction.setApprovalForAll(operator.toString(), state);

return tx;
}
}
17 changes: 9 additions & 8 deletions packages/solana/lib/nft/SolanaNftProvider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HttpClient, Nft, Wallet } from '@chainify/client';
import { UnsupportedMethodError } from '@chainify/errors';
import { AddressType, BigNumber, ChainId, NFTAsset, Transaction } from '@chainify/types';
import { BaseProvider } from '@ethersproject/providers';
import Moralis from 'moralis/node';
Expand Down Expand Up @@ -90,16 +91,16 @@ export class SolanaNftProvider extends Nft<BaseProvider, SolanaWalletProvider> {
});
}

balanceOf(_: AddressType, __: AddressType[], ___: number[]): Promise<BigNumber | BigNumber[]> {
throw new Error('Method not implemented.');
balanceOf(_contractAddress: AddressType, _owners: AddressType[], _tokenIDs: number[]): Promise<BigNumber | BigNumber[]> {
throw new UnsupportedMethodError('Method not supported');
}
approve(_: AddressType, __: AddressType, ___: number): Promise<Transaction<any>> {
throw new Error('Method not implemented.');
approve(_contract: AddressType, _operator: AddressType, _tokenID: number): Promise<Transaction<any>> {
throw new UnsupportedMethodError('Method not supported');
}
approveAll(_: AddressType, __: AddressType, ___: boolean): Promise<Transaction<any>> {
throw new Error('Method not implemented.');
approveAll(_contract: AddressType, _operator: AddressType, _state: boolean): Promise<Transaction<any>> {
throw new UnsupportedMethodError('Method not supported');
}
isApprovedForAll(_: AddressType, __: AddressType): Promise<boolean> {
throw new Error('Method not implemented.');
isApprovedForAll(_contract: AddressType, _operator: AddressType): Promise<boolean> {
throw new UnsupportedMethodError('Method not supported');
}
}

0 comments on commit 23c964d

Please sign in to comment.