Delegating Meta Transactions
Both CNS and UNS smart contracts have meta-transaction support, which allows you to delegate transactions to another party. Generally, meta-transactions allow members to sign messages to control their domains that are then submitted to the registry by a different party. This enables Unstoppable to submit transactions on behalf of members so that the member can still manage their domains in a self-custodial capacity without any gas.
Meta-transactions work by having members sign function calls along with a nonce. They then send that signed function call over to a different party. That party calls the meta-transaction-enabled function on the Registry
in UNS or Resolver
in CNS. The meta-transaction method then checks the permission for a domain against the address recovered from the signed message sent to the function, unlike the base method that checks it against the submitter of the transaction e.g. msg.sender
.
info
UNS provides an interface that defines its meta-transactions function signatures and how to trigger them. The source code can be found in IForwarder.sol.
info
For UNS and CNS, the meta-transaction versions of Registry
functions are included in the registry. The source code for signature validation can be found in UNSRegistryForwarder.sol and CNSRegistryForwarder.sol.
Token Nonce
Meta transaction methods are bound to names via their nonce (instead of Account nonce of traditional transactions). It protects from Double-spending in the same way as an account-based nonce in traditional transactions.
The example below shows how replay attacks can be used to exploit domains:
A nonce is simply a transaction counter for each token. This prevents replay attacks where a transfer of a token from A
to B
can be replayed by B
over and over to continually revert the state of the name back to a previous state. This counter increments by 1 each time a state transition happens to a token. Token-based nonces can be used to prevent misordering of transactions in a more general sense as well. This prevents front running non-fungible assets and enables secure transaction batching.
Meta Transaction Signature Generation
A meta transaction requires 2 signatures: one passed as a method argument and one classical. A classical signature is generated in a standard way. A meta signature requires a domain owner (or a person approved by the owner) to sign a special message formed from:
- A domain based meta-transaction nonce
- A Function Selector of the original method
- The original method parameters (the one without signature)
UNS Signature Generation
UNS Example for a reset
method call for a domain:
const domain = 'example.crypto';
const methodName = 'reset';
const methodParams = ['uint256'];
const contractAddress = '0x049aba7510f45BA5b64ea9E658E342F904DB358D';
// can be different or the same as contractAddress
const controllerContractAddress = '0x049aba7510f45BA5b64ea9E658E342F904DB358D';
const tokenId = namehash(domain);
function generateMessageToSign(
contractAddress: string,
signatureContract: string,
methodName: string,
methodParams: string[],
tokenId: string,
params: any[],
) {
return solidityKeccak256(
['bytes32', 'address', 'uint256'],
[
solidityKeccak256(
['bytes'],
[encodeContractInterface(contractAddress, method, methodParams, params)],
),
controllerContractAddress,
ethCallRpc(controllerContractAddress, 'nonceOf', tokenId),
],
);
}
const message = generateMessageToSign(
contractAddress,
signatureContractAddress,
methodName,
methodParams,
tokenId,
[tokenId]
);
Functions Reference:
-
namehash
— Namehashing function algorithm implementation -
ethCallRpc
— Ethereumeth_call
JSON RPC implementation -
encodeContractInterface
— Solidity ABI interface parameters encoder -
solidityKeccak256
— Solidity ABI parameters encoder