Sustainability Proofs

As the x2Earn ecosystem continues to evolve, developers must provide verifiable proof each time they reward a user. This requirement enhances transparency, trust, and accountability within the ecosystem. Below, we outline the standardized process for developers to follow when distributing rewards.

You can technically avoid providing proof, but this will have a bad impact on your reputation and could lead to expulsion from VeBetterDAO.

The proof will be stored on-chain in the form of an emitted event and will have the following JSON format:

{
   version: 2,
   description: 'The description of the action',
   proof: { 
      image: 'https://image.png', 
      link: 'https://twitter.com/tweet/1' 
   },
   impact: { 
      carbon: 100, 
      water: 200 
   }
}

Proofs

You can attach proof of the user's sustainable action when you distribute the reward. The proof can be a link, video, photo, and text. You can provide more than one proof (eg: a photo and a link).

The current proof types supported are:

"image"
"link"
"text"
"video"

Impacts

Determining the environmental impact of each sustainable action will require the development of a set of criteria and metrics for each type of action.

Categories

  • Carbon Footprint Reduction: Measures the decrease in greenhouse gas emissions. Metric: g of CO2 equivalent reduced or removed.

  • Resource Conservation: Assesses the reduction in the use of natural resources like water, electricity, and raw materials. Metric: milliliters of water saved, wh of electricity saved.

  • Waste Reduction: Evaluate the decrease in waste generated and improve waste management. Metric: g of waste diverted from landfills, number of items recycled.

  • Timber Conservation: Measures the reduction in the use or waste of timber resources.

    Metric: grams of timber saved.

  • Plastic Reduction: Evaluate the decrease in the use or waste of plastic materials.

    Metric: grams of plastic saved or reduced.

  • Education Time: The amount of seconds that a user spends learning about a sustainability topic

  • Trees Planted: The amount of trees that were actually planted

The following are the impact codes you will need to use:

"carbon"
"water"
"energy"
"waste_mass"
"education_time",
"timber"
"plastic"
"trees_planted"

We ask each app to find a way to calculate the impact they are having based on the previously listed categories. If your app does not fit in any of the above categories please feel free to submit your own category and impact definition.

Integration

To provide proofs and impacts you need to distribute the rewards through the X2EarnRewardsPool contract with the following function:

 /**
  * @dev Function used by x2earn apps to reward users that performed sustainable actions.
  *
  * @param appId the app id that is emitting the reward
  * @param amount the amount of B3TR token the user is rewarded with
  * @param receiver the address of the user that performed the sustainable action and is rewarded
  * @param proofTypes the types of the proof of the sustainable action
  * @param proofValues the values of the proof of the sustainable action
  * @param impactCodes the codes of the impacts of the sustainable action
  * @param impactValues the values of the impacts of the sustainable action
  * @param description the description of the sustainable action
  */
 function distributeRewardWithProof(
     bytes32 appId,
     uint256 amount,
     address receiver,
     string[] memory proofTypes, // link, photo, video, text, etc.
     string[] memory proofValues, // "https://...", "Qm...", etc.,
     string[] memory impactCodes, // carbon, water, etc.
     uint256[] memory impactValues, // 100, 200, etc.,
     string memory description
 ) external;

Apart from the usual appId, amount, and receiver fields you have 5 additional parameters that will allow the X2EarnRewardsPool contract to build the JSON proof and emit it through an event. Those parameters are:

  • proofTypes: an array with the proof types that you are providing. Eg: ["link", "image"]

  • proofValues: an array with the values of the types provided in the previous array. Eg: ["https://twitter.com/tweet/1233121231", "https://whatever.com/image.png"]

  • impactCodes: an array with the codes of impacts you are providing. Eg: ["water", "timber"]

  • impactValues: an array with the values of the impacts provided in the previous array. Eg: [1000, 23]

  • description: you can attach also a description to your action, it's optional.

When calling this function it is mandatory to provide at least proof or impact, if none of them is provided then the transaction will revert.

The transaction will revert also if something is wrong with the data you pass.

Warning: the impact values MUST be a number and the unit is considered the default minimum (so milliliters, grams, wh, etc.). So if you saved 1 litter of water then you should put it as an impact of "water" : "1000".

Examples

