import { recoverPersonalSignature } from 'eth-sig-util' ; import { Knex } from 'knex' ; import Web3 from 'web3' ; import { AUTO_FOLLOW_TAGS } from 'common/enums' ; import { environment } from 'common/environment' ; import { CryptoWalletExistsError , EthAddressNotFoundError , UserInputError , } from 'common/errors' ; import { getViewerFromUser , setCookie } from 'common/utils' ; import { AuthMode , GQLAuthResultType , MutationToWalletLoginResolver , } from 'definitions' ; const resolver = async ( _ , { input : { ethAddress , nonce , signedMessage , signature } } , context ) => { const { viewer , req , res , dataSources : { userService , atomService , systemService , tagService } , } = context ; if ( ! ethAddress || ! Web3 . utils . isAddress ( ethAddress ) ) { throw new UserInputError ( 'address is invalid' ) ; } const sig_table = 'crypto_wallet_signature' ; const lastSigning = await atomService . findFirst ( { table : sig_table , where : ( builder ) => builder . where ( { address : ethAddress , nonce , } ) . whereNull ( 'signature' ) . whereRaw ( 'expired_at > CURRENT_TIMESTAMP' ) , orderBy : [ { column : 'id' , order : 'desc' } ] , } ) ; if ( ! lastSigning ) { throw new EthAddressNotFoundError ( `wallet signing for "${ ethAddress }" not found` ) ; } console . log ( new Date ( ) , 'lastSigning:' , lastSigning ) ; const verifiedAddress = recoverPersonalSignature ( { data : signedMessage , sig : signature , } ) ; if ( ethAddress . toLowerCase ( ) !== verifiedAddress ) { throw new UserInputError ( 'signature is not valid' ) ; } if ( viewer . id && viewer . token ) { if ( viewer . ethAddress ) { throw new CryptoWalletExistsError ( 'user already has eth address' ) ; } await atomService . update ( { table : sig_table , where : { id : lastSigning . id , } , data : { signedMessage , signature , userId : viewer . id , updatedAt : new Date ( ) , expiredAt : null , } , } ) ; await userService . baseUpdate ( viewer . id , { updatedAt : new Date ( ) , ethAddress : verifiedAddress , } ) ; return { token : viewer . token , auth : true , type : GQLAuthResultType . LinkAccount , } ; } const archivedCallback = async ( ) => systemService . saveAgentHash ( viewer . agentHash || '' ) ; const tryLogin = async ( type = GQLAuthResultType . Login ) => { const { token , user } = await userService . loginByEthAddress ( { ethAddress , archivedCallback , } ) ; setCookie ( { req , res , token , user } ) ; context . viewer = await getViewerFromUser ( user ) ; context . viewer . authMode = user . role as AuthMode ; context . viewer . scope = { } ; await atomService . update ( { table : sig_table , where : { id : lastSigning . id , } , data : { address : ethAddress , signedMessage , signature , userId : user . id , updatedAt : new Date ( ) , expiredAt : null , } , } ) ; return { token , auth : true , type } ; } ; try { return await tryLogin ( GQLAuthResultType . Login ) ; } catch ( err ) { console . error ( new Date ( ) , 'ERROR:' , err ) ; if ( err instanceof EthAddressNotFoundError ) { } else { throw err ; } } const newUser = await userService . create ( { userName : ethAddress , ethAddress : verifiedAddress , } ) ; await userService . follow ( newUser . id , environment . mattyId ) . then ( ( err ) => console . error ( new Date ( ) , 'follow matty failed:' , err ) ) ; await tagService . followTags ( newUser . id , AUTO_FOLLOW_TAGS ) ; return tryLogin ( GQLAuthResultType . Signup ) ; } ; export default resolver ;