Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle load batch inscriptions #185

Merged
merged 18 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/extension-base/src/background/KoniTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,10 @@
externalUrl?: string;
rarity?: string;
description?: string;
properties?: Record<any, any> | null;

Check warning on line 231 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type

Check warning on line 231 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
type?: _AssetType.ERC721 | _AssetType.PSP34 | RMRK_VER; // for sending
rmrk_ver?: RMRK_VER;
onChainOption?: any; // for sending PSP-34 tokens, should be done better

Check warning on line 234 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

export interface NftCollection {
Expand Down Expand Up @@ -537,7 +537,7 @@
[ExtrinsicType.STAKING_COMPOUNDING]: RequestTuringStakeCompound,
[ExtrinsicType.STAKING_CANCEL_COMPOUNDING]: RequestTuringCancelStakeCompound,
[ExtrinsicType.STAKING_CANCEL_UNSTAKE]: RequestStakeCancelWithdrawal,
[ExtrinsicType.STAKING_POOL_WITHDRAW]: any,

Check warning on line 540 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type

// Yield
[ExtrinsicType.JOIN_YIELD_POOL]: RequestYieldStepSubmit,
Expand Down Expand Up @@ -566,8 +566,8 @@
[ExtrinsicType.TOKEN_APPROVE]: TokenApproveData,

[ExtrinsicType.EVM_EXECUTE]: TransactionConfig,
[ExtrinsicType.CROWDLOAN]: any,

Check warning on line 569 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
[ExtrinsicType.UNKNOWN]: any

Check warning on line 570 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

export enum ExtrinsicStatus {
Expand Down Expand Up @@ -662,7 +662,7 @@
// : T extends ExtrinsicType.MINT_VDOT
// ? Pick<SubmitBifrostLiquidStaking, 'rewardTokenSlug' | 'estimatedAmountReceived'>
// : undefined;
export interface TransactionHistoryItem<ET extends ExtrinsicType = ExtrinsicType.TRANSFER_BALANCE> {

Check warning on line 665 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

'ET' is defined but never used
origin?: 'app' | 'migration' | 'subsquid' | 'subscan' | 'blockstream', // 'app' or history source
callhash?: string,
signature?: string,
Expand Down Expand Up @@ -690,7 +690,7 @@
tip?: AmountData,
fee?: AmountData,
explorerUrl?: string,
additionalInfo?: any,

Check warning on line 693 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
startBlock?: number,
nonce?: number,
}
Expand Down Expand Up @@ -1135,12 +1135,12 @@
recipientAddress: string,

nftItemName?: string, // Use for confirmation view only
params: Record<string, any>,

Check warning on line 1138 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
nftItem: NftItem
}

export interface EvmNftTransaction extends ValidateTransactionResponse {
tx: Record<string, any> | null;

Check warning on line 1143 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

export interface EvmNftSubmitTransaction extends BaseRequestSign {
Expand Down Expand Up @@ -2232,6 +2232,12 @@
suri: string;
}

export type BitcoinBalanceMetadata = {
inscriptionCount: number
}

export type _BalanceMetadata = unknown;

/* Campaign */

// Use stringify to communicate, pure boolean value will error with case 'false' value
Expand Down Expand Up @@ -2278,6 +2284,7 @@
'pri(assetSetting.update)': [AssetSettingUpdateReq, boolean];

// NFT functions
'pri(inscription.loadMoreInscription)': [null, null]
'pri(evmNft.submitTransaction)': [NftTransactionRequest, SWTransactionResponse];
'pri(evmNft.getTransaction)': [NftTransactionRequest, EvmNftTransaction];
'pri(substrateNft.submitTransaction)': [NftTransactionRequest, SWTransactionResponse];
Expand Down
28 changes: 25 additions & 3 deletions packages/extension-base/src/koni/api/nft/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,18 @@ function createSubstrateNftApi (chain: string, substrateApi: _SubstrateApi | nul
}

function createBitcoinInscriptionApi (chain: string, addresses: string[]) {
const filteredAddresses = addresses.filter((a) => {
const mainnetAddresses = addresses.filter((a) => {
return getKeypairTypeByAddress(a) === 'bitcoin-86';
});

const testnetAddresses = addresses.filter((a) => {
return getKeypairTypeByAddress(a) === 'bittest-86';
});

if (_NFT_CHAIN_GROUP.bitcoin.includes(chain)) {
return new InscriptionApi(chain, filteredAddresses);
return new InscriptionApi(chain, mainnetAddresses);
} else if (_NFT_CHAIN_GROUP.bitcoinTest.includes(chain)) {
return new InscriptionApi(chain, testnetAddresses);
} else {
return null;
}
Expand All @@ -75,7 +81,7 @@ const createOrdinalApi = (chain: string, subscanChain: string, addresses: string
return new OrdinalNftApi(addresses, chain, subscanChain);
};

export class NftHandler {
export class NftService {
// General settings
chainInfoMap: Record<string, _ChainInfo> = {};
addresses: string[] = [];
Expand Down Expand Up @@ -213,4 +219,20 @@ export class NftHandler {
});
}));
}

public async loadMoreNfts (
updateItem: (chain: string, data: NftItem, owner: string) => void,
updateCollection: (chain: string, data: NftCollection) => void,
getOffset?: (address: string, chain: string) => Promise<number>
) {
const inscriptionApiHandlers = this.handlers.filter((handler) => handler instanceof InscriptionApi);

await Promise.all(inscriptionApiHandlers.map(async (handler) => {
await handler.fetchNfts({
updateItem,
updateCollection,
getOffset
});
}));
}
}
73 changes: 64 additions & 9 deletions packages/extension-base/src/koni/api/nft/inscription/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,38 @@ interface FetchedData {
results: InscriptionResponseItem[]
}

const ORDINAL_COLLECTION_INFO: NftCollection = {
export const ORDINAL_COLLECTION_INFO: NftCollection = {
chain: 'bitcoin',
collectionId: 'INSCRIPTION',
collectionName: 'Inscriptions'
};

export const ORDINAL_COLLECTION_INFO_TEST: NftCollection = {
chain: 'bitcoinTestnet',
collectionId: 'INSCRIPTION_TESTNET',
collectionName: 'Inscriptions Testnet'
};

const checkTestnet = (chain: string) => {
return chain === ORDINAL_COLLECTION_INFO_TEST.chain;
};

export class InscriptionApi extends BaseNftApi {
private isTestnet: boolean;

constructor (chain: string, addresses: string[]) {
super(chain, undefined, addresses);
this.isTestnet = checkTestnet(chain);
}

private createIframePreviewUrl (id: string) {
return `https://ordinals.com/preview/${id}`;
}

get collectionInfo () {
return this.isTestnet ? ORDINAL_COLLECTION_INFO_TEST : ORDINAL_COLLECTION_INFO;
}

private parseInsUrl (id: string, type: string) {
if (type.startsWith('audio/') || type.startsWith('text/html') || type.startsWith('image/svg') || type.startsWith('video/') || type.startsWith('model/gltf')) {
return this.createIframePreviewUrl(id);
Expand Down Expand Up @@ -123,19 +140,54 @@ export class InscriptionApi extends BaseNftApi {
return propertiesMap;
}

public async updateTextInscription (inscriptions: Inscription[], params: HandleNftParams, collectionMap: Record <string, NftCollection>) {
await Promise.all(inscriptions.map(async (ins) => {
const content = await getInscriptionContent(this.isTestnet, ins.id);
const propertiesMap = this.handleProperties(ins);

const parsedNft: NftItem = {
id: ins.id,
chain: this.chain,
owner: ins.address,
name: `#${ins.number.toString()}`,
image: this.parseInsUrl(ins.id, ins.content_type),
description: content ? JSON.stringify(content) : undefined,
collectionId: this.collectionInfo.collectionId,
rarity: ins.sat_rarity,
properties: propertiesMap
};

params.updateItem(this.chain, parsedNft, ins.address);

if (!collectionMap[this.collectionInfo.collectionId]) {
const parsedCollection: NftCollection = {
collectionId: this.collectionInfo.collectionId,
chain: this.chain,
collectionName: this.collectionInfo.collectionName,
image: this.collectionInfo.image
};

collectionMap[this.collectionInfo.collectionId] = parsedCollection;
params.updateCollection(this.chain, parsedCollection);
}
}));
}

public async handleNfts (params: HandleNftParams) {
try {
await Promise.all(this.addresses.map(async (address) => {
const balances = await getAddressInscriptions(address);
const offset = params.getOffset && await params.getOffset(address, this.collectionInfo.chain);
const balances = await getAddressInscriptions(address, this.isTestnet, offset);

if (balances.length > 0) {
const collectionMap: Record <string, NftCollection> = {};
const textIns: Inscription[] = [];

for (const ins of balances) {
let content;

if (ins.content_type.startsWith('text/plain') || ins.content_type.startsWith('application/json')) {
content = await getInscriptionContent(ins.id);
textIns.push(ins);
}

const propertiesMap = this.handleProperties(ins);
Expand All @@ -147,25 +199,28 @@ export class InscriptionApi extends BaseNftApi {
name: `#${ins.number.toString()}`,
image: this.parseInsUrl(ins.id, ins.content_type),
description: content ? JSON.stringify(content) : undefined,
collectionId: ORDINAL_COLLECTION_INFO.collectionId,
collectionId: this.collectionInfo.collectionId,
rarity: ins.sat_rarity,
properties: propertiesMap
};

params.updateItem(this.chain, parsedNft, ins.address);

if (!collectionMap[ORDINAL_COLLECTION_INFO.collectionId]) {
if (!collectionMap[this.collectionInfo.collectionId]) {
const parsedCollection: NftCollection = {
collectionId: ORDINAL_COLLECTION_INFO.collectionId,
collectionId: this.collectionInfo.collectionId,
chain: this.chain,
collectionName: ORDINAL_COLLECTION_INFO.collectionName,
image: ORDINAL_COLLECTION_INFO.image
collectionName: this.collectionInfo.collectionName,
image: this.collectionInfo.image
};

collectionMap[ORDINAL_COLLECTION_INFO.collectionId] = parsedCollection;
collectionMap[this.collectionInfo.collectionId] = parsedCollection;
params.updateCollection(this.chain, parsedCollection);
}
}

// handle all inscriptions has text content
await this.updateTextInscription(textIns, params, collectionMap);
}
}));
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion packages/extension-base/src/koni/api/nft/nft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { baseParseIPFSUrl } from '@subwallet/extension-base/utils';

export interface HandleNftParams {
updateItem: (chain: string, data: NftItem, owner: string) => void,
updateCollection: (chain: string, data: NftCollection) => void
updateCollection: (chain: string, data: NftCollection) => void,
getOffset?: (address: string, chain: string) => Promise<number>
}

export abstract class BaseNftApi {
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-base/src/koni/background/cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export class KoniCron {
};

checkNetworkAvailable = (serviceInfo: ServiceInfo): boolean => {
return Object.keys(serviceInfo.chainApiMap.substrate).length > 0 || Object.keys(serviceInfo.chainApiMap.evm).length > 0;
return Object.keys(serviceInfo.chainApiMap.substrate).length > 0 || Object.keys(serviceInfo.chainApiMap.evm).length > 0 || Object.keys(serviceInfo.chainApiMap.bitcoin).length > 0;
};

public async reloadNft () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2135,6 +2135,10 @@ export default class KoniExtension {
});
}

private loadMoreInscription () {
this.#koniState.loadMoreInscription();
}

private async evmNftSubmitTransaction (inputData: NftTransactionRequest): Promise<SWTransactionResponse> {
const { networkKey, params, recipientAddress, senderAddress } = inputData;
const contractAddress = params.contractAddress as string;
Expand Down Expand Up @@ -5373,6 +5377,10 @@ export default class KoniExtension {
case 'pri(accounts.get.meta)':
return this.getAccountMeta(request as RequestAccountMeta);

// Load more Inscriptions
case 'pri(inscription.loadMoreInscription)':
return this.loadMoreInscription();

/// Send NFT
case 'pri(evmNft.submitTransaction)':
return this.evmNftSubmitTransaction(request as NftTransactionRequest);
Expand Down
16 changes: 14 additions & 2 deletions packages/extension-base/src/koni/background/handlers/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { isSubscriptionRunning, unsubscribe } from '@subwallet/extension-base/ba
import { AccountRefMap, AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, BasicTxErrorType, ChainStakingMetadata, ChainType, ConfirmationsQueue, CrowdloanItem, CrowdloanJson, CurrentAccountInfo, CurrentAccountProxyInfo, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCheckPublicAndSecretKey, ServiceInfo, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes';
import { AccountJson, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning } from '@subwallet/extension-base/background/types';
import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH, MANTA_PAY_BALANCE_INTERVAL } from '@subwallet/extension-base/constants';
import { NftService } from '@subwallet/extension-base/koni/api/nft';
import { BalanceService } from '@subwallet/extension-base/services/balance-service';
import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
import BuyService from '@subwallet/extension-base/services/buy-service';
Expand Down Expand Up @@ -136,6 +137,7 @@ export default class KoniState {
readonly buyService: BuyService;
readonly earningService: EarningService;
readonly feeService: FeeService;
readonly nftService: NftService;

// Handle the general status of the extension
private generalStatus: ServiceStatus = ServiceStatus.INITIALIZING;
Expand Down Expand Up @@ -166,6 +168,7 @@ export default class KoniState {
this.transactionService = new TransactionService(this);
this.earningService = new EarningService(this);
this.feeService = new FeeService(this);
this.nftService = new NftService();

this.subscription = new KoniSubscription(this, this.dbService);
this.cron = new KoniCron(this, this.subscription, this.dbService);
Expand Down Expand Up @@ -498,6 +501,14 @@ export default class KoniState {
this.dbService.updateNominatorMetadata(item).catch((e) => this.logger.warn(e));
}

public loadMoreInscription () {
this.nftService.loadMoreNfts(
(...args) => this.updateNftData(...args),
(...args) => this.setNftCollection(...args),
(address: string, chain: string) => this.dbService.getAddressTotalInscriptions([address], chain)
).catch(this.logger.log);
}

public setNftCollection (network: string, data: NftCollection, callback?: (data: NftCollection) => void): void {
this.dbService.addNftCollection(data).catch((e) => this.logger.warn(e));
callback && callback(data);
Expand Down Expand Up @@ -1885,9 +1896,10 @@ export default class KoniState {
}

public async reloadNft () {
const currentAddress = this.keyringService.currentAccount.address;
const accountProxyId = this.keyringService.currentAccountProxy.proxyId;
const addresses = this.getAccountProxyAddresses(accountProxyId);

await this.dbService.removeNftsByAddress(currentAddress);
await this.dbService.removeNftsByAddress(addresses);

return await this.cron.reloadNft();
}
Expand Down
16 changes: 8 additions & 8 deletions packages/extension-base/src/koni/background/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
import { subscribeCrowdloan } from '@subwallet/extension-base/koni/api/dotsama/crowdloan';
import { NftHandler } from '@subwallet/extension-base/koni/api/nft';
import { NftService } from '@subwallet/extension-base/koni/api/nft';
import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
import { COMMON_RELOAD_EVENTS, EventItem, EventType } from '@subwallet/extension-base/services/event-service/types';
import DatabaseService from '@subwallet/extension-base/services/storage-service/DatabaseService';
Expand All @@ -16,8 +16,6 @@ import KoniState from './handlers/State';

type SubscriptionName = 'balance' | 'crowdloan' | 'yieldPoolStats' | 'yieldPosition';

const nftHandler = new NftHandler();

export class KoniSubscription {
private eventHandler?: (events: EventItem<EventType>[], eventTypes: EventType[]) => void;
private subscriptionMap: Record<SubscriptionName, (() => void) | undefined> = {
Expand All @@ -30,11 +28,13 @@ export class KoniSubscription {
public dbService: DatabaseService;
private state: KoniState;
private logger: Logger;
private nftService: NftService;

constructor (state: KoniState, dbService: DatabaseService) {
this.dbService = dbService;
this.state = state;
this.logger = createLogger('Subscription');
this.nftService = this.state.nftService;
}

getSubscriptionMap () {
Expand Down Expand Up @@ -142,12 +142,12 @@ export class KoniSubscription {
}

initNftSubscription (addresses: string[], substrateApiMap: Record<string, _SubstrateApi>, evmApiMap: Record<string, _EvmApi>, smartContractNfts: _ChainAsset[], chainInfoMap: Record<string, _ChainInfo>) {
nftHandler.setChainInfoMap(chainInfoMap);
nftHandler.setDotSamaApiMap(substrateApiMap);
nftHandler.setWeb3ApiMap(evmApiMap);
nftHandler.setAddresses(addresses);
this.nftService.setChainInfoMap(chainInfoMap);
this.nftService.setDotSamaApiMap(substrateApiMap);
this.nftService.setWeb3ApiMap(evmApiMap);
this.nftService.setAddresses(addresses);

nftHandler.handleNfts(
this.nftService.handleNfts(
smartContractNfts,
(...args) => this.state.updateNftData(...args),
(...args) => this.state.setNftCollection(...args)
Expand Down
Loading
Loading