diff --git a/src/core/NameSky.ts b/src/core/NameSky.ts index d06a062..079a1f9 100644 --- a/src/core/NameSky.ts +++ b/src/core/NameSky.ts @@ -1,4 +1,4 @@ -import { ACTION_MAX_NUM, moveRegistrantPublicKeyToEnd, REGISTRANT_KEYSTORE_PREFIX } from '../utils'; +import { ACTION_MAX_NUM, moveRegistrantPublicKeyToEnd, REGISTRANT_KEYSTORE_PREFIX, sleep } from '../utils'; import { CoreContract } from './contracts'; import { MarketplaceContract } from './contracts'; import { KeyPairEd25519, PublicKey } from 'near-api-js/lib/utils'; @@ -7,13 +7,14 @@ import { Network } from '@near-wallet-selector/core'; import { NameSkyComponent, NameSkyConfig } from './types/config'; import { Account } from 'near-api-js'; import { CleanStateArgs, InitArgs } from './types/args'; -import { GetControllerOwnerIdOptions, SetupControllerOptions } from './types/options'; +import { GetControllerOwnerIdOptions, NftRegisterOptions, SetupControllerOptions } from './types/options'; import { UserSettingContract } from './contracts/UserSettingContract'; import { getBase58CodeHash } from '../utils'; import { Amount, BlockQuery, BorshSchema, + MultiSendAccount, MultiSendWalletSelector, MultiTransaction, setupMultiSendWalletSelector, @@ -21,7 +22,7 @@ import { } from 'multi-transaction'; import { Provider } from 'near-api-js/lib/providers'; import { AccessKeyList, AccountView } from 'near-api-js/lib/providers/provider'; -import { NameSkyNFTSafety } from './types/data'; +import { NameSkyNftSafety, NameSkyToken } from './types/data'; import { Buffer } from 'buffer'; export class NameSky { @@ -59,8 +60,8 @@ export class NameSky { return this.userSettingContract.contractId; } - account(accountId: string): Promise { - return this.selector.near.account(accountId); + account(accountId: string): MultiSendAccount { + return MultiSendAccount.new(this.selector.near.connection, accountId); } rpc(): Provider { @@ -101,16 +102,35 @@ export class NameSky { console.log(`onRequestFullAccess Succeeded`); } + // signed by registrant + async register({ registrantId, minterId, gas }: NftRegisterOptions) { + const [mintFee, oldMinterId] = await Promise.all([ + this.coreContract.get_mint_fee({}), + this.coreContract.nft_get_minter_id({ args: { registrant_id: registrantId } }), + ]); + + const transaction = MultiTransaction.batch(this.getCoreContractId()).functionCall({ + methodName: 'nft_register', + args: { + minter_id: minterId, + }, + attachedDeposit: oldMinterId ? Amount.ONE_YOCTO : mintFee, + gas, + }); + + await this.selector.sendWithLocalKey(registrantId, transaction); + } + // signed by registrant async setupController({ registrantId, code, gasForCleanState, gasForInit }: SetupControllerOptions) { /* We don't need to check follow conditions at the same block, because these are only used to check whether to skip `setupController` */ - const account = await this.account(registrantId); + const account = this.account(registrantId); // code hash - const accountView = await account.state(); + const accountView = await account.into().state(); const accountCodeHash = accountView.code_hash; const codeHash = getBase58CodeHash(code); @@ -118,10 +138,10 @@ export class NameSky { const controllerOwnerId = await this.getControllerOwnerId({ accountId: registrantId }); // state - const state = await account.viewState(''); + const state = await account.into().viewState(''); // access keys - const accessKeys = await account.getAccessKeys(); + const accessKeys = await account.into().getAccessKeys(); const isCodeHashVerified = accountCodeHash === codeHash; const isControllerOwnerIdVerified = controllerOwnerId === this.coreContract.contractId; @@ -182,15 +202,29 @@ export class NameSky { console.log(`Removed local full access key, registrant id: ${registrantId}`); } - async getNFTAccountSafety(accountId: string): Promise { - /* - We need to check follow conditions at the same block for account security reason - */ - const { - header: { height }, - } = await this.rpc().block({ finality: 'optimistic' }); + // minted by operator + async waitForMinting(tokenId: string): Promise { + for (;;) { + const token = await this.coreContract.nft_namesky_token({ + args: { + token_id: tokenId, + }, + }); + + if (token) { + return token; + } + + console.log(`NFT(${tokenId}) is on minting...`); + + await sleep(1000); + } + } + + async getNftAccountSafety(accountId: string): Promise { + const block = await this.rpc().block({ finality: 'optimistic' }); - const blockQuery: BlockQuery = { blockId: height }; + const blockQuery: BlockQuery = { blockId: block.header.height }; const [codeHash, controllerCodeViews, controllerOwnerId, state, { keys: accessKeys }] = await Promise.all([ this.rpc() @@ -208,7 +242,7 @@ export class NameSky { blockQuery, }), - this.account(accountId).then((account) => account.viewState('', blockQuery)), + this.account(accountId).into().viewState('', blockQuery), this.rpc().query({ ...blockQuery, @@ -225,7 +259,10 @@ export class NameSky { return { isCodeHashCorrect, isControllerOwnerIdCorrect, isStateCleaned, isAccessKeysDeleted }; } - async getControllerOwnerId({ accountId, blockQuery }: GetControllerOwnerIdOptions): Promise { + private async getControllerOwnerId({ + accountId, + blockQuery, + }: GetControllerOwnerIdOptions): Promise { try { return await this.selector.view({ contractId: accountId, @@ -233,8 +270,12 @@ export class NameSky { blockQuery, }); } catch (e: any) { - console.warn(`Account(${accountId}) is not NameSky NFT yet`); - return undefined; + if (e.message.includes('MethodNotFound')) { + console.info(`Controller code not found on ${accountId}`); + return undefined; + } + + throw e; } } } diff --git a/src/core/contracts/CoreContract.ts b/src/core/contracts/CoreContract.ts index 27ca0e5..4469168 100644 --- a/src/core/contracts/CoreContract.ts +++ b/src/core/contracts/CoreContract.ts @@ -142,21 +142,6 @@ export class CoreContract extends Contract { // -------------------------------------------------- Call ------------------------------------------------------- - // signed by registrant - async nftRegister({ registrantId, args, gas }: NftRegisterOptions) { - const [mintFee, minterId] = await Promise.all([ - this.get_mint_fee({}), - this.nft_get_minter_id({ args: { registrant_id: registrantId } }), - ]); - const transaction = MultiTransaction.batch(this.contractId).functionCall({ - methodName: 'nft_register', - args, - attachedDeposit: minterId ? Amount.ONE_YOCTO : mintFee, - gas, - }); - await this.selector.sendWithLocalKey(registrantId, transaction); - } - async nftUnregister({ args, gas, callbackUrl }: NftUnregisterOptions): Promise { const transaction = MultiTransaction.batch(this.contractId).functionCall({ methodName: 'nft_unregister', diff --git a/src/core/types/args.ts b/src/core/types/args.ts index 37ec0d6..1493077 100644 --- a/src/core/types/args.ts +++ b/src/core/types/args.ts @@ -12,10 +12,6 @@ export type CleanStateArgs = Uint8Array[]; export type InitArgs = Uint8Array; // ---------------------------------------------- Core --------------------------------------------------------- -export interface NftRegisterArgs { - minter_id: string; -} - export interface NftUnregisterArgs { registrant_id: string; public_key: string; diff --git a/src/core/types/data.ts b/src/core/types/data.ts index 676eff7..346e033 100644 --- a/src/core/types/data.ts +++ b/src/core/types/data.ts @@ -1,7 +1,7 @@ import { Token } from 'multi-transaction'; // ---------------------------------------------- Controller --------------------------------------------------- -export interface NameSkyNFTSafety { +export interface NameSkyNftSafety { isCodeHashCorrect: boolean; isControllerOwnerIdCorrect: boolean; isStateCleaned: boolean; diff --git a/src/core/types/options.ts b/src/core/types/options.ts index 432517d..588dd77 100644 --- a/src/core/types/options.ts +++ b/src/core/types/options.ts @@ -27,7 +27,6 @@ import { NftNameSkyTokensArgs, NftNameSkyTokensForOwnerArgs, NftRedeemArgs, - NftRegisterArgs, NftRegistrantIdsOfArgs, NftStateArgs, NftUnregisterArgs, @@ -46,7 +45,13 @@ interface ChangeFunctionExtraOptions { callbackUrl?: string; } -// ---------------------------------------------- Controller --------------------------------------------------- +// ---------------------------------------------- Registrant --------------------------------------------------- +export interface NftRegisterOptions { + registrantId: string; + minterId: string; + gas?: string; +} + export interface SetupControllerOptions { registrantId: string; code: Buffer; @@ -55,12 +60,6 @@ export interface SetupControllerOptions { } // ---------------------------------------------- Core --------------------------------------------------------- -export interface NftRegisterOptions { - registrantId: string; - args: NftRegisterArgs; - gas?: string; -} - export interface NftUnregisterOptions extends ChangeFunctionExtraOptions { args: NftUnregisterArgs; gas?: string; diff --git a/src/utils/common.ts b/src/utils/common.ts index 68507c7..dd50519 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -17,3 +17,7 @@ export function moveRegistrantPublicKeyToEnd(registrantPublicKey: string, public result.push(registrantPublicKey); return result; } + +export async function sleep(timestamp: number): Promise { + return new Promise((resolve) => setTimeout(resolve, timestamp)); +}