Skip to content

Instantly share code, notes, and snippets.

@jtriley-eth
Created January 25, 2022 17:21
Show Gist options
  • Save jtriley-eth/39b36b54c32066d7d6f3303042ab26d6 to your computer and use it in GitHub Desktop.
Save jtriley-eth/39b36b54c32066d7d6f3303042ab26d6 to your computer and use it in GitHub Desktop.
Closing Flows on a User's Behalf with Meta Transactions

MetaStreamCloser

Abstract

The MetaStreamCloser contract allows a user to provide a digital signature, giving permission to a third party to close streams on the user's behalf. A stream's sender or receiver may propmt the contract for a message to sign, the message being a deleteFlow function call. By generating a signature and sending it to a third party, that third party has access to explicitly delete the unique flow of a token between the sender and receiver exactly once.

Message Forwarding

At the time of writing, Superfluid has approved a single contract on each network as a "trusted forwarder". The trusted forwarder is a smart contract that verifies an EIP712 digital signature and, if the signature is valid, executes the funcion call on behalf of the signing party, regardless of the message sender.

Message Generation

The message must be generated in accorandance with the BiconomyForwarder's ERC20ForwardRequest.

The message is a sha3 hash of a set of data with abi.encodePacked. Here is a soldity example of the message being generated.

bytes32 hash = keccak256(
    abi.encodePacked(
        senderAddress,
        superfluidHostAddress,
        tokenAddress, // not applicable
        callAgreementGas,
        tokenGasPrice, // not applicable
        batchId,
        batchNonce,
        deadline,
        keccak256(callAgreementCallData)
    )
);

In this case, the tokenAddress and tokenGasPrice refers to meta transactions that may require compensation paid in a non-native token. This protocol does not require this, but may implement it in the future.

Signature Generation

The digital signature is to be generated off-chain using the personal_sign API.

An example of this with web3.js is as follows.

const Web3 = require('web3')
const web3 = new Web3()

const privateKey = process.env.PRIV_KEY
const messageHash = '0x' // data returned from contract

function main() {
    const { signature } = web3.eth.accounts.sign(messageHash, privateKey)
    console.log(signature)
}

Sending Transaction and Signature Data Over the Wire

Since the signed data allows any address to call this function once, this opens the stream sender to a one-time grief attack. This allows an attacker to delete the user's flow exactly once at any time if they gain access to the digital signature. However, this signature is generated off-chain, and can be encrypted to be sent to a trusted third party for secure storage.

The SDK should immediately take the digital signature as an inoput and encrypt it with a public key, allowing only the owner of the decryption key to decrypt the digital signature and execute the flow deletion on the flow sender's behalf.

Testing

Testing is difficult, given the nature of the Biconomy contracts, but this has been successfully executed on the Goerli test network. More testing to come.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment