Newer
Older
import CredentialsProvider from 'next-auth/providers/credentials';
import { encodeAddress, Keyring } from '@polkadot/keyring';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';
import { AssetBalance } from '@polkadot/types/interfaces';
import { Int } from '@polkadot/types';
address: string | undefined;
ksmAddress: string;
freeBalance: BN;
address: string;
message: string;
signature: string;
csrfToken: string;
type TokenLevels = {
free?: number;
standard?: number;
premium?: number;
id?: number;
};
export const tokenAssetConfig = {
// load JSON file with token asset configuration from TOKEN_ASSET_CFG variable: {"JUNK":{"standard": 1,"premium":2},"GOLD":{"free": 0, "standard": 1, "premium":2}}
TAConfig: new Map<string, TokenLevels>(Object.entries(JSON.parse(process.env.NEXT_PUBLIC_TOKEN_ASSET_CFG || '{}')))
};
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
address: {
label: 'Address',
type: 'text',
placeholder: '0x0',
},
message: {
label: 'CSRF Token',
type: 'text',
placeholder: '0x0',
label: 'Name',
type: 'text',
placeholder: 'name',
},
// AA: specify xx Network
const keyring = new Keyring({ type: 'sr25519', ss58Format: 55 });
if (message.uri !== process.env.NEXTAUTH_URL) {
return Promise.reject(new Error('🚫 You shall not pass!'));
if (message.nonce !== credentials.csrfToken) {
return Promise.reject(new Error('🚫 You shall not pass!'));
const { isValid } = signatureVerify(
credentials.message,
credentials.signature,
credentials.address,
);
if (!isValid) {
return Promise.reject(new Error('🚫 Invalid Signature'));
// verify the account has the defined token
const wsProvider = new WsProvider(process.env.RPC_ENDPOINT ?? 'https://xxnetwork-rpc.dwellir.com');
const api = await ApiPromise.create({ provider: wsProvider });
if (credentials?.address) {
// AA: encode wallet address for ss58Format 55
const ksmAddress = encodeAddress(credentials.address, 55);
console.log("ksmAddress: ", ksmAddress);
const accountInfo = (await api.query.system.account(ksmAddress)) as any;
const balance = accountInfo.data.free.add(accountInfo.data.reserved);
// AA: write ksmAddress for xx Network to console
console.log('Wallet address on xx Network: ', ksmAddress);
console.log('Wallet balance on xx Network: ', balance.toString());
// AA: asset balance check
const assetId = 5; // AA: Replace with your asset ID
const accountAssetInfo = await api.query.assets.account(assetId, ksmAddress);
// AA: get Token-Asset Config
console.log('Token-asset config in [...nextauth].ts: ', tokenAssetConfig.TAConfig);
// AA: find nested 'id' in tokenAssetConfig.TAConfig.entries() and compare with assetID value
for (const [token, levels] of tokenAssetConfig.TAConfig.entries()) {
for (const [level, value] of Object.entries(levels)) {
if (value === assetId) {
console.log('Token-asset config entry: ', token, level, value);
console.log('Token-asset config entry found: ', token, level, value);
}
}
}
if (accountAssetInfo.isEmpty) {
console.log(
`No balance found for asset ${assetId} and address ${ksmAddress}`
);
} else {
assetBalance = (accountAssetInfo.toHuman() as { balance: string }).balance ? parseInt((accountAssetInfo.toHuman() as { balance: string }).balance) : 0;
console.log(
`Account balance for asset ${assetId} and address ${ksmAddress}:`,
assetBalance
);
if (assetBalance < 2) {
console.warn(`Warning: Account balance for asset ${assetId} is less than 2.`);
}
};
console.log('Asset balance going into returned object: ', assetBalance.toString());
if (accountInfo.data.free.gt(new BN(1_000_000_000)) || assetBalance >= 1) {
// if the user has a free balance > 1 XX or JUNK !> 0, we let them in
return {
id: credentials.address,
name: credentials.name,
freeBalance: accountInfo.data.free,
ksmAddress,
};
} else {
return Promise.reject(new Error('🚫 The gate is closed for you'));
}
// highlight-end
return Promise.reject(new Error('🚫 API Error'));
// maxAge: 3, // uncomment to test session expiration in seconds
},
callbacks: {
async jwt({ token, user }) {
if (user) {
session.assetBalance = token.assetBalance as string | undefined;
session.ksmAddress = encodeAddress(session.address, 55);
}
// as we already queried it, we can add whatever token to the session,
// so pages can use it without an extra query
session.freeBalance = token.freeBalance as BN;
return session;
pages: {
signIn: '/',
signOut: '/',
error: '/',
newUser: '/',
},