Twitter account verification with an Ethereum address
Submitted by:
polarzero
Check if a Twitter account belongs to a specific Ethereum address. This example uses the Twitter API to retrieve a user's recent tweets, and checks if they tweeted a specific message containing their address. It provides the arguments and returns the result via Chainlink Functions, which allows for prior validation of the user's ownership of the address via a signature or other method, thus performing a secure and non-intrusive verification.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// https://github.com/polar0/twitter-verifier-chainlink-functions/blob/main/implementation/twitter-verification/functions/Functions-request-source.js
// Get the arguments from the request config
const twitterUsername = args[0]; // e.g. 'TwitterDev'
const ethereumAddress = args[1]; // e.g. '0x1234567890123456789012345678901234567890'
// The string that must be included in the latest tweets of the user for the verification to pass
const requiredStringIncluded = `Verifying my Twitter account for ${ethereumAddress}`;
// How many tweets to check (min 5, max 100)
const MAX_RESULTS = 10;
// Initialize the result to -1 (error)
let result = -1;
// Get the bearer token from the environment variables
if (!secrets.apiKey) {
throw Error(
'TWITTER_BEARER_TOKEN environment variable not set for Twitter API. Get a free one: https://developer.twitter.com/en/docs/authentication/oauth-2-0/bearer-tokens',
);
}
// Don't even try if the username or address is empty
if (!twitterUsername || !ethereumAddress) {
throw Error('Twitter username or Ethereum address is empty');
}
// Prepare the API requests
const twitterRequest = {
// Get the user id from the provided username
userIdByUsername: () =>
Functions.makeHttpRequest({
url: `https://api.twitter.com/2/users/by/username/${twitterUsername}`,
headers: { Authorization: `Bearer ${secrets.apiKey}` },
}),
// Get the latest n tweets from the user (n = MAX_RESULTS)
lastTweetsByUserId: (userId) =>
Functions.makeHttpRequest({
url: `https://api.twitter.com/2/users/${userId}/tweets?max_results=${MAX_RESULTS}`,
headers: { Authorization: `Bearer ${secrets.apiKey}` },
}),
};
// First, request the user id from their username
const idRes = await new Promise((resolve, reject) => {
twitterRequest.userIdByUsername().then((res) => {
if (!res.error) {
resolve(res);
} else {
reject(res);
}
});
});
if (idRes.error) {
throw Error('Twitter API request failed - coult not get user id');
}
// Grab the user id
const userId = idRes.data.data.id || null;
// Let's be extra careful and make sure the user id is not null
if (!userId) {
throw Error('Twitter API request failed - user id is null');
}
// Then, request the latest tweets
const tweetsRes = await new Promise((resolve, reject) => {
twitterRequest.lastTweetsByUserId(userId).then((res) => {
if (!res.error) {
resolve(res);
} else {
reject(res);
}
});
});
if (tweetsRes.error) {
throw Error('Twitter API request failed - coult not get tweets');
}
// It'll only get here if the request was successful
const tweets = tweetsRes.data.data;
const tweetTexts = tweets.map((tweet) => tweet.text);
// Check if any of these tweets include the required string
const res = tweetTexts.some((text) =>
text.toLowerCase().includes(requiredStringIncluded.toLowerCase()),
);
// If it found the string, return 1, otherwise 0
result = res ? 1 : 0;
// `result` can either be:
// - 1 (verified)
// - 0 (not verified)
// - -1 (if by any chance no error was thrown, yet it could not verify)
// Return the result along with the username and address, which can be parsed and split
return Functions.encodeString(
`${result},${twitterUsername},${ethereumAddress}`,
);