Refunds in an ICO for a NEO Blockchain NEP-5 smart contract are tricky.

NEO Smart Economy Blockchain

Let’s start with understanding why you need to support refunds. When you’re invoking a smart contract on NEO to purchase digital assets through a wallet, you’re effectively passing in an asset into a smart contract and, subsequently, invoking a function on the contract. For an ICO, today, the end user passes in NEO to the smart contract, and then the code invokes MintToken. Think of this as a transaction, which is NOT a two-phase commit.

Now, the NEO has been routed to the smart contract, but what happens if the NEO is routed prior to the ICO or after the ICO has ended? Right now, the NEO will remain in the smart contract, and since the ICO is not active an asset token will NOT be issued. Effectively, the end user has routed money, and received nothing in return. This is bad. The end user is not going to be happy. The end user needs to receive their NEO back. They need a refund.

The NEP-5 smart contract specifies an interface that includes support for refunds, but it does not specify an implementation, which means that you have to write the implementation. Ah…

Our refund code works as follows:
1. Whenever a refund is required, we query Storage for the current list of users who need a refund.
2. Turn the refund amount passed into the Refund function, e.g. amount of Neo sent to the contract, into bytes.
3. Create a new_refund byte array to hold the sender bytes concatenated with the char “=” and the refund amount from step 2.
4. Append the new refund byte array to the refund bytes from step 1 using the “@” as the delimiter. Now, we have a list of users and refunds that would look like this:  refund_user1_address=1@refunder_user2_address=5@refund_user3_address=10.
5. Write the new_refund bytes array to Storage.
6. Trigger the RefundEvent so we’re NEP-5 compliant.

   public static void Refund(byte[] sender, BigInteger value) 
            byte[] refund = Storage.Get(Storage.CurrentContext, "refund");
            byte[] sender_value = IntToBytes(value);
            /** Store the value with the = char between the sender and the value, this allows us to split the values
             *  when trying to show the refunds.
            byte[] new_refund = sender.Concat("=".AsByteArray()).Concat(sender_value);            
            if (refund.Length != 0)
                /** Split each entry with a "@" symbol. This will allow us to identify each different refund entry. */
                new_refund = refund.Concat("@".AsByteArray()).Concat(new_refund);
            Storage.Put(Storage.CurrentContext, "refund", new_refund);                                               
            RefundEvent(sender, value);

We are now able to store our refunds in the Storage in our contract. Great! But how do you get this information out of the contract? We added a function called listRefund to the contract that returns the byte_array of the “refund”.  We invoke this listRefund function from the Neo-Gui. You can see the Refund section of the Query Dialog here.

Neo Gui Query Dialog

Neo Gui Query Dialog

You can see the refund code here.