From 617afae80f9fbdd2d11bd201168a83cdcf6f9a53 Mon Sep 17 00:00:00 2001 From: Edward Alvarado Date: Thu, 4 Jan 2024 18:31:00 -0300 Subject: [PATCH] wallet-delegate doc --- .../3-delegate-wallet/1-introduction.mdx | 16 +++ .../3-delegate-wallet/2-getting-started.mdx | 116 ++++++++++++++++++ .../3-delegate-wallet/3-how-it-works.mdx | 87 +++++++++++++ .../3-delegate-wallet/_category_.json | 3 + 4 files changed, 222 insertions(+) create mode 100644 docs/home/700-multichain-support/3-delegate-wallet/1-introduction.mdx create mode 100644 docs/home/700-multichain-support/3-delegate-wallet/2-getting-started.mdx create mode 100644 docs/home/700-multichain-support/3-delegate-wallet/3-how-it-works.mdx create mode 100644 docs/home/700-multichain-support/3-delegate-wallet/_category_.json diff --git a/docs/home/700-multichain-support/3-delegate-wallet/1-introduction.mdx b/docs/home/700-multichain-support/3-delegate-wallet/1-introduction.mdx new file mode 100644 index 00000000..9d42e3af --- /dev/null +++ b/docs/home/700-multichain-support/3-delegate-wallet/1-introduction.mdx @@ -0,0 +1,16 @@ +# Introduction + +Delegate Wallet allows for a wallet address to be delegated to another wallet address. + +In practice, a user can use wallet address `A` to play, but their real wallet address is `B` + +Some use cases: +* **Better UX**: By using a "Local Wallet" within the browser and sending transactions through the "Batcher", so each transaction gets signed automatically. +* **Security**: Allowing players to have a "Burner Wallet" with limited funds to only play. +* **Security**: Reduce interaction with real wallet. +* **Account Recovery**: account can be re-delegated to a new wallet address. +* **Identity Privacy**: real wallet address is not exposed. + + +This feature is optional, but you can implement at any time. +As good practice use `user_id` in the STF to identify the user, and not the wallet address. \ No newline at end of file diff --git a/docs/home/700-multichain-support/3-delegate-wallet/2-getting-started.mdx b/docs/home/700-multichain-support/3-delegate-wallet/2-getting-started.mdx new file mode 100644 index 00000000..d8de0740 --- /dev/null +++ b/docs/home/700-multichain-support/3-delegate-wallet/2-getting-started.mdx @@ -0,0 +1,116 @@ +# Getting Started + +We are going to implement a workflow to delegate a Local Wallet to an EVM Wallet. + + +## In your middleware + +1. First implement a [Local Wallet](../wallet-layer/introduction#thirdweb-support) + +2. Create a function to sign messages with your local wallet. `WalletConnectHelper` and `getActiveAddress` is provided by @paima/sdk. `LocalWallet` is provided by `thirdweb`. +```js +const signMessageWithLocalWallet = async (message: string) => { + const toSign = new WalletConnectHelper().buildMessageToSign(message); + + return { + message: toSign, + walletAddress: getActiveAddress(WalletMode.EvmEthers).toLocaleLowerCase(), + /* localWallet is an instance of LocalWallet from the previous step */ + signedMessage: await localWallet.signMessage(message), + }; +}; +``` + +3. Now create a function to get User Wallets. `allInjectedWallets` is provided by `@paima/sdk`. +```js +const getWallets = async () => { + /* "wallets" is a map of connected wallets */ + const wallets = await allInjectedWallets({ + gameName: 'GameName', + gameChainId: undefined, + }); + return wallets; +} +``` + +4. Create a final function to sign messages with an external wallet. `WalletConnectHelper` is provided by `@paima/sdk`. +```js +const connectAndSignExternalWallet = async ( + walletType: AddressType, + walletName: string, /* Provided in wallets[WalletMode].metadata.name */ + otherAddress: string +) => { + return await new WalletConnectHelper().connectExternalWalletAndSign(walletType, walletName, message); +}; +``` + +5. Put it all together. `walletConnectPromise` is provided by `@paima/sdk`. +```js +const localWalletMessage = await signMessageWithLocalWallet(); +const wallets = getWallets(); + +// This is to use only the first EVM wallet. +// You should check the contents of "wallets" and display the necessary options. +const injectedWalletMessage = await connectAndSignExternalWallet( + AddressType.EVM, + wallets[WalletMode.EvmInjected].metadata.name, + localWalletSignedMessage.walletAddress +); + +// Send message to blockchain. +await walletConnectPromise( + injectedWalletMessage.walletAddress, + null, // localWalletMessage.walletAddress, + injectedWalletMessage.signedMessage, + localWalletMessage.signedMessage +); +``` + + +## In your backend + + +1. In the default API index: `/backend/api/index.ts` +Add the following code: +```js +// Clear wallet-connection cache. +clearDelegateWalletCacheOnChanges(requirePersistentConnection()); +``` +This allows to clear the API cache when the user changes the wallet connection. + +2. STF Changes +As user wallet might change over time, as they can delegate, migrate and cancel delegations, we need to use `userId` instead of `userAddress` or `realAddress` for user identification in our STF. +```js +export default async function ( + inputData: SubmittedChainData, + blockHeight: number, + randomnessGenerator: Prando, + dbConn: Pool +): Promise { + const user = String(inputData.userId); + /* use user to identify user instead of userAddress or realAddress */ + ... +} +``` +* userAddress: contains the main wallet address. +* realAddress: contains the real wallet address that sent the transaction. +* userId: contains the user id, which is the same for all wallets of the same user. + + +3. API Controllers +In your API, if receiving user wallet address, convert them into the USER ID. +```js +@Route('items') +export class ItemController extends Controller { + @Get('/user') + public async getItemsForUser( + @Query() wallet: string + ): Promise<{ items: IGetAllItemsForUserResult[] }> { + const pool = requirePool(); + const address = await getMainAddress(wallet, pool); + /* now use address, instead of wallet */ + const items = await getAllItemsForUser.run({ wallet: address }, pool); + return { items }; + } +} +``` diff --git a/docs/home/700-multichain-support/3-delegate-wallet/3-how-it-works.mdx b/docs/home/700-multichain-support/3-delegate-wallet/3-how-it-works.mdx new file mode 100644 index 00000000..8bd4a43b --- /dev/null +++ b/docs/home/700-multichain-support/3-delegate-wallet/3-how-it-works.mdx @@ -0,0 +1,87 @@ +# How it works + + +## Wallet Delegate API + +The user must send a transaction to delegate their wallet address to another wallet address, through a concise command. +IMPORTANT: This functionally is automatically provided by the SDK helpers. +``` + delegate = &wd|from?|to?|from_signature|to_signature + migrate = &wm|from?|to?|from_signature|to_signature + cancelDelegations = &wc|to_signature +``` +They must sign the transaction with the following message: +``` +DELEGATE-WALLET::` +``` +* from: the main address that will be +* to +* other-address-lowercase: is "to" or "from" for "from" and "to" respectively address in lowercase. +* env-contract-address: is defined in .env CONTRACT_ADDRESS + +## STF + +STF data is slightly changed: +* userAddress: contains the main wallet address. +* realAddress: contains the real wallet address that sent the transaction. +* userId: contains the user id, which is the same for all wallets of the same user. + +userAddress and realAddress will be the same for the main wallet or if there are no delegations. + +## Wallet Delegate Tables + +There will to managed tables +* addresses: maps a unique user id to each wallet address. +* delegations: maps delegated wallet addresses. + +## API + +1. This function delegates Wallet "to" to "from". +null "from" or "to" gets replace by wallet that sends (or signs if using the batcher) the transaction. +```js +function walletConnect( + from: string | null, + to: string | null, + from_signature: string, + to_signature: string +): Promise | FailedResult> +``` + +2. This function removes any current delegation to the sender address. +The message has the format: +```js +DELEGATE-WALLET::`; +``` + +```js +function walletConnectCancelDelegations( + to_signature: string +): Promise | FailedResult> { +``` + +3. This function migrates the address "from" to "to". +null "from" or "to" gets replace by wallet that sends (or signs if using the batcher) the transaction. +```js +function walletConnectMigrate( + from: string | null, + to: string | null, + from_signature: string, + to_signature: string +): Promise | FailedResult> +``` + +4. This class contains helper functions to sign with injected or generate the message for local wallets. +```js +class WalletConnectHelper { + buildMessageToSign(subMessage: string): string; + connectExternalWalletAndSign( + walletType: AddressType, + walletName: string, + subMessage: string + ): Promise<{ + message: string; + signedMessage: string; + walletAddress: string; + }> +} +``` \ No newline at end of file diff --git a/docs/home/700-multichain-support/3-delegate-wallet/_category_.json b/docs/home/700-multichain-support/3-delegate-wallet/_category_.json new file mode 100644 index 00000000..bfed0bcb --- /dev/null +++ b/docs/home/700-multichain-support/3-delegate-wallet/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Delegate Wallet" +} \ No newline at end of file