Skip to content

Commit

Permalink
Fix eth transaction ++ (#3414)
Browse files Browse the repository at this point in the history
* Fixes for eth transactions

* enable switching chains from ui

* updates

* dont cache devnet..

* improve sanitation

* remove consoles
  • Loading branch information
ph101pp authored Mar 20, 2023
1 parent d5b57eb commit 6b3eb65
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 14 deletions.
8 changes: 6 additions & 2 deletions packages/app-extension/src/utils/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,18 @@ export const sanitizeTransactionWithFeeConfig = (
blockchain: Blockchain,
feeConfig?: { disabled: boolean; config: SolanaFeeConfig }
) => {
let tx = deserializeTransaction(txStr);
if (
blockchain === Blockchain.SOLANA &&
feeConfig &&
feeConfig?.config.priorityFee &&
tx.version === "legacy" &&
!feeConfig.disabled
) {
let tx = deserializeTransaction(txStr);

if (tx.version === "legacy") {
return txStr;
}

const transaction = deserializeLegacyTransaction(txStr);
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: feeConfig.config.computeUnits,
Expand Down
65 changes: 63 additions & 2 deletions packages/background/src/frontend/server-injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import {
ETHEREUM_RPC_METHOD_SIGN_AND_SEND_TX,
ETHEREUM_RPC_METHOD_SIGN_MESSAGE,
ETHEREUM_RPC_METHOD_SIGN_TX,
ETHEREUM_RPC_METHOD_SWITCH_CHAIN,
EthereumChainIds,
EthereumConnectionUrl,
getLogger,
NOTIFICATION_ETHEREUM_ACTIVE_WALLET_UPDATED,
NOTIFICATION_ETHEREUM_CHAIN_ID_UPDATED,
Expand Down Expand Up @@ -173,6 +176,8 @@ async function handle<T = any>(
return await handleConnect(ctx, Blockchain.ETHEREUM);
case ETHEREUM_RPC_METHOD_DISCONNECT:
return await handleDisconnect(ctx, Blockchain.ETHEREUM);
case ETHEREUM_RPC_METHOD_SWITCH_CHAIN:
return await handleEthereumSwitchChain(ctx, params[0]);
case ETHEREUM_RPC_METHOD_SIGN_MESSAGE:
return await handleEthereumSignMessage(ctx, params[0], params[1]);
case ETHEREUM_RPC_METHOD_SIGN_TX:
Expand Down Expand Up @@ -587,6 +592,62 @@ async function handleSolanaOpenXnft(
return ["success"];
}

async function handleEthereumSwitchChain(
ctx: Context<Backend>,
chainId: string
): Promise<RpcResponse<string>> {
if (ctx.sender.origin === undefined) {
throw new Error("origin is undefined");
}

const chainName: string | undefined = EthereumChainIds[chainId];

const url = chainName ? EthereumConnectionUrl[chainName] : undefined;

if (!url) {
throw new Error("Unsupported Chain: " + chainId);
}

const uiResp = await UiActionRequestManager.requestUiAction(
(requestId: string) => {
return openApproveMessagePopupWindow(
ctx.sender.origin!,
getTabTitle(ctx),
requestId,
`Switch to ${chainName} (${chainId})?`,
"walletAddress",
Blockchain.ETHEREUM
);
}
);

if (uiResp.error) {
logger.debug("require ui action error", uiResp);
BrowserRuntimeExtension.closeWindow(uiResp.window.id);
return;
}

let resp: RpcResponse<string>;
const didApprove = uiResp.result;

try {
// Only sign if the user clicked approve.
if (didApprove) {
await ctx.backend.ethereumConnectionUrlUpdate(url);
resp = await ctx.backend.ethereumChainIdUpdate(chainId);
}
} catch (err) {
logger.debug("error updating blockchain", err.toString());
}

if (!uiResp.windowClosed) {
BrowserRuntimeExtension.closeWindow(uiResp.window.id);
}
if (resp) {
return resp;
}
}

async function handleEthereumSignAndSendTx(
ctx: Context<Backend>,
tx: string,
Expand Down Expand Up @@ -618,12 +679,12 @@ async function handleEthereumSignAndSendTx(
let resp: RpcResponse<string>;
// The transaction may be modified and returned as result to accomodate user
// tweaked gas settings/nonce.
const { didApprove } = uiResp.result;
const { didApprove, transaction } = uiResp.result;
try {
// Only sign if the user clicked approve.
if (didApprove) {
const sig = await ctx.backend.ethereumSignAndSendTransaction(
tx,
transaction,
walletAddress
);
resp = [sig];
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ export const NOTIFICATION_USER_ACCOUNT_PUBLIC_KEY_DELETED =
// Ethereum web injected provider API.
//
export const ETHEREUM_RPC_METHOD_CONNECT = "ethereum-connect";
export const ETHEREUM_RPC_METHOD_SWITCH_CHAIN = "ethereum-switch-chain";
export const ETHEREUM_RPC_METHOD_DISCONNECT = "ethereum-disconnect";
export const ETHEREUM_RPC_METHOD_SIGN_TX = "ethereum-sign-tx";
export const ETHEREUM_RPC_METHOD_SIGN_AND_SEND_TX = "ethereum-sign-and-send-tx";
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/ethereum/background-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class BackgroundEthereumProvider extends JsonRpcProvider {
method: ETHEREUM_PROVIDER_RPC_GET_BALANCE,
params: [address, blockTag],
});
return BigNumber.from(result);
return result;
}

async getCode(
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/ethereum/connection-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ export const EthereumConnectionUrl = {
process.env.DEFAULT_ETHEREUM_CONNECTION_URL ||
"https://swr.xnfts.dev/ethereum-rpc-proxy",
};

export const EthereumChainIds = {
"0x1": "MAINNET",
"0x5": "GOERLI",
};
13 changes: 11 additions & 2 deletions packages/common/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ export class Plugin {
{ padding: false }
);

const isDevnetHACKY =
this._connectionUrls[Blockchain.SOLANA]?.includes("devnet");

const iframeRootUrl =
url.startsWith("ar://") || url.startsWith("ipfs://")
!isDevnetHACKY && (url.startsWith("ar://") || url.startsWith("ipfs://"))
? // || this.xnftAddress.toBase58() ===
// "CkqWjTWzRMAtYN3CSs8Gp4K9H891htmaN1ysNXqcULc8"
`https://${xnftAddressB32}.gateway.xnfts.dev`
Expand Down Expand Up @@ -586,7 +589,13 @@ export class Plugin {
return ["success"];
}

private async _handleWindowOpen(url: string): Promise<RpcResponse> {
private async _handleWindowOpen(_url: string): Promise<RpcResponse> {
const url = new URL(_url);

if (!url.protocol.startsWith("http")) {
throw "Invalid url.";
}

window.open(url, "_blank");
return ["success"];
}
Expand Down
4 changes: 3 additions & 1 deletion packages/provider-core/src/common/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ function encodeTransaction(transaction: any) {
delete transaction.from;
// Set transaction type if fully formed EIP1559
if (
(transaction.type === 2 || transaction.type == null) &&
(transaction.type === 2 ||
transaction.type === "0x2" ||
transaction.type == null) &&
transaction.maxFeePerGas != null &&
transaction.maxPriorityFeePerGas != null
) {
Expand Down
32 changes: 30 additions & 2 deletions packages/provider-core/src/provider-ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CHANNEL_ETHEREUM_RPC_REQUEST,
CHANNEL_ETHEREUM_RPC_RESPONSE,
ETHEREUM_RPC_METHOD_CONNECT,
ETHEREUM_RPC_METHOD_SWITCH_CHAIN,
getLogger,
NOTIFICATION_ETHEREUM_ACTIVE_WALLET_UPDATED,
NOTIFICATION_ETHEREUM_CHAIN_ID_UPDATED,
Expand Down Expand Up @@ -267,12 +268,20 @@ export class ProviderEthereumInjection extends EventEmitter {
});
}

const rpc_fallback = (method: string) => async (params: any[]) => {
const result = await this.provider!.send(method, params);
return result;
};

const functionMap = {
eth_accounts: this.handleEthAccounts,
eth_requestAccounts: this.handleEthRequestAccounts,
eth_chainId: () => this.chainId,
net_version: () => `${parseInt(this.chainId)}`,
eth_getBalance: (address: string) => this.provider!.getBalance(address),
eth_getBalance: async (...params) =>
rpc_fallback("eth_getBalance")(params),
eth_getGasPrice: async (...params) =>
rpc_fallback("eth_getGasPrice")(params),
eth_getCode: (address: string) => this.provider!.getCode(address),
eth_getStorageAt: (address: string, position: string) =>
this.provider!.getStorageAt(address, position),
Expand All @@ -297,13 +306,19 @@ export class ProviderEthereumInjection extends EventEmitter {
},
personal_sign: (messageHex: string, _address: string) =>
this.handleEthSignMessage(messageHex),
eth_signTypedData_v4: (_address: string, messageHex: string) => {
throw new Error("Backpack does not support eth_signTypedData_v4");
},
eth_signTransaction: (transaction: any) =>
this.handleEthSignTransaction(transaction),
eth_sendTransaction: (transaction: any) =>
this.handleEthSendTransaction(transaction),
wallet_switchEthereumChain: ({ chainId }) =>
this.handleEthSwitchChain(chainId),
};

const func = functionMap[method];

if (func === undefined) {
throw ethErrors.rpc.invalidRequest({
message: messages.errors.invalidRequestMethod(),
Expand Down Expand Up @@ -457,7 +472,20 @@ export class ProviderEthereumInjection extends EventEmitter {
};

/**
* Handle eth_requestAccounts requests
* Handle wallet_switchEthereumChain requests
*/
handleEthSwitchChain = async (chainId) => {
// Send request to the RPC API.
const response = await this.requestManager.request({
method: ETHEREUM_RPC_METHOD_SWITCH_CHAIN,
params: [chainId],
});

return response;
};

/**
* Handle wallet_switch requests
*/
handleEthRequestAccounts = async () => {
// Send request to the RPC API.
Expand Down
3 changes: 2 additions & 1 deletion packages/recoil/src/hooks/solana/usePlugins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export function useOpenPlugin(): (xnftAddress: string) => void {
const background = useRecoilValue(atoms.backgroundClient);

return (xnftAddress) => {
const url = `xnft/${xnftAddress}`;
const normalizedXnftAddress = xnftAddress.replace(/\.+\//g, ""); // simple normalize to prevent: "goodxnft/../badxnft"
const url = `xnft/${normalizedXnftAddress}`;
background
.request({
method: UI_RPC_METHOD_NAVIGATION_PUSH,
Expand Down
6 changes: 3 additions & 3 deletions packages/recoil/src/hooks/useTransactionData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function useEthereumTxData(serializedTx: any): TransactionData {
(async () => {
const parsed = ethers.utils.parseTransaction(bs58.decode(serializedTx));

if (parsed.chainId === 0) {
if (!parsed.chainId || parsed.chainId === 0) {
// chainId not passed in serialized transaction, use provider
parsed.chainId = parseInt(ethereumCtx.chainId);
}
Expand All @@ -128,8 +128,8 @@ export function useEthereumTxData(serializedTx: any): TransactionData {
// Set any transaction override values that were passed in the serialized
// transaction
const overridesToUpdate: Partial<TransactionOverrides> = {};
if (parsed.nonce !== 0) {
overridesToUpdate.nonce = 0;
if (parsed.nonce != 0) {
overridesToUpdate.nonce = parsed.nonce;
} else {
overridesToUpdate.nonce = await voidSigner.getTransactionCount();
}
Expand Down

1 comment on commit 6b3eb65

@vercel
Copy link

@vercel vercel bot commented on 6b3eb65 Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.