Solidity

1) Import the latest interface

import "./interfaces/IX2EarnRewardsPool.sol";

2) Distribute the rewards with proof

Example of providing both proof and impact

// this declaration is needed because solidity has 
// an issue with dynamic vs fixed-size arrays
string[] memory proofTypes = new string[](1);
proofTypes[0] = "link";

string[] memory proofUrls = new string[](1);
proofUrls[0] = action.proofUrl;

string[] memory impactTypes = new string[](1);
impactTypes[0] = "waste_mass";

uint256[] memory impactValues = new uint256[](1);
// let's pretend you have another function that calculates your impact
impactValues[0] = calculateWasteMass(
    challenge.litterSize,
    challenge.litterCount
);

IX2EarnRewardsPool x2EarnRewardsPool = IX2EarnRewardsPool(x2EarnRewardsPoolAddress);

// If distributeReward fails, it will revert
x2EarnRewardsPool.distributeRewardWithProof(
    VBD_APP_ID,
    rewardAmount,
    receiver,
    proofTypes,
    proofUrls,
    impactTypes,
    impactValues,
    "User participated in a solo cleanup"
);

Example of providing only proof with no impact

// this declaration is needed because solidity has 
// an issue with dynamic vs fixed-size arrays
string[] memory proofTypes = new string[](1);
string[] memory proofValues = new string[](1);
string[] memory impactCodes = new string[](0);
uint256[] memory impactValues = new uint256[](0);

proofTypes[0] = "link";
proofValues[0] = action[actionId].proofUrl;

IX2EarnRewardsPool x2EarnRewardsPool = IX2EarnRewardsPool(x2EarnRewardsPoolAddress);

// If distributeReward fails, it will revert
x2EarnRewardsPool.distributeRewardWithProof(
    VBD_APP_ID,
    amountToClaim,
    receiverAddress,
    proofTypes,
    proofValues,
    impactCodes,
    impactValues,
    "User participated in a cleanup campaign."
);

Javascript + SDK

As a first thing get the ABI of the X2EarnRewardsPool contract from here.

import {
  ThorClient,
  VeChainProvider,
  ProviderInternalBaseWallet,
  VeChainSigner,
} from "@vechain/sdk-network";

// Prepare the SDK
const thor = ThorClient.fromUrl("https://mainnet.vechain.org");
const rootWallet = Wallet.fromPhrase(mnemonic);
const rootAccount = {
  privateKey: Buffer.from(rootWallet.privateKey.slice(2), "hex"),
  address: rootWallet.address,
};
const provider = new VeChainProvider(
  thor,
  new ProviderInternalBaseWallet([rootAccount])
);
const rootSigner = await provider.getSigner(rootAccount.address);

// Call the method
const distributeRewardFragment = new fragment.Function(
  ethers.FunctionFragment.from(distributeRewardWithProofAbi),
);

const clause = [
  {
    clause: {
      to: x2EarnRewardsPoolContractAddress,
      value: "0x0",
      data: new Interface(X2EarnRewardsPool.abi).encodeFunctionData("distributeRewardWithProof", [
        APP_ID, 
        amount, 
        receiverAddress, 
        ["link", "image"], 
        ["https://link-to-proof.com", "https://link-to-image.com/1.png"], 
        ["waste_mass"], 
        [100], 
        "User performed a sustainable action on my app"
      ]),
    },
    functionFragment: coder
      .createInterface(X2EarnRewardsPool.abi)
      .getFunction("distributeRewardWithProof") as FunctionFragment,
  },
]

await (
  await thor.contracts.executeMultipleClausesTransaction(
    clause,
    rootSigner
  )
).wait();

Deprecated template (version 1)

If no "version" field is found in the the json proof then you should consider it as version 1.

{
  "app_name": "cleanify",
  "action_type": "litter_picking",
  "proof": {
    "proof_type": "link",
    "proof_data": "https://x.com/TengMab95659/status/1814152547202195788"
  },
  "metadata": {
    "description": "@TengMab95659 picked up 8 small pieces of litter.",
    "additional_info": ""
  },
  "impact": {
    "waste_mass": "300",
    "biodiversity": "1"
  },
}

Deprecated impacts:

waste_items
people
biodiversity

Last updated