Let's take a look at the verification process. As well as it can be requested from the App, a verification can also be asked directly from the VerifyTwitter contract. It will only take a username as an input, and grab the Ethereum address from the interacting user.
Parameter
Example input
Info
_username
bob
A string representing the Twitter handle.
The @ should not be provided.
_userAddress
❌
The user approving the transaction - the address can't be supplied, as it will grab it from the transaction.
How does the EA perform the verification?
The External Adapter is written as a serverless function. Each time it is triggered with a request, the API server grabs the input parameters, performs the custom computation, and sends back its result (or an error, if anything happens in between). The full code is available here. Let's take a look at what occurs inside the scope of the createRequest function ; you will find comments directly in the code, to explain the process.
index.js
// The Twitter API client is initialized, using a Bearer Token// It is a read-only keyconstclient=newTwitterApi(process.env.BEARER_TOKEN);constroClient=client.readOnly;// Custom parameters will be used by the External Adapter// true: the parameter is required, if not provided it will// throw an error// false: the parameter is optionalconstcustomParams= { username:true, address:true, endpoint:false,};constcreateRequest= (input, callback) => {// The Chainlink Validator helps validate the request dataconstvalidator=newValidator(callback, input, customParams);constjobRunID=validator.validated.id;// Validate input parameters// The username specified by the user in the request parametersconstusername=validator.validated.data.username ||'TwitterDev';// The Ethereum address of the user, grabbed when they// interacted with the contract (msg.sender)constaddress=validator.validated.data.address ||'false';// Specify the signature it should find in the tweetsconstsignature=`Verifying my Twitter account for ${address} with @usePromise!`;// 1st STEP// Get the user's ID from their usernameroClient.v2.userByUsername(username).then((preRes) => {// If the username doesn't exist, return early// We don't want to throw an error by trying to read an// undefined user tweets ; here, it will just return// as if the user could not be verifiedif ( (preRes.errors &&preRes.errors[0].title.includes('Not Found')) ||!preRes.data ) {constresponse= { data: { username: username, result:false, address, }, jobRunID, status:200, };// Return that data to the Chainlink Nodecallback(response.status,Requester.success(jobRunID, response)); } else {// 2nd STEP// If the user was found, we can proceed// Get their 10 latest tweetroClient.v2.userTimeline(preRes.data.id, { max_results:10, exclude: ['retweets','replies'], })// Then check if their 10 latest tweets include the signature.then((res) => {// If we are in development, use mocks instead of the actual tweets// It allows us to test for a successful/failed signatureconsttweets=process.env.DEVELOPMENT==='true'? mockTweets :res.data.data;// In each one of the tweets, check if the signature is presentconstresult=// Make sure there are tweets!!tweets &&// Check each one of the tweets, to see if it includes the signature// (use lower case to ignore address case irregularities)tweets.some((tweet) =>tweet.text.toLowerCase().includes(signature.toLowerCase()), );// Pass the result to the responseconstresponse= { data: {// Either true, if the signature was found, otherwise false result: result, username:preRes.data.username,// Return the address to be able to notify the user// that the process is complete address, name:preRes.data.name, }, jobRunID, status:200, };// Then return the response data to the Chainlink nodecallback(response.status,Requester.success(jobRunID, response)); }).catch((error) => {console.log(error);callback(500,Requester.errored(jobRunID, error)); }); } }).catch((err) => {console.log(err);callback(500,Requester.errored(jobRunID, err)); });};
Resources
External Adapter
Contracts
The process following the request is described in the preceding section (). We will now further investigate the way in which the External Adapter operates.