X-to-Earn Design Pattern
X to Earn


It's now common practice to reward your users/fans/following for positive behavior. Game-to-earn, Social-to-earn and even Step-to-earn takes only minutes to set up on XP Protocol.
The key to this design pattern is in the Idompotency of the updateId parameter of the updateScore() method.

Repeatable Actions

Great for use with "Liked", "Saved", and "Posted". Types of Actions that a user should perform regularly
Below is a great example of an updateId for an action that a user will do often. Notice how the string remains static except for the last edition of the unix time.
The Unix time in milliseconds attached to the end of the string will ensure that this updateId will always change per each time a user performs that action.
Let updateId = "<actionName>:<non-checksum-address>:<UnixTimeSinceEpoch>"

Full Example

let myHeaders = new Headers();
let message =;
let signatureObject = await web3.eth.accounts.sign(message, <PRIVATE_KEY>); //Private key of a wallet that is either an Updater or an Owner of the current project.
let signature = signatureObject.signature;
myHeaders.append("Content-Type", "application/json");
let targetAddress = "0xa6869c3001de171c26418e9e2eedca1c0d763ee2";
let updateId = "liked:"+targetAddress+":";
updateId = '0x' + sha256(updateId).toString(); // 0x to signify its a bytes32
//Send to API
const body = JSON.stringify({
"updateId": updateId,
"projectId": "0xadb591795f9e9047f9117163b83c2ebcd5edc4503644d59a98cf911aef0367f8",
"actionName": "liked",
"scoreType": "social",
"targetWallet": targetAddress,
"signature": signature,
"message": message
const requestOptions = {
method: 'POST',
headers: myHeaders,
body: body,
redirect: 'follow'
let response = await fetch(', requestOptions).then(async (response) => ({ status: response.status, value: await response.text() }));

Semi-Repeatable Actions (time-based)

Sometimes you will also want to reward your users for time based actions, such as a "Daily Login".
Typically as a developer you would have to track each day that your users log in and ensure that you do not accidently give them that reward more than once in a 24h period.
Because XP has built-in Dedupe capabilities, you no longer need to keep track of that.
This can be achieved with "Semi-Repeatable Actions", this design again simply relies on how we construct the updateId.
let dateNow = new Date();
let dayNumberSinceEpoch = (Math.floor(dateNow / 8.64e7)).toString();
let updateId = '0x' + await sha256('dailyLogin:'+ targetAddress + ':' + dayNumberSinceEpoch);
Notice how the dayNumberSinceEpoch will change every 24h. This allows you as a developer to not worry about storing and tracking the reward, you can simply let an updateScore() transaction fire upon a "Login" event on your platform, and know that it will only be counted toward that user's score every 24h.