Enveloping allows users to pay transaction fees with tokens. For this purpose, the system exposes methods that dApps and wallets can consume to provide Enveloping as a service.
The Relay Server is the off-chain component in charge of receiving transactions and sending them to the on-chain component, which is a Relay Manager. The Manager owns Relay Worker accounts with funds in native coin. To relay a transaction, a Worker signs it and sends it to the Relay Hub paying for the gas consumed.
A user can communicate with a Relay Server through a Relay Client. A Relay Client knows the addresses of different Relay Managers and it sends the on-chain request to the most active one. The Relay Client then sends the transaction to be sponsored to the Relay Server via HTTP request.
Users can interact with the Relay Server directly or indirectly.
The simplest option to use Enveloping in your wallet or dApp is by calling the Relay Server directly. See the instructions for running a Relayer in Getting Started. The communication with the Relay Server is through HTTP requests.
The order of events for relaying transactions or deploying smart wallets through the Relay Server is
/relay
method using an HTTP POST request.Another option is to use Enveloping through a Relay Provider. The latter wraps web3, and then all transactions and calls are made through the Relay Provider. If a Relay Client is not provided then the Relay Provider creates an instance.
this.config = await resolveConfigurationGSN(web3.currentProvider, {
verbose: window.location.href.includes('verbose'),
onlyPreferredRelays: false, //If false it will look for a relayer, if true it reads preferred Relays
chainId: chainId,
relayVerifierAddress: this.contracts.relayVerifier,
factory: this.contracts.factory,
preferredRelays:[]
})
this.provider = new Enveloping.RelayProvider(web3.currentProvider, this.config)
web3.setProvider(this.provider)
this.provider.deploySmartWallet(trxData)
this.provider.calculateSmartWalletAddress(
factory.address,gaslessAccount.address, recoverer, customLogic, walletIndex, bytecodeHash)
await testRecipient.emitMessage('hello world', {
from: gaslessAccount.address,
gas: '100000',
gasPrice: '1',
callVerifier: verifierInstance.address,
onlyPreferredRelays: true //It will read the preferredRelays on the config.
})
As we mentioned in the documentation, an advantage of the Enveloping’s solution is the chance to have a token’s wallet without deploying it. When a user needs to use her tokens, she needs to deploy the smart wallet using a deploy request. Thereby, when a gas-less account sent a transaction through Enveloping, they could use their smart wallet address to pay for the gas.
As a simplification of the process, the Enveloping Utils is provided to use as a library. It simplifies the process to create an smart wallet and therefore relay a transaction. It gives the chance to the developers to propose their provider to sign the transaction. The functions that the developer should code on the provider are sign
and verifySign
.
// Initialize the Enveloping Utils
const partialConfig: Partial<EnvelopingConfig> =
{
relayHubAddress: relayHub.address,
proxyFactoryAddress: factory.address,
chainId: chainId,
relayVerifierAddress: relayVerifier.address, // The verifier that will verify the relayed transaction
deployVerifierAddress: deployVerifier.address, // The verifier that will verify the smart wallet deployment
preferredRelays: ['http://localhost:8090'], //If there is a preferred relay server.
forwarderAddress: swAddress //To get the smart wallet address it's necessary to calculate the smart wallet address.
}
config = configure(partialConfig)
enveloping = new EnvelopingUtils(config, web3, workerAddress)
await enveloping._init()
// Instances a signature provider: This is just for test, please DO NOT use in production.
const signatureProvider: SignatureProvider = {
sign: (dataToSign: TypedRequestData, privKey?: Buffer) => {
// @ts-ignore
return sigUtil.signTypedData_v4(privKey, { data: dataToSign })
},
verifySign: (signature: PrefixedHexString, dataToSign: TypedRequestData, request: RelayRequest|DeployRequest) => {
// @ts-ignore
const rec = sigUtil.recoverTypedSignature_v4({
data: dataToSign,
sig: signature
})
return isSameAddress(request.request.from, rec)
}
}
// Deploying a Smart Wallet
const deployRequest = await enveloping.createDeployRequest(senderAddress, deploymentGasLimit, tokenContract, tokenAmount, tokenGas, gasPrice, index)
const deploySignature = enveloping.signDeployRequest(signatureProvider, deployRequest)
const httpDeployRequest = await enveloping.generateDeployTransactionRequest(deploySignature, deployRequest)
const sentDeployTransaction = await enveloping.sendTransaction(localhost, httpDeployRequest)
sentDeployTransaction.transaction?.hash(true).toString('hex') //This is used to get the transaction hash
// Relaying a transaction
const encodedFunction = testRecipient.contract.methods.emitMessage('hello world').encodeABI()
const relayRequest = await enveloping.createRelayRequest(gaslessAccount.address, testRecipient.address, encodedFunction, gasLimit, tokenContract, tokenAmount, tokenGas)
const relaySignature = enveloping.signRelayRequest(signatureProvider, relayRequest, gaslessAccount.privateKey)
const httpRelayRequest = await enveloping.generateRelayTransactionRequest(relaySignature, relayRequest)
const sentRelayTransaction = await enveloping.sendTransaction(localhost, httpRelayRequest)
sentRelayTransaction.transaction?.hash(true).toString('hex') //This is used to get the transaction hash
As a complete example (works on Regtest), we developed Metacoin for minting and sending tokens without requiring RBTC for gas.
See: Enveloping MetaCoin
Go to top