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

feat: wait for minting #10

Merged
merged 2 commits into from
Feb 21, 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
83 changes: 62 additions & 21 deletions src/core/NameSky.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -7,21 +7,22 @@ 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,
Stringifier,
} 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 {
Expand Down Expand Up @@ -59,8 +60,8 @@ export class NameSky {
return this.userSettingContract.contractId;
}

account(accountId: string): Promise<Account> {
return this.selector.near.account(accountId);
account(accountId: string): MultiSendAccount {
return MultiSendAccount.new(this.selector.near.connection, accountId);
}

rpc(): Provider {
Expand Down Expand Up @@ -101,27 +102,46 @@ 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);

// controller owner id
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;
Expand Down Expand Up @@ -182,15 +202,29 @@ export class NameSky {
console.log(`Removed local full access key, registrant id: ${registrantId}`);
}

async getNFTAccountSafety(accountId: string): Promise<NameSkyNFTSafety> {
/*
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<NameSkyToken> {
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<NameSkyNftSafety> {
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()
Expand All @@ -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<AccessKeyList>({
...blockQuery,
Expand All @@ -225,16 +259,23 @@ export class NameSky {
return { isCodeHashCorrect, isControllerOwnerIdCorrect, isStateCleaned, isAccessKeysDeleted };
}

async getControllerOwnerId({ accountId, blockQuery }: GetControllerOwnerIdOptions): Promise<string | undefined> {
private async getControllerOwnerId({
accountId,
blockQuery,
}: GetControllerOwnerIdOptions): Promise<string | undefined> {
try {
return await this.selector.view({
contractId: accountId,
methodName: 'get_owner_id',
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;
}
}
}
Expand Down
15 changes: 0 additions & 15 deletions src/core/contracts/CoreContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
const transaction = MultiTransaction.batch(this.contractId).functionCall({
methodName: 'nft_unregister',
Expand Down
4 changes: 0 additions & 4 deletions src/core/types/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/core/types/data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Token } from 'multi-transaction';

// ---------------------------------------------- Controller ---------------------------------------------------
export interface NameSkyNFTSafety {
export interface NameSkyNftSafety {
isCodeHashCorrect: boolean;
isControllerOwnerIdCorrect: boolean;
isStateCleaned: boolean;
Expand Down
15 changes: 7 additions & 8 deletions src/core/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
NftNameSkyTokensArgs,
NftNameSkyTokensForOwnerArgs,
NftRedeemArgs,
NftRegisterArgs,
NftRegistrantIdsOfArgs,
NftStateArgs,
NftUnregisterArgs,
Expand All @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ export function moveRegistrantPublicKeyToEnd(registrantPublicKey: string, public
result.push(registrantPublicKey);
return result;
}

export async function sleep(timestamp: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, timestamp));
}