-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from 0xsequence/guidesMigration
Guides migration
- Loading branch information
Showing
306 changed files
with
3,832 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
docs/public/** filter=lfs diff=lfs merge=lfs -text |
Binary file not shown.
267 changes: 267 additions & 0 deletions
267
docs/pages/guides/templates/02-building-relaying-server.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
--- | ||
slug: /relayer/building-relaying-server | ||
--- | ||
|
||
# Building a Relaying Server | ||
|
||
With Sequence, you can create a smart contract wallet your server can use to dispatch transactions for your users | ||
without you having to be worried about transaction speed, throughput and re-orgs. | ||
|
||
:::info Sequentual Transactions | ||
By default, Sequence transactions will be executed sequentially. | ||
::: | ||
|
||
## Nodejs Server | ||
|
||
Your server will need an EOA wallet that will be able to sign messages. It will be the owner of your server-side Sequence | ||
wallet which will be used to dispatch transactions. | ||
|
||
This Sequence wallet should have the correct ownership at your contract level, not the EOA. | ||
Also, the Sequence wallet should be sufficiently funded in order to pay fees needed by the relayer to dispatch your transactions. | ||
|
||
Using this approach - versus the next section (with sponsoring) - includes the ability to pay for gas in any available currency by our relayer (e.g. 'MATIC', 'DAI', 'USDC', 'WETH'). | ||
|
||
```ts | ||
import { Session } from '@0xsequence/auth' | ||
|
||
// Create your server EOA | ||
const walletEOA = new ethers.Wallet(serverPrivateKey, provider) | ||
|
||
// Open a Sequence session, this will find or create | ||
// a Sequence wallet controlled by your server EOA | ||
const session = await Session.singleSigner({ | ||
signer: walletEOA, | ||
projectAccessKey: '<access_key>' | ||
}) | ||
|
||
// Get the Sequence wallet address | ||
console.log(session.account.address) | ||
|
||
// Get a signer for a specific network | ||
// - 1: Ethereum Mainnet | ||
// - 137: Polygon Mainnet | ||
// - 42161: Arbitrum One | ||
// See https://chainid.network/ for more | ||
const signer = session.account.getSigner(137) | ||
|
||
// Craft your transaction | ||
const erc721Interface = new ethers.utils.Interface([ | ||
'function safeTransferFrom(address _from, address _to, uint256 _tokenId)' | ||
]) | ||
|
||
const data = erc721Interface.encodeFunctionData( | ||
'safeTransferFrom', [senderAddress, recipientAddress, id] | ||
) | ||
|
||
const txn = { | ||
to: erc721TokenAddress, | ||
data | ||
} | ||
|
||
// Send the transaction | ||
const txnResponse = await signer.sendTransaction(txn) | ||
|
||
// Check if transaction was successful | ||
if (txnReceipt.status != 1) { | ||
console.log(`Unexpected status: ${txnReceipt.status}`) | ||
} | ||
``` | ||
|
||
You can also enforce a specific way to pay for gas fees, or the openning of a specific Sequence wallet. | ||
|
||
```ts | ||
import { Session } from '@0xsequence/auth' | ||
|
||
// Create your server EOA | ||
const walletEOA = new ethers.Wallet(serverPrivateKey, provider) | ||
|
||
// Open a Sequence session, this will find or create | ||
// a Sequence wallet controlled by your server EOA | ||
const session = await Session.singleSigner({ | ||
signer: walletEOA, | ||
projectAccessKey: '<access_key>' | ||
// OPTIONAL: Multiple wallets could be found for the same EOA | ||
// to enforce a specific wallet you can use the following callback | ||
selectWallet: async (wallets: string[]) => { | ||
const found = wallets.find(w => w === EXPECTED_WALLET_ADDRESS) | ||
if (!found) throw Error('wallet not found') | ||
// Returning the wallet address will make the session use it | ||
// returning undefined will make the session create a new wallet | ||
return found | ||
} | ||
}) | ||
|
||
const signer = session.account.getSigner(137, { | ||
// OPTIONAL: You can also enforce a specific way to pay for gas fees | ||
// if not provided the sdk will select one for you | ||
selectFee: async ( | ||
_txs: any, | ||
options: FeeOption[] | ||
) => { | ||
// Find the option to pay with native tokens | ||
const found = options.find(o => !o.token.contractAddress) | ||
if (!found) throw Error('fee option not found') | ||
return found | ||
} | ||
}) | ||
|
||
// Initialize the contract | ||
const usdc = new ethers.Contract( | ||
'0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC on Polygon | ||
ERC_20_ABI, | ||
signer | ||
) | ||
|
||
// Send the transaction | ||
const txnResponse = await usdc.transfer(recipient, 1) | ||
|
||
// Check if transaction was successful | ||
if (txnReceipt.status != 1) { | ||
console.log(`Unexpected status: ${txnReceipt.status}`) | ||
} | ||
``` | ||
|
||
:::caution Triggers migration | ||
|
||
Openning a session may trigger a migration of your Sequence wallet to a new version, this could be `v1` to `v2` or `v2` to future versions. | ||
|
||
Migration is a one-way process, once your wallet is migrated it cannot be reverted to a previous version. | ||
|
||
To catch any unwanted migration, you can use the `onMigration` callback. | ||
::: | ||
|
||
## Nodejs Server with Gas Sponsoring using Sequence Builder | ||
|
||
|
||
If you want to have your transactions sponsored & paid for with a credit card, you can follow the below steps before beginning your code, at the following link: https://sequence.build/ | ||
|
||
By sponsoring your transaction, you can now just send the transaction without a fee object and not have to fund the smart contract wallet before relaying any transactions. | ||
|
||
:::info Already Deployed Contract | ||
For this example, we assume you have a smart contract deployed with a contract address to include in the last step. | ||
::: | ||
|
||
### A. Create Dapp | ||
|
||
![Sequence builder create app](/img/builder/builder_create_dapp.png) | ||
|
||
### B. New Dapp | ||
|
||
![Sequence builder new dapp](/img/builder/builder_new_dapp.png) | ||
|
||
### C.1 Gas Tank | ||
|
||
![Sequence builder gas tank](/img/builder/builder_gas_tank.png) | ||
|
||
### C.2 Add Gas | ||
|
||
![Sequence builder add gas](/img/builder/builder_add_gas.png) | ||
|
||
### C.3 Add Sponsored Address | ||
|
||
![Sequence builder add sponsored address](/img/builder/builder_add_sponsored_address.png) | ||
|
||
The following is example code that implements a relayed transaction, same as the above example (i.e. Nodejs Server) but without fees, taken care of by the Sequence Builder. | ||
|
||
```ts | ||
import { Session } from '@0xsequence/auth' | ||
|
||
// Create your server EOA | ||
const walletEOA = new ethers.Wallet(serverPrivateKey, provider) | ||
|
||
// Open a Sequence session, this will find or create | ||
// a Sequence wallet controlled by your server EOA | ||
const session = await Session.singleSigner({ | ||
signer: walletEOA, | ||
projectAccessKey: '<access_key>' | ||
}) | ||
|
||
// Get the Sequence wallet address | ||
console.log(session.account.address) | ||
|
||
// Get a signer for a specific network | ||
// - 1: Ethereum Mainnet | ||
// - 137: Polygon Mainnet | ||
// - 42161: Arbitrum One | ||
// See https://chainid.network/ for more | ||
const signer = session.account.getSigner(137, { | ||
// OPTIONAL: This ensures that the transaction is paid for by the gas tank | ||
// but if not provided, the gas tank will be used anyway | ||
selectFee: async ( | ||
_txs: any, | ||
_options: FeeOption[] | ||
) => { | ||
return undefined | ||
} | ||
}) | ||
|
||
// Craft your transaction | ||
const erc721Interface = new ethers.utils.Interface([ | ||
'function safeTransferFrom(address _from, address _to, uint256 _tokenId)' | ||
]) | ||
|
||
const data = erc721Interface.encodeFunctionData( | ||
'safeTransferFrom', [senderAddress, recipientAddress, id] | ||
) | ||
|
||
const txn = { | ||
to: erc721TokenAddress, | ||
data | ||
} | ||
|
||
// Send the transaction | ||
const txnResponse = await signer.sendTransaction(txn) | ||
|
||
// Check if transaction was successful | ||
if (txnReceipt.status != 1) { | ||
console.log(`Unexpected status: ${txnReceipt.status}`) | ||
} | ||
``` | ||
|
||
## Parallel Transactions | ||
|
||
If you want to send multiple independent transactions without needing to batch them, you can also send them in distinct nonce spaces. | ||
Using distinct nonce spaces for your transactions signals to the relayer that there's no dependency between them and that | ||
they can be executed on-chain in any order. | ||
|
||
This allows the transactions to be dispatched immediately in an unbuffered way without having to wait for a full batch. | ||
Here is an example of how to do that: | ||
|
||
```js | ||
// Generate random nonce spaces with ~0% probability of collision | ||
const randomNonceSpace1 = ethers.BigNumber.from(ethers.utils.hexlify(ethers.utils.randomBytes(20))) | ||
const randomNonceSpace2 = ethers.BigNumber.from(ethers.utils.hexlify(ethers.utils.randomBytes(20))) | ||
|
||
// Create signers for each nonce space | ||
const signer1 = session.account.getSigner(137, { | ||
nonceSpace: randomNonceSpace1 | ||
}) | ||
|
||
const signer2 = session.account.getSigner(137, { | ||
nonceSpace: randomNonceSpace2 | ||
}) | ||
|
||
// Generate transactions | ||
const txn1 = { | ||
to: tokenContract.address, | ||
data: erc20Interface.encodeFunctionData( | ||
'transfer', [recipient1, amount1] | ||
) | ||
} | ||
|
||
const txn2 = { | ||
to: tokenContract.address, | ||
data: erc20Interface.encodeFunctionData( | ||
'transfer', [recipient2, amount2] | ||
) | ||
} | ||
|
||
// Dispatch transactions, which can now be executed in parallel | ||
await Promise.all([ | ||
signer1.sendTransaction(txn1), | ||
signer2.sendTransaction(txn2) | ||
]) | ||
``` | ||
|
||
If batching transactions is not a problem for your use-case, you can call `await wallet.sendTransaction(txns)`. | ||
You can read more about batch transactions in [Sending Batched Transactions](/solutions/wallets/universal-wallet/03-guides/09-send-batch-transactions). |
Oops, something went wrong.