2

I have a contract / dapp that is "pay to play". You send X amount of Eth, and you can play.

I have a price feed working via Chainlink, but I'd like to peg the Eth amount to USD.

Where the contract is now looking like..

uint public price = 0.15 ether;

I'd like to make that more dynamic.

uint public price = <$2.00 USD expressed as Eth>;

I'm not sure if this is even possible, because the price of Eth fluctuates so much. A tx could fire and while in the mempool, the price of Eth could jump and once the tx hits the function, it would be rejected.

function play() external payable returns(bool) {
    require(
      msg.value == price,
      'Please send the correct amount of ETH'
    );

So rather the price following the fluctuations of Eth so closely, perhaps it could change the price every hour or so?

Another idea I had was to make a function that changes the price, available only to the contracts author.

function changePrice(uint memory price) external ownerAddressOnly() {
    this.price = price;
  }

Where I could manually change the price when I choose, without having to redeploy a new contract.

How are operations like typically this handled in Solidity / Chainlink?

2
  • 1
    Did you consider using a stable token like dai? An alternative is to require more ether but return remainder.
    – Ismael
    Dec 25 '20 at 2:47
  • 1
    Yes, I am considering going this route
    – GN.
    Dec 30 '20 at 7:56
1

Just divide by the price of the Chainklink feed. The price feed is stable, and you will get very few rejections when the price feed updates. This way, you could set the cost to things like $2 in terms of ETH.

Let's say ETH is $600, and we want to set the price to $2 in ETH. We then have this:

$600/1 ETH = $2/X ETH

We then just do some algebra and get this:

$2/$600 = X ETH

This way, you'd get a decentralized price for ETH in USD. Then, your UI could just always send the correct amount of ETH at that time.

For Kovan, your contract could look like:

pragma solidity ^0.6.7;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";

contract PayToPlay {
    uint256 public cost;

    AggregatorV3Interface internal priceFeed;

    /**
     * Network: Kovan
     * Aggregator: ETH/USD
     * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331
     */
    constructor() public {
        priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
    }

    function getLatestPrice() public view returns (int) {
        (
            uint80 roundID, 
            int price,
            uint startedAt,
            uint timeStamp,
            uint80 answeredInRound
        ) = priceFeed.latestRoundData();
        return price;// <== feed return is with 8 or 18 decimals to ensure conversion factor
    }

    function play() external payable returns(bool) {

        require(
          msg.value == cost / getLatestPrice(),
          'Please send the correct amount of ETH'
        );
    }
}
1

The centralized workaround is probably too centralized.

Returning to your first idea, you can resolve the mempool/delay/volatility issue by introducing a 2-step account funding process. The user would deposit ETH, then in step 2 spend some with each "move" in the game.

The rate of exchange would be computed when the transaction is mined, and might not be exactly as estimated when the transaction was signed but, subject to sufficient ETH in their account, it will succeed and the accounting will be fair.

Surplusses from over-funded accounts and winnings would be withdrawn when the player wants to leave the game.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.