11import { IWalletAdapter , RpcRequest } from "./IWalletAdapter" ;
2- import { Contract , ContractRunner , ContractTransaction , ethers , EventLog , FetchRequest , Network } from "ethers" ;
2+ import {
3+ Contract ,
4+ ContractRunner ,
5+ ContractTransaction ,
6+ ethers ,
7+ EventLog ,
8+ getBigInt ,
9+ Network , Overrides
10+ } from "ethers" ;
311import { ITransactionListener } from "../TokenScript" ;
412import { ErrorDecoder , ErrorType } from "ethers-decode-error" ;
513
@@ -48,18 +56,24 @@ export class EthersAdapter implements IWalletAdapter {
4856 return ( await contract . getFunction ( method ) . staticCall ( ...( args . map ( ( arg : any ) => arg . value ) ) ) ) ;
4957 }
5058
51- async sendTransaction ( chain : number , contractAddr : string , method : string , args : any [ ] , outputTypes : string [ ] , value ?: BigInt , waitForConfirmation : boolean = true , listener ?: ITransactionListener , errorAbi : any [ ] = [ ] ) {
59+ async sendTransaction ( chain : number , contractAddr : string , method : string , args : any [ ] , outputTypes : string [ ] , value ?: bigint , waitForConfirmation : boolean = true , listener ?: ITransactionListener , errorAbi : any [ ] = [ ] ) {
5260
5361 console . log ( "Send ethereum transaction. chain " + chain + "; contract " + contractAddr + "; method " + method + "; value " + value + "; args" , args ) ;
5462
55- await this . switchChain ( chain ) ;
63+ const res = await this . switchChain ( chain ) ;
64+
65+ // User rejection
66+ if ( ! res )
67+ return false ;
5668
5769 // TODO: if no method is set, send raw transaction? Is this allowed?
5870 // TODO: handle no-method transaction
5971
6072 const contract = await this . getEthersContractInstance ( chain , contractAddr , method , args , outputTypes , value ? "payable" : "nonpayable" , errorAbi ) ;
6173
62- const overrides : any = { } ;
74+ const overrides : Overrides = {
75+ chainId : chain
76+ } ;
6377
6478 if ( value )
6579 overrides . value = value ;
@@ -72,6 +86,10 @@ export class EthersAdapter implements IWalletAdapter {
7286 try {
7387 tx = await contract [ method ] ( ...( args . map ( ( arg : any ) => arg . value ) ) , overrides ) as ContractTransaction ;
7488 } catch ( e : any ) {
89+
90+ if ( EthersAdapter . isTransactionRejection ( e ) )
91+ return false ;
92+
7593 const errDecoder = ErrorDecoder . create ( errorAbi )
7694 const decodedError = await errDecoder . decode ( e ) ;
7795 console . error ( e ) ;
@@ -129,6 +147,14 @@ export class EthersAdapter implements IWalletAdapter {
129147 return tx ;
130148 }
131149
150+ private static isTransactionRejection ( e : any ) {
151+ return (
152+ e . message . indexOf ( "ACTION_REJECTED" ) > - 1 ||
153+ e . message . indexOf ( "Rejected by the user" ) > - 1 ||
154+ e . message . indexOf ( "User denied" ) > - 1
155+ ) ;
156+ }
157+
132158 private async getEthersContractInstance ( chain : number , contractAddr : string , method : string , args : any [ ] , outputTypes : string [ ] | any [ ] , stateMutability : string , errorAbi : any [ ] = [ ] ) {
133159
134160 const abiData = {
@@ -187,21 +213,62 @@ export class EthersAdapter implements IWalletAdapter {
187213 return Number ( network . chainId ) ;
188214 }
189215
190- private async switchChain ( chain : number ) {
216+ private async switchChain ( chain : number , tryToAdd = true ) : Promise < boolean > {
191217
192218 const ethersProvider = await this . getEthersProvider ( ) ;
193219
194- if ( chain != await this . getChain ( ) ) {
220+ // TODO: Metamask is currently returning an old chain ID when chain is switched via the metamask UI, so we need to call this everytime
221+ //console.log("Current chain: ", await this.getChain());
222+ //if (chain != await this.getChain()){
195223
196224 console . log ( "Switch chain: " , chain ) ;
197225
198226 try {
199227 await ethersProvider . send ( "wallet_switchEthereumChain" , [ { chainId : "0x" + chain . toString ( 16 ) } ] ) ;
228+ return true ;
200229 } catch ( e ) {
201230 console . error ( e ) ;
231+ if ( EthersAdapter . isTransactionRejection ( e ) )
232+ return false ;
233+
234+ if ( tryToAdd ) {
235+ await this . addChain ( chain ) ;
236+ return this . switchChain ( chain , false ) ;
237+ }
202238 throw new Error ( "Connected to wrong chain, please switch the chain to chainId: " + chain + ", error: " + e . message ) ;
203239 }
204- }
240+ //}
241+ }
242+
243+ private async addChain ( chain : number ) {
244+ console . info ( "Trying to add ethereumChain" ) ;
245+
246+ if ( ! this . chainConfig [ chain ] )
247+ throw new Error ( "Chain is not defined in TS engine config" ) ;
248+
249+ const chainList = await fetch ( "https://chainid.network/chains.json" ) . then ( ( res ) => res . json ( ) ) as any [ ] ;
250+
251+ const chainMeta = chainList . find ( ( meta ) => meta . chainId === chain ) ;
252+
253+ if ( ! chainMeta )
254+ throw new Error ( "Could not find chain config at chainid.network" ) ;
255+
256+ const chainIcons = await fetch ( "https://chainid.network/chain_icons.json" ) . then ( ( res ) => res . json ( ) ) as any [ ] ;
257+
258+ const icon = chainIcons . find ( ( meta ) => meta . name === chainMeta . icon ) ;
259+
260+ // TODO: Convert IPFS URLs
261+ const icons = icon ? [ icon . icons [ 0 ] . url ] : [ ] ;
262+
263+ const ethersProvider = await this . getEthersProvider ( ) ;
264+ await ethersProvider . send ( "wallet_addEthereumChain" , [ {
265+ chainId : "0x" + chain . toString ( 16 ) ,
266+ chainName : chainMeta . name ,
267+ iconUrls : icons ,
268+ rpcUrls : chainMeta . rpc . filter ( url => url . indexOf ( "${INFURA_API_KEY}" ) === - 1 ) ,
269+ nativeCurrency : chainMeta . nativeCurrency ,
270+ blockExplorerUrls : chainMeta . explorers ? chainMeta . explorers . map ( explorer => explorer . url ) : [ ]
271+ } ] ) ;
205272 }
206273
207274 async getCurrentWalletAddress ( ) {
0 commit comments