[feature] Memoless Transactions

Summary

Memos pass intent to TC about the execution of a tx. But:

  1. Not all wallets support writing memos (ie OP_RETURN on Bitcoin).
  2. Some chains have limited length memos or no support at all. BTC: 80bytes
  3. Some assets don't support memos at all (ERC20).

Supporting memoless transactions allows any wallet in the world to be able to interact THORChain and utilize its protocols, expanding its available market.

This issue introduces "memoless transactions", with two variants available for users.

UX Flows

FLOW1 (amount-registration)

A user goes to a website and creates a quote. They set the FROM asset and TO asset, with an ideal amount they wish to swap. The website is connected to a simple server with a RUNE wallet that "registers" their memo-intent by doing an on-chain transaction, costing ~20c. There will need to be a CAPTCHA and rate-limiting to prevent DOS by bots. Once registered, the memo-intent is encoded with a 5-digit number; which can be queried for correctness. The user then sends their funds directly to a vault with the last 5-digits in the amount and the swap is executed.

If the amount is wrong, TC will not find an associated memo-intent and refund. If the user accidentally sends the amount with a 5-digit code of another user, then that memo-intent will be executed and the funds sent to the other user. The user should double check the amount.

FLOW2 (address-registration)

To tighten execution, a user can optionally specify their FROM address (where they are sending their assets from). The frontend will once again register the memo-intent to TC. The User will be able to query the memo-intent for correctness before sending (FROM address, FROM asset, TO asset, TO address).

If an attacker pre-registers a lot of addresses (frequent swappers), the user will get an error message "Address already registered". If the user skips this error, they will still be able to see the griefing attack displayed to them before sending (the FROM/TO address and FROM/TO amount will not be theirs). The registration expires after 24hours.

Summary of Flows

Users have two paths to register and execute their memo-intents. Amount-registration cannot be griefed, but requires sending an exact amount. Address-registration can be griefed, but can be easily detected. If an address is griefed, the user can switch to amount-registration in the 24hour expiry period.

In all cases, the user can verify their memo-intent before executing. If a mistake is made, it is likely to be refunded.

Time to Live

There is a TTL (time to live) on all memo-intents which is controlled by mimir; default will be 24hrs.

The memo-intent expires after 24hours OR after it is executed.

5-digit Codes

The range of numbers is 10000 to 99999, which means the network has 90k references available per chain per 24 hours.

If the space is too large, it is more difficult for shrimps to interact with the network (depending on the value of the asset). Too small and can run out references or be more subject to provider DoS attacks.

If BTC is $100k, then 10k-20k sats is $10-$20, so memoless tx's would always have a minimum amount that can be swapped. Or the user can switch to address-registration.

Implementation - FLOW 1

MsgType

THORChain tx using MsgDeposit, with:

r:<memo>
type MsgRegisterMemo struct {
  Memo string
}

TC will generate a ref code, 5 digits long, associated with that long memo. This code can now be used as a short memo, or for memo-less, simply the last 5 digits of the amount. The user can query thornode via their transaction hash to retrieve their "reference" code. The memo is added to the transaction and continues through the network.

eg:

longMemo: =:BNB.BNB:bnb1z2eh9wj29jkt3qqwjgqaxw7u6gve6fwcxh4u4s:97961079:tw:0

func generateMemoCode(fromAddress, fromChain, longMemo string) string {
	// Concatenate all inputs with separators to ensure uniqueness
	input := fromAddress + "|" + fromChain + "|" + longMemo
	
	// Create SHA256 hash of the combined string
	hash := sha256.Sum256([]byte(input))
	
	// Convert first 8 bytes of hash to hex string
	hashStr := hex.EncodeToString(hash[:8])
	
	// Convert hex to integer
	var num uint64
	fmt.Sscanf(hashStr, "%x", &num)
	
	// Get number in range 0-89999 and add 10000 to ensure minimum of 10000
	// 90000 is the range size (99999 - 10000 + 1)
	code := (num % 90000) + 10000
	
	// Format as 5-digit string
	return fmt.Sprintf("%05d", code)
}
short memo: 23456
added to amount: 1_0002_3456 (1 BTC)
used as short memo in the memo field: 23456

Implementation - FLOW 2

MsgType

THORChain tx using MsgDeposit, with:

r:<memo>:<from_chain>:<from_address>
type MsgRegisterMemo struct {
  Chain common.chain //from chain (to check correct)
  From_Address common.Address //User's from address
  Memo string
}

TC will then store the long memo against the first memo-less transaction seen for the registered address.

UTXO Bifrost Changes for HD Wallets

A HD wallet (Ledger Live) will quote the user the next address when user asks. It will also send the change there, which can be detected:

  1. If no OP_RETURN (either mistake or memo-less)
  2. Inspect the first Vin and the first non-THORChain Vout (where the change is going)
  3. If different, check either the Vin or the change Vout address as the "from_address" (could be a HD wallet) (if same, it is not HD).
  4. If memo-intent found in the amount, and (3) matches, process; if not, schedule for refund.

Possible outcomes:

  • Mistake from non-HD: refund back to Vin address
  • Memoless from non-HD: process with Vin as from_address
  • Mistake from HD: refund back to Vout address
  • Memoless from HD: process with Vout as from_address

Importantly - the user will either get executed correctly, or refunded. There will be no lost funds.

API Endpoints

/thorchain/memoless/<memo> // return the long memo with TTL
/thorchain/memoless/<address> // return the long memo with FROM_Address and TTL

Mimirs

  • MemolessTTL - number of blocks before the reference memo expires
  • MemolessCodeStart - starting code number (10,000 is suggested)
  • MemolessCodeEnd - ending code number (99,999 is suggested)

Consids

Savers Logic

Remove all "memoless" savers deposit/withdrawals that took up the 10k-20k slots.

Edited by THORChain