Zilliqa / ZRC

Zilliqa Reference Contracts
MIT License
40 stars 57 forks source link

ZRC - Introduce batching & "MetaClaim" (abstracting Transfer) #191

Closed henry-wrightman closed 1 year ago

henry-wrightman commented 2 years ago

Proposed extensions & changes to ZRC

Currently, the ZRC3 standard MetaFungible primarily contains "Cheques" as a means of transfering tokens (primarily abstracting TransferFrom). I propose the extension - namely Claim, to compliment ChequeSend by abstracting Transfer. This enables the means of meta-transfering tokens, implicitly gated & authorized by the _sender (as Transfer typically operates), through a hot-wallet or relayer for token distribution. An example could be rewarding users for tasks, or any other forms of remittance.

Its worth noting that through this implementation, the recipient does not require any pre-existing tokens prior, as the fee is paid secondary to receiving the initial claim; unless of course if fee exceeds the claim amount, it will subsequently fail due to insufficient ZRC2 balance.

10. Claim

(* @dev: Enables a cheque signer to recieve/claim tokens from the `_sender` or relayer, while paying for the relayer's gas fee. *)
(* @dev: Balance of recipient will increase. Balance of `sender_ will decrease.                                                 *)
(* @param pubkey:      Public Key of the token_owner whose balance is decreased.                                                *)
(* @param to:          Address of the recipient whose balance is increased.                                                     *)
(* @param amount:      Amount of tokens to be sent.                                                                             *)
(* @param fee:         Reward taken/minings fee from the signer's balance to the relayer.                                       *)
(* @param nonce:       A random value included in the cheque to make each unique.                                               *)
(* @param signature:   The signature of the cheque by the recipient to authorize paying a fee to receive.                       *)
transition Claim(pubkey: ByStr33, to: ByStr20, amount: Uint128, fee: Uint128, nonce:Uint128, signature: ByStr64)
  from = extract_from pubkey;
  cheque_hash = hash_cheque to amount fee nonce signature _this_address;
  IsChequeValid cheque_hash;
  IsValidSignature pubkey cheque_hash signature;

  AuthorizedMoveIfSufficientBalance _sender to amount;
  AuthorizedMoveIfSufficientBalance from _sender fee;

  void_cheques[cheque_hash] := _sender;
  e = {_eventname : "ClaimSuccess"; initiator : _sender; sender: _sender; recipient : to; amount : amount; fee : fee};
  event e;
  (* Prevent sending to a contract address that does not support transfers of token *)
  msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 
                      sender : _sender; recipient : to; amount : amount};
  msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 
                  sender : _sender; recipient : to; amount : amount};
  msgs = two_msgs msg_to_recipient msg_to_sender;
  send msgs
end

Arguments:

Name Type Description
@param pubkey ByStr33 Public Key of the token_owner whose balance is to decrease.
@param to ByStr20 Address of the recipient whose balance is to increase.
@param amount Uint128 Amount of tokens to be sent.
@param fee Uint128 Reward taken/minings fee from the signer's balance to the relayer.
@param nonce Uint128 A random value included in the cheque to make each unique.
@param signature ByStr64 The signature of the cheque by the recipient to authorize paying a fee to receive.

Messages sent:

Name Description Callback Parameters
_tag RecipientAcceptChequeSend Dummy callback to prevent invalid recipient contract. initiator: ByStr20, sender : ByStr20, recipient: ByStr20, amount: Uint128, where initiator is the address of an operator,sender is the address of the token_owner, recipient is the address of the recipient, and amount is the amount of fungible tokens to be transferred.
_tag ChequeSendSuccessCallBack Provide the relayer the status of the transfer. initiator: ByStr20, sender : ByStr20, recipient: ByStr20, amount: Uint128, where initiator is the address of an relayer,sender is the address of the token_owner, recipient is the address of the recipient, and amount is the amount of fungible tokens to be transferred.

Events/Errors:

Name Description Event Parameters
_eventname ClaimSuccess Claim is successful. initiator: ByStr20 which is the _sender's address, sender: ByStr20 which is the token_owner's (and _sender's) address, recipient: ByStr20 which is the recipient's address, and amount: Uint128 which is the amount of fungible tokens to be transferred.
_eventname Error Sending is not successful. - emit CodeChequeVoid if the cheque submitted has already been transferred.
- emit CodeInsufficientFunds if the balance of the token_owner is lesser than the specified amount that is to be transferred.
- emit CodeSignatureInvalid if the signature of the cheque does not match the cheque parameters.
- emit CodeInvalidSigner if the signer of the metatransaction is not the owner of the tokens to be moved.

Additionally, via batching to reduce gas cost for larger volume:

transition BatchClaim(cheques: List (Pair (Pair (Pair ByStr33 ByStr20) (Pair Uint128 Uint128)) (Pair Uint128 ByStr64)))
  (* construct cheque list *)
  c =
    let map = @list_map (Pair (Pair (Pair ByStr33 ByStr20) (Pair Uint128 Uint128)) (Pair Uint128 ByStr64)) Cheque in
    map map_fn cheques;

  (* validate & distribute credits batched *)
  forall c ValidateAndClaim
end
transition BatchChequeSend(cheques: List (Pair (Pair (Pair ByStr33 ByStr20) (Pair Uint128 Uint128)) (Pair Uint128 ByStr64)))
  c =
    let map = @list_map (Pair (Pair (Pair ByStr33 ByStr20) (Pair Uint128 Uint128)) (Pair Uint128 ByStr64)) Cheque in
    map map_fn cheques;

  (* validate & cash all cheques batched *)
  forall c ValidateAndCashCheque
end