Token Subscription (MetaTx) Community Audit

This audit was performed upon the commit 9ff9deb by Dean Eigenmann, it was done as part of the ZK Labs community audit initiative.

The code is kept simple & concise, there is no unnecessary complexity found in the smart contract.

Flow Graph

Contract Intent

The Token Subscription (MetaTx) project enables applications to create a trustless subscription model without their users needing to constantly and manually transfer tokens. It consists of one contract, the Subscription contract, which is created with the parameters for a subscription including destination address, token address, token amount, and period of recurrence. The publisher then supplies a link to the subscriber that is presented with the terms of the subscription to sign an off-chain meta transaction that is replayed on the defined period. The subscriber controls the flow of the tokens (starting, stopping, pausing, etc) using the ERC20 standard approve() function.

More information can be found through the website.

Tests

There does not seem to be any tests covering the smart contracts, these types of tests should be created.

Subscription.sol

  • ECDSA is used for all bytes32 variables.
  • SafeMath is used for all uint256 variables.

Suggestions

  • Move mapping on L72 to there where rest of the variables are defined.
  • Move constructor to the rest of the function definitions.
  • Stick to a consistent code style.
  • Make all functions that need not be called by the contract itself external.
  • Add a nonce field to subscription signatures. Making it possible to reopen cancelled subscriptions.

Constructor

This function sets various fields: - requiredToAddress - requiredTokenAddress - requiredTokenAmount - requiredPeriodSeconds - requiredGasPrice - author

isSubscriptionActive

This function returns whether the nextValidTimestamp for a given subscription with a gracePeriod added to it is smaller than or equal to the current timestamp.

getSubscriptionHash

This function packs the passed variables using the abi.encodePacked function and then proceeds to hashing those bytes using the keccak256 function.

getSubscriptionSigner

This function recovers the signer of a given message using the recover function found in the ECDSA library.

isSubscriptionReady

This function checks multiple things, such as: - That the signer of the message is equal to the from address. - That the from address is not equal to the to address. - That the current time is greater than or equal to the nextValidTimestamp for the given subscription. - Ensures that the token allowance is greater than the tokenAmount added with the gasPrice. - Ensures that the token balance is greater than the tokenAmount added with the gasPrice.

cancelSubscription

First the subscription hash is generated by the passed variables using the getSubscriptionHash function. The signer is then recovered using the recover function found in the ECDSA library. It is then ensured that the signer is equal to the value of the passed from variable. Finally the nextValidTimestamp for the given subscription is set to 2**256 - 1.

Suggestions

  • Instead of doing the whole recover call, the getSubscriptionSigner function can be called as it has been in other functions.

executeSubscription

Initially the subscription hash is generated using the getSubscriptionHash function. Then the signer is recovered using the getSubscriptionSigner function. Next it is ensured that the signer is equal to the passed from address. It is then ensured that the current timestamp is greater than or equal to the value of the nextValidTimestamp for the given subscription.

Several checks are then run: - It is ensured that the requiredToAddress is either equal to 0x0 or the passed to variable. - It is ensured that the requiredTokenAddress is either equal to 0x0 or the passed tokenAddress variable. - It is ensured that the requiredTokenAmount is either equal to 0 or the passed tokenAmount variable. - It is ensured that the requiredPeriodSeconds is either equal to 0 or the passed periodSeconds variable. - It is ensured that the requiredGasPrice is either equal to 0 or the passed gasPrice variable.

The value of the nextValidTimestamp for the given subscription is then set to the current timestamp added to the periodSeconds variable.

Tokens are then transfered from the from address to the passed to address using the transferFrom function. It is ensured that tokens were transfered successfully by adding the balance of the to address before the transfer to the transfered amount and ensuring that said value is equal to the new balance of the to address. The ExecuteSubscription event is then emitted.

Finally is the value of gasPrice is greater than zero, the value is transfered to the function caller from the from address.

Issues

  • The require on L233-236 will not work for every token. See here.
  • Should this function not call isSubscriptionReady?