Skip to content

Commit

Permalink
feat: gas account (#2501)
Browse files Browse the repository at this point in the history
* feat: gas account

* fix

* feat: add btn loading state

* Refactor hooks to accept dependencies and improve state management

* fix ui

* fix

* fix ui

* Update src/background/controller/wallet.ts

Co-authored-by: vvvvvv1vvvvvv <[email protected]>

* Update src/background/controller/wallet.ts

Co-authored-by: vvvvvv1vvvvvv <[email protected]>

* fix

* fix

* feat: gasaccount logout verfied

* feat: logout gas account when address removed

* feat: sync logout state after deleting addresses

* update text

* update text

* remove bg

---------

Co-authored-by: vvvvvv1vvvvvv <[email protected]>
Co-authored-by: vvvvvv1vvvvvv <[email protected]>
  • Loading branch information
3 people authored Sep 5, 2024
1 parent 0c899e2 commit 685df0a
Show file tree
Hide file tree
Showing 62 changed files with 2,862 additions and 215 deletions.
70 changes: 68 additions & 2 deletions _raw/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
"myNativeTokenBalance": "My Gas balance: ",
"gasLimitEmptyAlert": "Please input gas limit",
"gasLimitMinValueAlert": "Gas limit should be more than 21000",
"nativeTokenForGas": "Use {{tokenName}} token on {{chainName}} to pay for gas",
"gasAccountForGas": "Use USD from my GasAccount to pay for gas",
"gasAccount": {
"totalCost": "Total cost: ",
"currentTxCost": "Gas amount sent to your address: ",
"gasCost": "Gas cost for sending Gas to your address: "
},
"balanceChange": {
"successTitle": "Simulation Results",
"failedTitle": "Simulation Failed",
Expand Down Expand Up @@ -346,12 +353,23 @@
"gasless": {
"unavailable": "Your Gas Balance is not enough",
"notEnough": "Gas balance is not enough",
"GetFreeGasToSign": "Get Free Gas to sign",
"GetFreeGasToSign": "Get Free Gas",
"rabbyPayGas": "Rabby'll pay for the gas needed – just sign on",
"customRpcUnavailableTip": "Custom RPCs are not supported for Free Gas",
"walletConnectUnavailableTip": "Mobile wallet connected via WalletConnect is not supported for Free Gas",
"watchUnavailableTip": "Watch-only address is not supported for Free Gas"
},
"gasAccount": {
"customRPC": "Not supported when using custom RPC",
"notEnough": "GasAccount is not enough",
"useGasAccount": "Use GasAccount",
"WalletConnectTips": "WalletConnect is not supported by GasAccount",
"chainNotSupported": "This chain is not supported by GasAccount",
"loginFirst": "Please log in to GasAccount first",
"login": "Log in",
"gotIt": "Got it",
"deposit": "Deposit"
},
"walletConnect": {
"connectedButCantSign": "Connected but unable to sign.",
"switchToCorrectAddress": "Please switch to the correct address in mobile wallet",
Expand Down Expand Up @@ -1024,7 +1042,8 @@
"sort-address": "Sort Address",
"enterThePassphrase": "Enter the Passphrase",
"enterPassphraseTitle": "Enter Passphrase to Sign",
"passphraseError": "Passphrase invalid"
"passphraseError": "Passphrase invalid",
"addNewAddress": "Add New Address"
},
"dashboard": {
"home": {
Expand Down Expand Up @@ -2180,6 +2199,53 @@
"txSending": "Sending signing request",
"txSendings": "Sending signing request({{current}}/{{total}})"
}
},

"gasAccount": {
"title": "GasAccount",
"deposit": "Deposit",
"withdraw": "Withdraw",
"gasExceed": "GasAccount balance cannot exceed $1000",
"logout": "Log out current GasAccount",
"history": {
"noHistory": "No history"
},
"loginInTip": {
"title": "Deposit USDC / USDT",
"desc": "Pay Gas Fees on All Chains",
"login": "Log in GasAccount",
"gotIt": "Got it"
},
"loginConfirmModal": {
"title": "Log in with Current Address",
"desc": "Once confirmed you can only log in with this address"
},
"logoutConfirmModal": {
"title": "Log out current GasAccount",
"desc": "Once logged out, you will no longer be able to use GasAccount to pay for gas fees.",
"logout": "Log out"
},
"depositPopup": {
"title": "Deposit",
"desc": "Make a deposit to Rabby's DeBank L2 Account with no extra fees—withdraw anytime.",
"amount": "Amount",
"token": "Token",
"selectToken": "Select Token to Deposit"
},
"withdrawPopup": {
"title": "Withdraw",
"desc": "You can withdraw your GasAccount balance to your DeBank L2 Wallet. Log in to your DeBank L2 Wallet to transfer the funds to a supported blockchain as needed.",
"amount": "Amount",
"to": "To"
},
"withdrawConfirmModal": {
"title": "Transferred to your DeBank L2 Wallet",
"button": "View on DeBank"
},
"GasAccountDepositTipPopup": {
"title": "Open GasAccount and Deposit",
"gotIt": "Got it"
}
}
},
"component": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@rabby-wallet/gnosis-sdk": "1.3.8",
"@rabby-wallet/page-provider": "0.4.0",
"@rabby-wallet/rabby-action": "^0.1.1-beta.3",
"@rabby-wallet/rabby-api": "0.7.28",
"@rabby-wallet/rabby-api": "0.7.31",
"@rabby-wallet/rabby-security-engine": "2.0.7-beta",
"@rabby-wallet/rabby-swap": "0.0.39",
"@rabby-wallet/widgets": "1.0.9",
Expand Down
4 changes: 4 additions & 0 deletions src/background/controller/provider/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ interface ApprovalRes extends Tx {
lowGasDeadline?: number;
reqId?: string;
isGasLess?: boolean;
isGasAccount?: boolean;
logId?: string;
}

Expand Down Expand Up @@ -367,6 +368,7 @@ class ProviderController extends BaseController {
const preReqId = approvalRes.reqId;
const isGasLess = approvalRes.isGasLess || false;
const logId = approvalRes.logId || '';
const isGasAccount = approvalRes.isGasAccount || false;

let signedTransactionSuccess = false;
delete txParams.isSend;
Expand All @@ -387,6 +389,7 @@ class ProviderController extends BaseController {
delete txParams.isCoboSafe;
delete approvalRes.isGasLess;
delete approvalRes.logId;
delete approvalRes.isGasAccount;

let is1559 = is1559Tx(approvalRes);
if (
Expand Down Expand Up @@ -698,6 +701,7 @@ class ProviderController extends BaseController {
req_id: preReqId || '',
origin,
is_gasless: isGasLess,
is_gas_account: isGasAccount,
log_id: logId,
});
hash = res.req.tx_id || undefined;
Expand Down
102 changes: 101 additions & 1 deletion src/background/controller/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
RabbyPointsService,
HDKeyRingLastAddAddrTimeService,
bridgeService,
gasAccountService,
} from 'background/service';
import buildinProvider, {
EthereumProvider,
Expand Down Expand Up @@ -86,7 +87,12 @@ import transactionWatcher from '../service/transactionWatcher';
import Safe from '@rabby-wallet/gnosis-sdk';
import { Chain } from '@debank/common';
import { isAddress } from 'web3-utils';
import { findChain, findChainByEnum, getChainList } from '@/utils/chain';
import {
findChain,
findChainByEnum,
findChainByServerID,
getChainList,
} from '@/utils/chain';
import { cached } from '../utils/cache';
import { createSafeService } from '../utils/safe';
import { OpenApiService } from '@rabby-wallet/rabby-api';
Expand Down Expand Up @@ -116,6 +122,7 @@ import {
import { appIsProd } from '@/utils/env';
import { getRecommendGas, getRecommendNonce } from './walletUtils/sign';
import { waitSignComponentAmounted } from '@/utils/signEvent';
import pRetry from 'p-retry';

const stashKeyrings: Record<string | number, any> = {};

Expand Down Expand Up @@ -1744,6 +1751,10 @@ export class WalletController extends BaseController {
setBridgeSortIncludeGasFee = bridgeService.setBridgeSortIncludeGasFee;
setBridgeSettingFirstOpen = bridgeService.setBridgeSettingFirstOpen;

getGasAccountData = gasAccountService.getGasAccountData;
getGasAccountSig = gasAccountService.getGasAccountSig;
setGasAccountSig = gasAccountService.setGasAccountSig;

setCustomRPC = RPCService.setRPC;
removeCustomRPC = RPCService.removeCustomRPC;
getAllCustomRPC = RPCService.getAllRPC;
Expand Down Expand Up @@ -4370,6 +4381,95 @@ export class WalletController extends BaseController {
return signature;
};

signGasAccount = async () => {
const account = await preferenceService.getCurrentAccount();
if (!account) throw new Error(t('background.error.noCurrentAccount'));

const { text } = await wallet.openapi.getGasAccountSignText(
account.address
);
const signature = await this.sendRequest<string>({
method: 'personal_sign',
params: [text, account.address],
});

if (signature) {
const result = await pRetry(
async () =>
wallet.openapi.loginGasAccount({
sig: signature,
account_id: account.address,
}),
{
retries: 2,
}
);

if (result?.success) {
await gasAccountService.setGasAccountSig(signature, account);
await pageStateCacheService.clear();
await pageStateCacheService.set({
path: '/gas-account',
states: {},
});
}
}
};

topUpGasAccount = async ({
to,
chainServerId,
tokenId,
rawAmount,
amount,
}: {
to: string;
chainServerId: string;
tokenId: string;
rawAmount: string;
amount: number;
}) => {
const account = await preferenceService.getCurrentAccount();
if (!account) throw new Error(t('background.error.noCurrentAccount'));

const { sig, accountId } = this.getGasAccountSig();

const tx = await this.sendToken({
to,
chainServerId,
tokenId,
rawAmount,
});

const chain = findChainByServerID(chainServerId);

const nonce = await this.getNonceByChain(account.address, chain!.id);

if (tx) {
this.openapi.rechargeGasAccount({
sig: sig!,
account_id: accountId!,
tx_id: tx,
chain_id: chainServerId,
amount,
user_addr: account?.address,
nonce: nonce! - 1,
});
} else {
Sentry.captureException(
new Error(
'topUp GasAccount tx failed, params: ' +
JSON.stringify({
userAddr: account.address,
gasAccount: accountId,
chain: chainServerId,
amount: amount,
})
)
);
}
};

addCustomTestnet = async (
chain: Parameters<typeof customTestnetService.add>[0],
ctx?: {
Expand Down
46 changes: 33 additions & 13 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
EVENTS,
EVENTS_IN_BG,
KEYRING_CATEGORY_MAP,
KEYRING_TYPE,
} from 'consts';
import { storage } from './webapi';
import {
Expand All @@ -32,6 +33,7 @@ import {
transactionBroadcastWatchService,
HDKeyRingLastAddAddrTimeService,
bridgeService,
gasAccountService,
} from './service';
import { providerController, walletController } from './controller';
import { getOriginFromUrl } from '@/utils';
Expand All @@ -42,7 +44,7 @@ import createSubscription from './controller/provider/subscriptionManager';
import buildinProvider from 'background/utils/buildinProvider';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { setPopupIcon } from './utils';
import { isSameAddress, setPopupIcon } from './utils';
import { appIsDev, getSentryEnv } from '@/utils/env';
import { matomoRequestEvent } from '@/utils/matomo-request';
import { testnetOpenapiService } from './service/openapi';
Expand All @@ -51,6 +53,7 @@ import Safe from '@rabby-wallet/gnosis-sdk';
import { customTestnetService } from './service/customTestnet';
import { findChain } from '@/utils/chain';
import { syncChainService } from './service/syncChain';
import { GasAccountServiceStore } from './service/gasAccount';

Safe.adapter = fetchAdapter as any;

Expand Down Expand Up @@ -99,6 +102,7 @@ async function restoreAppState() {
await RabbyPointsService.init();
await HDKeyRingLastAddAddrTimeService.init();
await bridgeService.init();
await gasAccountService.init();

await walletController.tryUnlock();

Expand Down Expand Up @@ -193,18 +197,6 @@ restoreAppState();
};
sendEvent();
interval = setInterval(sendEvent, 5 * 60 * 1000);
// TODO: remove me after 2022.12.31
const arrangeOldContactAndAlias = async () => {
const addresses = await keyringService.getAllAdresses();
const contactMap = contactBookService.getContactsByMap();
addresses.forEach(({ address }) => {
const item = contactMap[address];
if (item && item.isContact && !item.isAlias) {
contactBookService.addAlias({ name: item.name, address });
}
});
};
arrangeOldContactAndAlias();
});

keyringService.on('lock', () => {
Expand All @@ -213,6 +205,34 @@ restoreAppState();
interval = null;
}
});

keyringService.on(
'removedAccount',
async (address: string, type: string, brand?: string) => {
if (type !== KEYRING_TYPE.WatchAddressKeyring) {
const restAddresses = await keyringService.getAllAdresses();
const gasAccount = gasAccountService.getGasAccountData() as GasAccountServiceStore;
if (!gasAccount?.account?.address) return;
// check if there is another type address in wallet
const stillHasAddr = restAddresses.some((item) => {
return (
isSameAddress(item.address, gasAccount.account!.address) &&
item.type !== KEYRING_TYPE.WatchAddressKeyring
);
});
if (
!stillHasAddr &&
isSameAddress(address, gasAccount.account.address)
) {
// if there is no another type address then reset signature
gasAccountService.setGasAccountSig();
eventBus.emit(EVENTS.broadcastToUI, {
method: EVENTS.GAS_ACCOUNT.LOG_OUT,
});
}
}
}
);
}

// for page provider
Expand Down
Loading

0 comments on commit 685df0a

Please sign in to comment.