Skip to content

Instantly share code, notes, and snippets.

Last active April 25, 2023 21:57
Show Gist options
  • Save 0xekez/f06d1c6c756c8f209c5de74e0ce9b702 to your computer and use it in GitHub Desktop.
Save 0xekez/f06d1c6c756c8f209c5de74e0ce9b702 to your computer and use it in GitHub Desktop.


Tonic makes transactions fee free. With tonic, there is no need to buy tokens to use DAO DAO.

To use Tonic, a user signs a message and submits that message to a third party, the executor. The executor then collects messages and, individually or in batches, submits them to Tonic. Tonic verifies each message's signature and creates an account for each user. If a message's signature is valid, Tonic forwards the message to the signer's account, and the account executes it.


This design means, Tonic's users have two addresses: one derived from their public key, and one account with Tonic. We plan to use Tonic only for accounts created with web3auth, in which case a new account is created per-app anyway. The DAO DAO UI will display a user's tonic account address, instead of their public-key derived one.

Message Forwarding

Given a way to verify signatures, Tonic is quite simple. All signature verification happens in the top-level Tonic module, as opposed to the account. This simplifies upgrading signature verification logic and pausing the system as all messages are routed through the top-level Tonic module.

The account contract then stores the public-key address of its owner, and the address of its parent Tonic module. Accounts allow both the Tonic module, and their owner to execute messages via them. This means that a paused Tonic module will not lock users out of their accounts, requiring instead that they pay gas fees.


To execute a message, the account simply checks the sender is either its owner or the Tonic module.

image image

The top-level Tonic module verifies signatures, creates accounts, and forwards validated messages to their respective accounts.


Message Signing

Messages are submitted as a wrapped CosmosMsg. The wrapper contains information specific to the signature like the nonce, an expiration, and the public key corresponding to the private key that signed.

	"payload": {
		"nonce": u128,
		"msg": CosmosMsg,
		"expiration": Timestamp | null,
		"version": String,
		"to": Addr,
	"signature": Binary,
	"pk": secp256k1,

The signature is a signed hash of the payload JSON-serialized. The hash function used a 512 bit sha2 hash. To accept a message, the Tonic module:

  1. Checks the signature against the payload.
  2. Checks the nonce is correct for the signature.
  3. Checks that the version corresponds to the current Tonic version.
  4. Checks that the to field contains the address of the verifying Tonic module.
  5. Checks that the message is not expired.

Once an message has been verified, the public key is used to derive an account address using CosmWasm's instantiate2, and that account is instantiated, if needed, then used to execute the message.

Execution Incentives and Pausing

The Tonic module will fire a hook whenever a new message is submitted. This hook will contain then entire submitted payload and the public key. If the hook fails to be processed, execution of the submitted message will be canceled.


Canceling the message submission if the hook receiver errors means that a pause or rate limiter can be implemented by listening to incoming messages, and erroring if paused or if the rate limit has been exceeded. Passing the entire message information to hook receivers allows building quite granular inventive and rate limiting schemes.

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