2

This might be a stupid question, but I am a noob to this. I am wondering if the creator/owner of a smart contract can choose to reject a payment they receive. If so - how does the owner of the contract interact identify themselves to the contract. I mean how do they securely interact with the contract in a way that no-one else can? Is that possible?

Or can a smart contract be made that, for example, auto refunds any payments sent to it after x days - unless the owner of the contract accepts them somehow?

4 Answers 4

4

If I get you correctly, you want to specify a owner to a contract and wish if some functions could be called only by owner and no one else.

This is pretty possible. You need to use following lines of code in your contract.

 contract owned {
    address public owner;

    function owned() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _;
    }

    function transferOwnership(address newOwner) onlyOwner {
        owner = newOwner;
    }
}

Explanation:

The function owned() is the constructor of contract, there you specify the owner to be the address from which the contract is deployed.

The onlyOwner modifier checks if the function is called by the owner of contact, else it willthrow.

The next function is transferOwnership, this just sets owner to be a different address, in case you wish to transfer ownership of contract to a different address. This function is optional.

You can use a function like:

function approveATransaction() onlyOwner{
 // only the owner can call this function
 // Logic to accept or reject a payment
}
1
  • that's not what he was asking, He wants hus contract to refuse ether
    – errata
    Feb 5, 2020 at 19:06
2

While all the other answers go into some interesting details of how to handle the normal flows, there is no way that your contract could prevent against getting Ether in the following: contractA is in possession of Ether. It sends it to your contractB in a selfdestruct(contractB) instruction. In this case there is no way to reimburse the Ether or send them back to contractA (since the originally sending entity is not active anymore). This is likely an edge case of a user knowingly forcing Ether into your contract but should be considered in any case.

1

I thought about that usecase before so I made a small contract "Payment Gateway" that receives a "pending" transaction into the contract. The owner can accept or refund the payment. The contract compiles, I made some quick tests but I'm pretty sure I forgot some cases and safe check:

pragma solidity ^0.4.11;

contract PaymentGateway {

    //***********************
    //* Structure and enums     
    //*
    //***********************/

    enum State {
        PENDING,
        ACCEPTED,
        REFUNDED
    }

    struct Transaction {
        uint            id;
        address         from;
        uint256         amount;
        State           state;
        uint            date;
    }

    //***********************
    //* Data                
    //*
    address public owner;
    uint  constant AUTO_REFUND_AFTER = 5 days;
    uint public sequence;
    uint public nbPendingTrasanction = 0;
    mapping(uint => Transaction) db;
    uint256 pendingVolume = 0;
    uint256 availableVolume = 0;
    //***********************/


    //***********************
    //* Modifier    
    //*

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _;
    }
    //***********************


    //***********************
    //* Events      
    //*
    event received_payment(uint id, address from, uint amount, State state, uint date);
    //***********************/


    //***********************
    //* Constructor    
    //*
    function PaymentGateway() {
        owner = msg.sender;
        sequence = 0;  
    }
    //***********************/


    //***********************
    //* Getter   
    //*

    /**
     * @dev Return the list of pending transactions
     * @return array of transaction ID
     */
    function getPendingTransactions() constant returns (uint[]) {

        if(nbPendingTrasanction == 0) {
            return new uint[](0);
        }

        uint[] memory policyIDArray  = new uint[](nbPendingTrasanction);

        uint index = 0;
        for (var id = 0; id < sequence ; id++) {
            Transaction memory t  = db[id];

            if(t.state == State.PENDING) {
                policyIDArray[index] = t.id;
                index++;
            }
        }

        return (policyIDArray);
    }

    /**
     * @dev Return a transaction details
     * @param _id Transaction ID
     * @return ID
     * @return from
     * @return amount
     * @return state
     * @return date
     */
    function getTransactionDetail(uint _id) constant returns (uint, address, uint256, State, uint) {
        return (db[_id].id, db[_id].from, db[_id].amount, db[_id].state, db[_id].date);
    }
    //***********************/


    //***********************
    //* Public functions    
    //*

    /**
     * @dev Receive a payment into the gateway
     */
    function receivePayment() payable {

        Transaction memory t; 
        t.id             = sequence;
        t.from           = msg.sender;
        t.amount         = msg.value;
        t.state          = State.PENDING;
        t.date           = now;

        // Store the transaction
        db[t.id]  = t;

        // Increment sequence
        sequence++; 

        // Increment the no of pending transactions
        nbPendingTrasanction++;

        // Add the amount to the pending volume
        pendingVolume += msg.value; 

        // Trigger event
        received_payment(t.id, t.from, t.amount, t.state, t.date);
    }

    /**
     * @dev Accept a payment transaction and tag it as accepted
     * @param _id Transaction ID
     */
    function acceptPayment(uint _id) onlyOwner {
         db[_id].state = State.ACCEPTED;

        //TODO Safe check
        // Move the amount to the available volume
        availableVolume += db[_id].amount; 
        pendingVolume -= db[_id].amount; 


        // Decrement the no of pending transactions
        nbPendingTrasanction--;
    }


    /**
     * @dev Refund a payment transaction
     * @param _id Transaction ID
     */
    function refundPayment(uint _id) onlyOwner {

        // Check if the transaction amount is greater than 0
        require(db[_id].amount != 0);

        // Check if the contract balance is greater than the amount asked to refund
        require(this.balance >= db[_id].amount);

        // Change the state to REFUNDED
        db[_id].state = State.REFUNDED;


        //TODO Safe check
        // Remove the fund from the pending volume
        pendingVolume -= db[_id].amount;

        // Decrement the no of pending transactions
        nbPendingTrasanction--;

        // Execute the payment refund
        assert(db[_id].from.send(db[_id].amount));
    }

    /**
     * @dev Allows the owner to transfer funds
     * @param _to The address to transfer funds
     * @param _amount Amount to transfert
     */
    function transfer(address _to, uint256 _amount) onlyOwner {
         //TODO Transfer the fund to another address
    }


    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param _newOwner The address to transfer ownership to.
     */
    function transferOwnership(address _newOwner) onlyOwner {
        owner = _newOwner;
    }

    //***********************/



    //***********************
    //* Private functions       
    //*

    //***********************/


}

For information, that doesn't include the automated refund after X days. For that, I recommend to use an off-chain system (scheduler) that execute a transaction every day for example. Or use Ethereum Alarm Clock

I pushed the code in my Github: https://github.com/gjeanmart/stackexchange/tree/master/22790-can-a-smart-contract-owner-choose-to-reject-or-accept-a-transaction

0

To immediately reject a payment, you can use require(), or its relatives revert(), assert(), and the now-deprecated throw. This rolls back the entire transaction^1.

In fact, by default all contracts reject payments. A function must have the payable modifier to accept funds, or it will automatically revert.

Here's an example of example of require():

function purchase() payable {
    require(msg.value >= 1 ether);
    // ...actually sell the product.
}

If you need for the owner to decide whether to accept a payment sometime after the transaction happens, you'll need to look at one of the other examples.

1: Technically, it only reverts the current message call, but solidity helpfully unwinds the entire call stack under normal circumstances.

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.