Skip to content

Commit

Permalink
Feature/etherspot prime sdk improvements (#38)
Browse files Browse the repository at this point in the history
* etherspot prime fixes and improvements
  • Loading branch information
poocart authored Jun 14, 2023
1 parent 00960e9 commit 902bc1c
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 43 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [0.3.2] - 2023-06-14

### Added
- Added fixes and improvements how Etherspot Prime SDK is created and handled
- Added `userOpHash` and `via` type to `SentBatches` type to to able to tell if it is
`etherspot` `batchHash` or `etherspot-prime` `userOpHash`

## [0.3.1] - 2023-06-13

### Added
Expand Down
4 changes: 2 additions & 2 deletions __tests__/hooks/useProviderWalletTransaction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe('useProviderWalletTransaction()', () => {
.toThrow('Multiple <ProviderWalletTransaction /> not allowed');
});

it('throws an error if <ProviderWalletTransaction /> rendered without <EtherspotTransactionKit />', () => {
it('throws an error if <ProviderWalletTransaction /> rendered without <EtherspotTransactionKit /> that includes <EtherspotContextProvider />', () => {
expect(() => render(
<ProviderWalletTransaction
to={'0x12'}
Expand All @@ -129,6 +129,6 @@ describe('useProviderWalletTransaction()', () => {
<span>test</span>
</ProviderWalletTransaction>
))
.toThrow('No parent <EtherspotTransactionKit />');
.toThrow('No parent <EtherspotContextProvider />');
});
})
3 changes: 2 additions & 1 deletion __tests__/hooks/useWalletAddress.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { ethers } from 'ethers';
// hooks
import { EtherspotTransactionKit, useWalletAddress } from '../../src';

const provider = new ethers.Wallet.createRandom();
const ethersProvider = new ethers.providers.JsonRpcProvider('http://localhost:8545', 'goerli'); // replace with your node's RPC URL
const provider = new ethers.Wallet.createRandom().connect(ethersProvider);

const etherspotAddress = '0x7F30B1960D5556929B03a0339814fE903c55a347';
const etherspotPrimeAddress = '0x07ff85757f5209534EB601E1CA60d72807ECE0bC';
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@etherspot/transaction-kit",
"description": "React Etherspot Transaction Kit",
"version": "0.3.1",
"version": "0.3.2",
"main": "dist/cjs/index.js",
"scripts": {
"rollup:build": "NODE_OPTIONS=--max-old-space-size=8192 rollup -c",
Expand Down
2 changes: 2 additions & 0 deletions src/contexts/EtherspotTransactionKitContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createContext, Dispatch, SetStateAction } from 'react';
import { PrimeSdk } from 'etherspot-prime';

// types
import { IBatches, IEstimatedBatches, ISentBatches } from '../types/EtherspotTransactionKit';
Expand All @@ -10,6 +11,7 @@ export interface IEtherspotTransactionKitContext {
batches: IBatches[];
estimate: (batchesIds?: string[]) => Promise<IEstimatedBatches[]>;
send: (batchesIds?: string[]) => Promise<ISentBatches[]>;
getEtherspotPrimeSdkForChainId: (chainId: number, force?: boolean) => Promise<PrimeSdk | null>;
},
setGroupedBatchesPerId: Dispatch<SetStateAction<TypePerId<IBatches>>>;
}
Expand Down
23 changes: 13 additions & 10 deletions src/hooks/useWalletAddress.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { useEffect, useState } from 'react';
import { useEtherspot } from '@etherspot/react-etherspot';
import { AccountTypes, EnvNames } from 'etherspot';
import { PrimeSdk } from 'etherspot-prime';
import { AccountTypes } from 'etherspot';

// types
import { IWalletType } from '../types/EtherspotTransactionKit';

// utils
import { isTestnetChainId } from '../utils/common';
// hooks
import useEtherspotTransactions from './useEtherspotTransactions';

/**
* Hook to return wallet address by wallet type
* @param walletType {IWalletType} - wallet type
* @param chainId {number} - Chain ID
* @returns {string | undefined} - wallet address by it's type
* @returns {string | undefined} - wallet address by its type
*/
const useWalletAddress = (walletType: IWalletType = 'etherspot', chainId: number = 1): string | undefined => {
const [walletAddress, setWalletAddress] = useState<(string | undefined)>(undefined);
const { connect, getSdkForChainId, provider, providerWalletAddress } = useEtherspot();
const { connect, getSdkForChainId, providerWalletAddress } = useEtherspot();
const { getEtherspotPrimeSdkForChainId } = useEtherspotTransactions();

useEffect(() => {
let shouldUpdate = true;
Expand All @@ -44,12 +44,15 @@ const useWalletAddress = (walletType: IWalletType = 'etherspot', chainId: number
return;
}

const etherspotPrimeSdk = await getEtherspotPrimeSdkForChainId(chainId);
if (!etherspotPrimeSdk) {
console.warn(`Unable to get Etherspot Prime SDK for chain ID ${chainId}`);
setWalletAddress(undefined);
return;
}

try {
// @ts-ignore
const etherspotPrimeSdk = new PrimeSdk(provider, {
networkName: etherspotNetwork.name,
env: isTestnetChainId(etherspotNetwork.chainId) ? EnvNames.TestNets : EnvNames.MainNets,
});
updatedWalletAddress = await etherspotPrimeSdk.getCounterFactualAddress();
} catch (e) {
console.warn(
Expand Down
68 changes: 50 additions & 18 deletions src/providers/EtherspotTransactionKitContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEtherspot } from '@etherspot/react-etherspot';
import React, { useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { PrimeSdk } from 'etherspot-prime';
import { AccountStates, EnvNames, NETWORK_NAME_TO_CHAIN_ID } from 'etherspot';
import { AccountStates, EnvNames, isWalletProvider, NETWORK_NAME_TO_CHAIN_ID, Web3WalletProvider } from 'etherspot';
import { ethers } from 'ethers';

// contexts
Expand Down Expand Up @@ -63,7 +63,7 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
}

if (groupedBatch.via === 'etherspot-prime') {
const etherspotPrimeSdkForChainId = getEtherspotPrimeSdkForChainId(batchChainId);
const etherspotPrimeSdkForChainId = await getEtherspotPrimeSdkForChainId(batchChainId);
if (!etherspotPrimeSdkForChainId) {
estimatedBatches.push({ ...batch, errorMessage: 'Failed to get Etherspot Prime SDK for chain!' });
continue;
Expand Down Expand Up @@ -123,17 +123,45 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
}));
}

const getEtherspotPrimeSdkForChainId = (sdkChainId: number, forceNewInstance: boolean = false) => {
const getEtherspotPrimeSdkForChainId = useCallback(async (sdkChainId: number, forceNewInstance: boolean = false) => {
if (etherspotPrimeSdkPerChain[sdkChainId] && !forceNewInstance) return etherspotPrimeSdkPerChain[sdkChainId];

if (!provider) return null;
if (!provider) {
console.warn('No initial provider set for Etherspot Prime');
return null;
}

const networkNames = Object.keys(NETWORK_NAME_TO_CHAIN_ID);
const matchingNetworkName = networkNames.find((networkName) => NETWORK_NAME_TO_CHAIN_ID[networkName] === sdkChainId);
if (!matchingNetworkName) return null;
if (!matchingNetworkName) {
console.warn(`No matching network name for Etherspot Prime chain ID ${sdkChainId}`);
return null;
}

let mappedProvider;

try {
// @ts-ignore
mappedProvider = isWalletProvider(provider) ? provider : new Web3WalletProvider(provider);
} catch (e) {
console.warn('Failed to map Etherspot Prime provider', e);
}

if (!mappedProvider) {
console.warn('No mapped provider set for Etherspot Prime');
return null;
}

try {
// @ts-ignore
if (mappedProvider) await mappedProvider.refresh();
} catch (e) {
// this should be ok to throw warning but proceed
console.warn('Failed to refresh Etherspot Prime provider', e);
}

// @ts-ignore
const sdkForChain = new PrimeSdk(provider, {
const sdkForChain = new PrimeSdk(mappedProvider, {
networkName: matchingNetworkName,
env: isTestnetChainId(sdkChainId) ? EnvNames.TestNets : EnvNames.MainNets,
});
Expand All @@ -144,7 +172,7 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
}

return sdkForChain;
}
}, [provider]);

const send = async (batchesIds?: string[]): Promise<ISentBatches[]> => {
const groupedBatchesToClean = Object.values<IBatches>(groupedBatchesPerId)
Expand All @@ -158,7 +186,7 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
const batchChainId = batch.chainId ?? chainId;

if (via === 'etherspot-prime') {
const etherspotPrimeSdkForChainId = getEtherspotPrimeSdkForChainId(batchChainId);
const etherspotPrimeSdkForChainId = await getEtherspotPrimeSdkForChainId(batchChainId);
if (!etherspotPrimeSdkForChainId) return;
await etherspotPrimeSdkForChainId.clearTransactionsFromBatch();
return;
Expand All @@ -178,34 +206,36 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether

const sentBatches: SentBatch[] = [];

const via = estimatedBatches.via ?? 'etherspot';

// send in same order as estimated
for (const estimatedBatch of estimatedBatches.estimatedBatches) {
const batchChainId = estimatedBatch.chainId ?? chainId

// return error message as provided by estimate
if (estimatedBatch.errorMessage) {
sentBatches.push({ ...estimatedBatch, errorMessage: estimatedBatch.errorMessage });
sentBatches.push({ ...estimatedBatch, via, errorMessage: estimatedBatch.errorMessage })
continue;
}

if (estimatedBatches.via === 'etherspot-prime') {
const etherspotPrimeSdkForChainId = getEtherspotPrimeSdkForChainId(batchChainId);
const etherspotPrimeSdkForChainId = await getEtherspotPrimeSdkForChainId(batchChainId);
if (!etherspotPrimeSdkForChainId) {
sentBatches.push({ ...estimatedBatch, errorMessage: 'Failed to get Etherspot Prime SDK for chain!' });
sentBatches.push({ ...estimatedBatch, via, errorMessage: 'Failed to get Etherspot Prime SDK for chain!' });
continue;
}

try {
const op = await etherspotPrimeSdkForChainId.sign();
const batchHash = await etherspotPrimeSdkForChainId.send(op);
sentBatches.push({ ...estimatedBatch, batchHash });
const userOpHash = await etherspotPrimeSdkForChainId.send(op);
sentBatches.push({ ...estimatedBatch, via, userOpHash });
} catch (e) {
sentBatches.push({ ...estimatedBatch, errorMessage: (e instanceof Error && e.message) || 'Failed to estimate!' });
sentBatches.push({ ...estimatedBatch, via, errorMessage: (e instanceof Error && e.message) || 'Failed to estimate!' });
}
} else {
const sdkForChainId = getSdkForChainId(batchChainId);
if (!sdkForChainId) {
sentBatches.push({ ...estimatedBatch, errorMessage: 'Failed to get Etherspot SDK for chain!' });
sentBatches.push({ ...estimatedBatch, via, errorMessage: 'Failed to get Etherspot SDK for chain!' });
continue;
}

Expand All @@ -215,11 +245,11 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
// testnets does not have guards
const guarded = !isTestnetChainId(batchChainId);
const { hash: batchHash } = await sdkForChainId.submitGatewayBatch({ guarded });
sentBatches.push({ ...estimatedBatch, batchHash });
sentBatches.push({ ...estimatedBatch, via, batchHash });
} catch (e) {
const rawErrorMessage = e instanceof Error && e.message;
const errorMessage = parseEtherspotErrorMessageIfAvailable(rawErrorMessage || 'Failed to send!');
sentBatches.push({ ...estimatedBatch, errorMessage });
sentBatches.push({ ...estimatedBatch, via, errorMessage });
}
}
}
Expand All @@ -235,9 +265,11 @@ const EtherspotTransactionKitContextProvider = ({ children, chainId = 1 }: Ether
estimate,
send,
chainId,
getEtherspotPrimeSdkForChainId,
}), [
chainId,
groupedBatchesPerId,
getEtherspotPrimeSdkForChainId,
]);

return (
Expand Down
12 changes: 10 additions & 2 deletions src/types/EtherspotTransactionKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@ export interface EstimatedBatch extends IBatch {
cost?: BigNumber;
}

export interface SentBatch extends EstimatedBatch {
errorMessage?: string;
export interface EtherspotSentBatch extends EstimatedBatch {
via: 'etherspot';
batchHash?: string;
}
export interface EtherspotPrimeSentBatch extends EstimatedBatch {
via: 'etherspot-prime';
userOpHash?: string;
}

export type SentBatch = {
errorMessage?: string;
} & (EtherspotSentBatch | EtherspotPrimeSentBatch);

export interface IBatches {
id?: string;
Expand Down

0 comments on commit 902bc1c

Please sign in to comment.