Skip to content

Instantly share code, notes, and snippets.

Last active February 6, 2023 21:00
Show Gist options
  • Save 0xekez/98cf6abe3705f4ff83920b7d82c7f332 to your computer and use it in GitHub Desktop.
Save 0xekez/98cf6abe3705f4ff83920b7d82c7f332 to your computer and use it in GitHub Desktop.

Gas Free CosmWasm

Here we describe a simple system for interacting with CosmWasm contracts without paying gas. Instead of submitting transactions to a RPC node, addresses submit a signed ExecuteMsg to a third party and that party relays those messages to the appropriate smart contract. This third party may censor but not forge messages, and messages may be submitted as regular transactions to circumvent any censorship by the third party.

The System

Messages have the format:

	"payload": {
		"nonce": u64,
		"msg": ExecuteMsg,
		"expiration": Timestamp | null,
		"bech32_prefix": String,
		"to": Addr,
		"version": String
	"signature": Binary,
	"pk": secp256,

To accept one of these messages from a smart contract:

  1. Validate that the payload is validly signed, or error.
  2. Validate that the payload has the correct nonce, or error.
  3. Validate that the payload has not expired, or error.
  4. Validate that the to field corresponds with the current contract.
  5. Validate that the version field corresponds to the verifier version.
  6. Set the message sender to the address corresponding to the provided public key.
  7. Call back into the contract's execute handler with this new sender and message.
let nonces: Map<String, u64> = Map::new("nonces");
let nonce = nonces.load(,;
deps.api.secp256k1_verify(msg.payload, msg.signature,;
if msg.payload.nonce != nonce {
    return Err(NogasError::InvalidNonce)
if msg.payload.expiration.is_expired(&env.block) {
    return Err(NogasError::ExpiredPayload)
if != env.contract.address {
	return Err(NoGasError::WrongReceiver)
if msg.payload.version != VERIFIER_VERSION {
	return Err(NoGasError::WrongVersion)
},, nonce + 1)?;

// call back into execute
info.sender = pk_to_addr(;
execute(deps, info, env, msg.payload.msg)

This is permissionless in that any address may submit these signed messages.



The simplest design has a single web2 server with a private key submit messages on behalf of users. This server would likely be run by the developers. We are exploring designs for decentralizing execution:

We expect that executors will want to filter messages. For example, DAO DAO may start by only making votes free. This filtering should only happen on the executor, and not in contracts.


This system provides the same security and availability as an RPC node. The web2 server committing your signed messages to the chain may censor you, like an RPC node. No party can execute messages on your behalf so long so your private key is kept private.

Copy link

ahh, I see. I was mistakenly thinking that the verification logic would be running on the intermediary server. So the contract would verify that this message signature corresponds to the incoming public key. but then does that mean that the non-forgeability depends on the authorization logic after this line?

info.sender = pk_to_addr(;

ie, what if a compromised DAO DAO server changes the message and resigns it with a different private key, and the address that corresponds to this bad key is a valid address in the contract's authorization checks. Would this be a valid forge?

Copy link

0xekez commented Jan 18, 2023

but then does that mean that the non-forgeability depends on the authorization logic after this line?

@blue-note good point. the server could forge votes for keys that it generates, i would argue that this isn't a security concern, as anyone can do that right now by generating a new public key.

for the address derivation, i think we can do this safely. this is done in a variety of places and @NoahSaso already does it in pfpk:

thanks for asking these questions btw. gets out the things that i forget to write down.

Copy link

Attack vectors and message manipulation

It is possible that a user may submit a message to the queue, then resubmit the same message, bypassing the queue. However, this scenario does not differ from what is possible in the current system, and in order to lower the likelihood of this occurring, we should make sure that the executor executes messages on a frequent enough cadence. We can also explore having state in the frontend which keeps track of whether a given message has been submitted to the queue, rather than if it has been executed (for example, if someone sends a message to the queue to submit a proposal, while we are waiting for the message to get executed, the frontend can display something such that a user does not mistakenly think their proposal was not submitted and attempt to re-submit).

This re-submit problem poses an issue for operations which are not idempotent, such as something which is flipping a toggle or incrementing a count.

In general, we should strive to have the behavior of the executor not differ from the current guarantees of an RPC node.

Future extensions of this

We discussed having the ability to allow DAOs to pay for the gas of its participants. One way this could be done is with the fee grant module.

Signing a list of messages in order to preserve message ordering

To be honest, this solution still does not make sense to me. The user is usually submitting one message at a time, so if we wanted to preserve the order of messages as they are coming in from the frontend, we’d have to sign the messages on the executor, but it is not clear how this would provide safety guarantees as the executor could have shuffled the ordering of messages before signing them.

Copy link

bekauz commented Feb 3, 2023

As far as the public key goes, are we going to be passing a compressed on uncompressed one?

Copy link

0xekez commented Feb 6, 2023

As far as the public key goes, are we going to be passing a compressed on uncompressed one?

discussed in call: it seems like supporting both compressed and uncompressed is the same amount of work. we'll support both.

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