Skip to content

Commit

Permalink
refactor: migrate transferNft method to base authenticator class, use…
Browse files Browse the repository at this point in the history
… account authenticator in nft store, resolve git conflicts
  • Loading branch information
donnyquixotic committed Oct 31, 2023
2 parents 192ecd4 + 45d8271 commit e3dc951
Show file tree
Hide file tree
Showing 18 changed files with 350 additions and 150 deletions.
2 changes: 1 addition & 1 deletion src/antelope/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getAntelope } from 'src/antelope';
import { AntelopeError, AntelopeErrorPayload } from 'src/antelope/types';

export class AntelopeConfig {
wrapError(description: string, error: unknown): AntelopeError {
transactionError(description: string, error: unknown): AntelopeError {
if (error instanceof AntelopeError) {
return error as AntelopeError;
}
Expand Down
12 changes: 6 additions & 6 deletions src/antelope/stores/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export const useBalancesStore = defineStore(store_name, {
this.processBalanceForToken(label, sys_token, balanceBn);
} catch (error) {
console.error(error);
throw getAntelope().config.wrapError('antelope.evm.error_update_system_balance_failed', error);
throw getAntelope().config.transactionError('antelope.evm.error_update_system_balance_failed', error);
}
},
shouldAddTokenBalance(label: string, balanceBn: BigNumber, token: TokenClass): boolean {
Expand Down Expand Up @@ -322,7 +322,7 @@ export const useBalancesStore = defineStore(store_name, {
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
}
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_transfer_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_transfer_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand All @@ -347,7 +347,7 @@ export const useBalancesStore = defineStore(store_name, {
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
}
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_wrap_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_wrap_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand All @@ -371,7 +371,7 @@ export const useBalancesStore = defineStore(store_name, {
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
}
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_unwrap_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_unwrap_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand Down Expand Up @@ -403,7 +403,7 @@ export const useBalancesStore = defineStore(store_name, {
});
} catch (error) {
console.error(error);
throw getAntelope().config.wrapError('antelope.evm.error_transfer_failed', error);
throw getAntelope().config.transactionError('antelope.evm.error_transfer_failed', error);
} finally {
useFeedbackStore().unsetLoading('transferNativeTokens');
}
Expand All @@ -423,7 +423,7 @@ export const useBalancesStore = defineStore(store_name, {
return result as EvmTransactionResponse | SendTransactionResult;
} catch (error) {
console.error(error);
throw getAntelope().config.wrapError('antelope.evm.error_transfer_failed', error);
throw getAntelope().config.transactionError('antelope.evm.error_transfer_failed', error);
} finally {
useFeedbackStore().unsetLoading('transferEVMTokens');
}
Expand Down
37 changes: 34 additions & 3 deletions src/antelope/stores/nfts.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
/**
* NFTs: This store is responsible for all functionality pertaining to NFTs,
* such as fetching them for a given account or getting information on a particular NFT
* such as fetching them for a given account, getting information on a particular NFT, or transferring NFTs
*/

import { defineStore } from 'pinia';

import { Label, Network, Address, IndexerTransactionsFilter, NFTClass, NftTokenInterface } from 'src/antelope/types';
import { Label, Network, Address, IndexerTransactionsFilter, NFTClass, NftTokenInterface, TransactionResponse, addressString } from 'src/antelope/types';

import { useFeedbackStore, getAntelope, useChainStore, useEVMStore, CURRENT_CONTEXT } from 'src/antelope';
import { createTraceFunction, isTracingAll } from 'src/antelope/stores/feedback';
import { toRaw } from 'vue';
import { AccountModel } from 'src/antelope/stores/account';
import { AccountModel, EvmAccountModel, useAccountStore } from 'src/antelope/stores/account';
import NativeChainSettings from 'src/antelope/chains/NativeChainSettings';
import EVMChainSettings from 'src/antelope/chains/EVMChainSettings';
import { errorToString } from 'src/antelope/config';
import { truncateAddress } from 'src/antelope/stores/utils/text-utils';
import { subscribeForTransactionReceipt } from 'src/antelope/stores/utils/trx-utils';

export interface NFTsInventory {
owner: Address;
Expand Down Expand Up @@ -126,6 +127,7 @@ export const useNftsStore = defineStore(store_name, {
},
});
},

async updateNFTsForAccount(label: string, account: AccountModel | null) {
this.trace('updateNFTsForAccount', label, account);
if (!account?.account) {
Expand Down Expand Up @@ -280,6 +282,35 @@ export const useNftsStore = defineStore(store_name, {
limit: 10000,
});
},

async subscribeForTransactionReceipt(account: EvmAccountModel, response: TransactionResponse): Promise<TransactionResponse> {
this.trace('subscribeForTransactionReceipt', account.account, response.hash);
return subscribeForTransactionReceipt(account, response).then(({ newResponse, receipt }) => {
newResponse.wait().then(() => {
this.trace('subscribeForTransactionReceipt', newResponse.hash, 'receipt:', receipt.status, receipt);
this.updateNFTsForAccount(CURRENT_CONTEXT, account);
});
return newResponse;
});
},

async transferNft(label: Label, contractAddress: string, tokenId: string, type: NftTokenInterface, from: addressString, to: addressString): Promise<TransactionResponse> {
const funcname = 'transferNft';
this.trace(funcname, label, contractAddress, tokenId, type);

try {
useFeedbackStore().setLoading(funcname);
const account = useAccountStore().loggedAccount as EvmAccountModel;
return await account.authenticator.transferNft(contractAddress, tokenId, type, from, to)
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
} catch (error) {
const trxError = getAntelope().config.transactionError('antelope.evm.error_transfer_nft', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
useFeedbackStore().unsetLoading(funcname);
}
},
},
});

Expand Down
6 changes: 3 additions & 3 deletions src/antelope/stores/rex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ export const useRexStore = defineStore(store_name, {
return await authenticator.stakeSystemTokens(amount)
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_stakes_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_stakes_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand All @@ -272,7 +272,7 @@ export const useRexStore = defineStore(store_name, {
return await authenticator.unstakeSystemTokens(amount)
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_unstakes_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_unstakes_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand All @@ -296,7 +296,7 @@ export const useRexStore = defineStore(store_name, {
return await authenticator.withdrawUnstakedTokens()
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
} catch (error) {
const trxError = getAntelope().config.wrapError('antelope.evm.error_withdraw_failed', error);
const trxError = getAntelope().config.transactionError('antelope.evm.error_withdraw_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
} finally {
Expand Down
1 change: 1 addition & 0 deletions src/antelope/types/Filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ export interface IndexerTransfersFilter {
startBlock?: number; // first block to include in the query
contract?: string; // filter by contract address
includeAbi?: boolean; // indicate whether to include abi
tokenId?: number; // optional id for an NFT in a given collection
}
29 changes: 27 additions & 2 deletions src/antelope/wallets/authenticators/EVMAuthenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import { SendTransactionResult, WriteContractResult } from '@wagmi/core';
import { BigNumber, ethers } from 'ethers';
import { CURRENT_CONTEXT, getAntelope, useAccountStore } from 'src/antelope';
import { CURRENT_CONTEXT, getAntelope, useAccountStore, useContractStore } from 'src/antelope';
import EVMChainSettings from 'src/antelope/chains/EVMChainSettings';
import { useChainStore } from 'src/antelope/stores/chain';
import { useEVMStore } from 'src/antelope/stores/evm';
import { createTraceFunction, isTracingAll, useFeedbackStore } from 'src/antelope/stores/feedback';
import { usePlatformStore } from 'src/antelope/stores/platform';
import { AntelopeError, EvmABI, EvmFunctionParam, EvmTransactionResponse, ExceptionError, TokenClass, addressString, erc20Abi, escrowAbiWithdraw, stlosAbiDeposit, stlosAbiWithdraw, wtlosAbiDeposit, wtlosAbiWithdraw } from 'src/antelope/types';
import { AntelopeError, NftTokenInterface, ERC1155_TYPE, ERC721_TYPE, EvmABI, EvmABIEntry, EvmFunctionParam, EvmTransactionResponse, ExceptionError, TokenClass, addressString, erc20Abi, erc721Abi, escrowAbiWithdraw, stlosAbiDeposit, stlosAbiWithdraw, wtlosAbiDeposit, wtlosAbiWithdraw } from 'src/antelope/types';

export abstract class EVMAuthenticator {

Expand Down Expand Up @@ -235,6 +235,31 @@ export abstract class EVMAuthenticator {
}
}

/**
* This method transfers NFTs between accounts
* @param contractAddress collection address
* @param tokenId id of the nft in collection
* @param type type of token, 721 or 1155
* @param from address of sender
* @param to address of receiving account
* @param quantity optional value for 1155, default 1 for 721
* @returns transaction response with the hash and a wait() method to wait confirmation
*/
async transferNft(contractAddress: string, tokenId: string, type: NftTokenInterface, from: addressString, to: addressString, quantity = 1): Promise<EvmTransactionResponse | WriteContractResult | undefined> {
this.trace('transferNft', contractAddress, tokenId, type, from, to);
const contract = await useContractStore().getContract(this.label, contractAddress);
if (contract) {
const transferAbi = erc721Abi.filter((abi:EvmABIEntry) => abi.name === 'safeTransferFrom');
if (type === ERC721_TYPE){
return this.signCustomTransaction(contractAddress, [transferAbi[0]], [from, to, tokenId]);
}else if (type === ERC1155_TYPE){
return this.signCustomTransaction(contractAddress, [transferAbi[1]], [from, to, tokenId, quantity]);
}
} else {
throw new AntelopeError('antelope.balances.error_token_contract_not_found', { address: contractAddress });
}
}

/**
* This method creates a Transaction to wrap system tokens into ERC20 tokens
* @param amount amount of system tokens to wrap
Expand Down
2 changes: 0 additions & 2 deletions src/antelope/wallets/authenticators/OreIdAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { useChainStore } from 'src/antelope/stores/chain';
import { RpcEndpoint } from 'universal-authenticator-library';
import { TELOS_ANALYTICS_EVENT_IDS } from 'src/antelope/chains/chain-constants';


const name = 'OreId';
export const OreIdAuthName = name;

Expand Down Expand Up @@ -294,5 +293,4 @@ export class OreIdAuth extends EVMAuthenticator {

return this.performOreIdTransaction(from, transactionBody);
}

}
2 changes: 1 addition & 1 deletion src/components/evm/AppNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export default defineComponent({
// if the user has come from an external source, pressing back should go to the parent route
const navigatedFromApp = sessionStorage.getItem('navigatedFromApp');
if (navigatedFromApp) {
if (navigatedFromApp && this.$route.query.tab !== 'attributes') { // @TODO refactor to avoid explicit conditionals, required to nav back from details page but retain tab navigation history
this.$router.go(-1);
} else {
const parent = this.$route.meta.parent as string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/evm/AppPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default defineComponent({
}
if (!this.tabs.includes(newValue.query.tab)) {
this.$router.replace({ query: { tab: this.tabs[0] } });
this.$router.push({ path: this.$route.path, query: { ...this.$route.query, tab: this.tabs[0] } });
}
}
},
Expand Down
8 changes: 8 additions & 0 deletions src/css/global/_global.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.q-btn.wallet-btn {
@include text--header-5;
width: auto;
padding: 13px 24px;
&+& {
margin-left: 16px;
}
}
1 change: 1 addition & 0 deletions src/css/global/global-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
@import 'z-index';
@import 'media-queries';
@import 'typography';
@import 'global';
6 changes: 6 additions & 0 deletions src/i18n/en-us/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ export default {
empty_collection_message: 'Purchase your first collectible',
empty_collection_link_text: 'here',
collectibles_per_page: 'Collectibles per page',
// transfer
transfer: 'Transfer',
transfer_collectible: 'transfer collectible',
transfer_from: 'from',
transfer_on_telos: 'on Telos',
},
evm_wrap: {
wrap: 'Wrap',
Expand Down Expand Up @@ -548,6 +553,7 @@ export default {
error_unstakes_failed: 'An unknown error occurred when unstaking tokens',
error_withdraw_failed: 'An unknown error occurred when withdrawing tokens',
error_fetching_token_price: 'An unknown error occurred when fetching token price data',
error_transfer_nft: 'An error occured while transferring collectible',
},
history: {
error_fetching_transactions: 'Unexpected error fetching transactions. Please refresh the page to try again.',
Expand Down
8 changes: 0 additions & 8 deletions src/pages/demo/SendPageErrors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,6 @@ export default defineComponent({
</template>

<style lang="scss">
.q-btn.wallet-btn {
@include text--header-5;
&+& {
margin-left: 16px;
}
}
.c-send-page-errors {
&__title-container {
flex-direction: column;
Expand Down
Loading

0 comments on commit e3dc951

Please sign in to comment.