Connect a Smart Contract to the Twitter API
Social media platforms like Twitter are rich with data on what people across the globe find important and worth discussing. We’re moving into a world increasingly driven by such data and Chainlink provides the opportunity to securely connect this vast dataset to the on-chain world, bringing the reliability and transparency guarantees of smart contracts to our social media lives.
In this walkthrough, we’ll cover how to get your smart contract connected to the Twitter API. Specifically, we’ll explain how to trigger a tweet from a smart contract, but the possibilities for data analysis and smart contract executions around social media data are innumerable.
Constructor
pragma solidity ^0.6.12; import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol"; contract ChainlinkTwitter is ChainlinkClient { address private oracle; bytes32 private jobId; uint256 private fee; uint256 public statusCode; //only the contract owner should be able to tweet address payable owner; modifier onlyOwner { require(msg.sender == owner); _; } constructor() public { setPublicChainlinkToken(); oracle = 0x4CF0507fe3236DedDbE6cD18508f35D9b5e16e7C; // oracle address jobId = "948db03c9576480a8fa0545bee5b28ab"; //job id fee = 11 * 10 ** 17; // 1.1 LINK owner = msg.sender; }
We start with the familiar ChainlinkClient import and inheritance. Importing the ChainlinkClient contract exposes all the functions necessary to form a request, submit it to one or more Chainlink nodes, and receive the answer. Simply define the oracle address of your choosing, the job spec ID provided by that node for its Twitter job, and define the fee that node requires to process the request. Additionally, we define the onlyOwner modifier so that only the contract creating address will be able to tweet through this contract.
Tweet Function
//tweets the supplied string function tweet(string memory twt) public onlyOwner{ Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector); //req.add("endpoint", "https://api.twitter.com/1.1/statuses/update.json"); req.add("status", twt); req.add("copyPath", "statusCode") sendChainlinkRequestTo(oracle, req, fee); } //callback function function fulfill(bytes32 _requestId, uint256 _statusCode) public recordChainlinkFulfillment(_requestId) { statusCode = _statusCode; }
Once the contract is constructed, tweeting is very straightforward. The tweet function takes in the string to be tweeted, creates the request struct, and submits it to the defined Chainlink node. In this case, two fields are required in our request struct: “status” and “copyPath”. Status, the tweet we wish to post, is defined as an input in the Twitter External Adapter that the node will be running to fulfill the job. CopyPath however, is part of the default adapters supported by all Chainlink nodes and tells the node how to parse the JSON output. Additionally “endpoint” could be specified as a different Twitter endpoint with different functionality but the adapter defaults to posting a tweet status, making this irrelevant for now.
{ jobRunID: 0, data: { result: 1315380402618499000 }, result: 1315380402618499000, statusCode: 200 }
Above is the example JSON output from the external adapter, as provided on its market.link listing. We want the statusCode of the tweet to confirm it was successful. StatusCode is a first level key in the JSON structure of the output, so the path is simply “statusCode”. If we wished to retrieve “data: {result: }” where result is a second level key, we could use dot notation to specify that path as “data.result”. Further information is provided for Copy and other adapters on the Chainlink Adapters doc page.
With the request containing the tweet and the path to be returned, it’s submitted, processed by the node, and statusCode is returned in the fulfill() callback. This simple request formation and submission is all that’s required from the smart contract side, as most of the heavy lifting is handled by the Chainlink node.
Twitter External Adapter and Node Configuration
On the node side, there are a few things we must do:
- Install and run the Twitter External Adapter
- Create a bridge between the node and adapter
- Create a job spec to use the bridgeAs outlined on the listing for the Twitter adapter, four environment variables defining the Twitter API connection access are required before running the adapter with yarn:
TWITTER_CONSUMER_KEY TWITTER_CONSUMER_SECRET TWITTER_ACCESS_TOKEN_KEY TWITTER_ACCESS_TOKEN_SECRET git clone https://github.com/tweether-protocol/twitter-cl-ea cd twitter-cl-ea yarn yarn start
By default, the adapter listens on port 8080. Make note that if your node and adapter are not running in the same container, or not both native, localhost:8080 will not be the same to them. In this case, when you define the bridge to the node, you would need to specify your docker container’s IP (172.x.x.x) or your host’s local IP (192.168.1.x) according to where your adapter is running. Additionally you could even have the adapter hosted on a separate machine, in which case you would specify its public IP or local IP if it is within the same LAN.
Defining a bridge between your node and adapter is easy. Simply fill out the New Bridge page in your node’s interface with the adapter’s URL (Adapter IP:8080), give it a name, and specify what minimum confirmation and payment you desire.
Now that the adapter is running and connected to your node, we need a job spec defined to use the bridge when it’s called by a smart contract request. This can be performed under the New Job section of the node dashboard. Here we define a JSON spec for what will initiate a job and what tasks will be performed for this job.
In the case of this Twitter adapter, our job spec would look like this:
{ "initiators": [ { "type": "runlog", "params": { "address": "YOUR ORACLE CONTRACT ADDRESS HERE" } } ], "tasks": [ { "type": "twitter-cl-ea", "confirmations": null, "params": { } }, { "type": "copy", "confirmations": null, "params": { } }, { "type": "ethuint256", "confirmations": null, "params": { } }, { "type": "ethtx", "confirmations": null, "params": { } } ], "startAt": null, "endAt": null }
First is the initiator. This simply tells the node which oracle contract to monitor and pick up jobs from. This is the same oracle address that was used earlier in the creation of the requesting smart contract. The request is sent to this oracle on-chain and because the node is monitoring it for job requests, it will kick off this job when the request comes in. More can be found about deploying this oracle contract at the Fulfilling Chainlink Requests doc page.
Next are the tasks. Of course we want our Twitter task—this simply needs to be the name of the bridge to the Twitter adapter. Next we have the copy adapter task defined so that it will process the copyPath in the request, then the ethuint256 adapter to convert the output to unit256, and finally the ethtx adapter to commit the answer back on-chain. At this point, node setup is done. A job ID will be provided when this job spec is created, and it’s this jobID the requesting contract should use when forming its request.
Conclusion
Hopefully, this walkthrough has helped further showcase the potential for Chainlink to connect any API to your smart contract while also explaining some of the configuration necessary for node operators to handle these tasks.
If you’re a developer and want to connect your smart contract to off-chain data and systems, visit the developer documentation and join the technical discussion on Discord. If you want to schedule a call to discuss integration more in-depth, reach out here.
Website | Twitter | Reddit | YouTube | Telegram | Events | GitHub | Price Feeds | DeFi