-
Notifications
You must be signed in to change notification settings - Fork 237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add wallet-rpc-provider
to support various extension wallets
#994
base: master
Are you sure you want to change the base?
Conversation
|
wallet-rpc-provider
to support various extension walletwallet-rpc-provider
to support various extension wallets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add unit tests for this change
What is the benefit of having the consumer of this class provide a Overall these changes don't seem like a great candidate for a new |
Hi @andy-haynes , Thank you for your comments.
|
jsonrpc: '2.0' | ||
}; | ||
const response = await this._provider.request(request); | ||
if (Array.isArray(response)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Under what conditions is response
an array? Is this behavior that this._provider.request
is required to implement?
* | ||
* @param transactions The transactions being sent | ||
*/ | ||
async signAndSendTransaction(transactions: Transaction[]): Promise<FinalExecutionOutcome> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will end up calling this? Is signAndSendTransation
a valid RPC method?
this._provider = provider; | ||
this._provider.on('chainChanged', this.updateNetwork.bind(this)); | ||
this._provider.on('accountsChanged', this.updateAccount.bind(this)); | ||
this.init(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Firing and forgetting an async method in the constructor here is problematic:
- Any rejections will be unhandled
- There is no way of knowing when this method has completed and thus when the object has been initialized
This initialization must occur outside of this class or as an explicit secondary step after instantiation for behavior dependent on init
's resolution to be reliable at all.
Thanks @Yoon-Suji!
The Would it be possible to get an example of a
I think I'm still not quite understanding - why does need to be in a My concern with the network and account monitoring is that it makes the |
Hey @andy-haynes! thanks for your comment and sorry for late response🥲
interface Wallet {
id: string;
connected: boolean;
network: Network;
accounts: Array<Account>;
supportsNetwork(networkId: string): Promise<boolean>;
connect(params: ConnectParams): Promise<Array<Account>>;
signIn(params: SignInParams): Promise<void>;
signOut(params: SignOutParams): Promise<void>;
signTransaction(params: SignTransactionParams): Promise<transactions.SignedTransaction>;
signTransactions(params: SignTransactionsParams): Promise<Array<transactions.SignedTransaction>>;
disconnect(): Promise<void>;
on<EventName extends keyof Events>(
event: EventName,
callback: (params: Events[EventName]) => void
): Unsubscribe;
off<EventName extends keyof Events>(
event: EventName,
callback?: () => void
): void;
} |
which can be used to interact with the NEAR RPC API using provider in injected wallet
which signs using NEAR injected wallet
fix near connect to support wallet-rpc-provider and injected-wallet-signer
hi @andy-haynes ! We have couple of changes in our code. If you don't mind, could you check it out? Thank you :) DescriptionThere are some extension wallets that have their own RPC provider. I think that using another It can connect with the extension wallet using a property called Changes
Example (when using WELLDONE wallet)class Provider implements Wallet {
id: string;
network: Network;
accounts: Account[];
constructor() {
this.id = 'welldone';
this.network = { networkId: '', nodeUrl: '' };
this.accounts = [];
}
get connected() {
return Boolean(this.accounts.length);
}
on(event: keyof Events, listener: (...args: any[]) => void) {
let message;
if (event === 'accountsChanged') {
message = 'dapp:accountsChanged';
} else if (event === 'networkChanged') {
message = 'dapp:chainChanged';
}
(window as any).dapp.on(message, listener);
return listener;
}
off<EventName extends keyof Events>(event: EventName, callback?: (() => void) | undefined): void {
throw new Error('Method not implemented.');
}
async request(args: RequestParams) {
// wallet required to implement signAndSendTransaction
if (args.method === 'signAndSendTransaction') {
args.method = 'dapp:sendTransaction';
}
const result = await (window as any).dapp.request('near', args);
return result;
}
async connect() {
if (!(window as any).dapp) {
throw new Error('Wallet is not installed');
}
const result = await (window as any).dapp.request('near', { method: 'dapp:accounts' });
if (result['near'] && result['near'].address) {
const accounts = {
accountId: result['near'].address,
publicKey: nearAPI.utils.PublicKey.from(result['near'].pubKey),
};
this.accounts = [accounts];
return this.accounts;
}
this.network.networkId = (window as any).dapp.networks.near.chain;
return [];
}
async signTransaction(): Promise<nearAPI.transactions.SignedTransaction> {
throw new Error('This method is not necessary for this example.');
}
async signMessage(): Promise<nearAPI.transactions.Signature> {
throw new Error('This method is not necessary for this example.');
}
}
const config: nearAPI.NearConfig = {
networkId: 'testnet',
keyStore: new nearAPI.keyStores.BrowserLocalStorageKeyStore(),
nodeUrl: 'https://rpc.testnet.near.org',
wallet: new Provider(),
};
const accountTest = async () => {
// create NEAR connection
const nearConnection = await nearAPI.connect(config as nearAPI.ConnectConfig);
// create Extension Wallet connection
const injectedWalletConnection = await new nearAPI.InjectedWalletConnection(nearConnection);
// request user to sign in
await injectedWalletConnection.signIn();
// get current connected account from extension wallet
const account = await injectedWalletConnection.account();
// account.viewFunction
const view_result = await account.viewFunction('contract.myaccount8.testnet', 'get_num', {});
console.log('view_result: ', view_result);
// account.functionCall
const call_receipt = await account.functionCall({
contractId: 'contract.myaccount8.testnet',
methodName: 'increment',
args: { count: 10 },
});
console.log('function_call_receipt: ', call_receipt);
};
const contractTest = async () => {
// create NEAR connection
const nearConnection = await nearAPI.connect(config as nearAPI.ConnectConfig);
// create Extension Wallet connection
const injectedWalletConnection = await new nearAPI.InjectedWalletConnection(nearConnection);
// request user to sign in
await injectedWalletConnection.signIn();
// get current connected account from extension wallet
const account = await injectedWalletConnection.account();
// load contract using extension wallet account
const contract = await new nearAPI.Contract(account, 'contract.myaccount8.testnet', {
viewMethods: ['get_num'],
changeMethods: ['increment', 'reset'],
});
// call contract
await (contract as any).reset();
// view contract
console.log('view_contract_result: ', await (contract as any).get_num());
}; |
Motivation
To provide a universal provider that can be used by NEAR-supported extension wallets.
Description
near-api-js/packages/near-api-js/src/providers/wallet-rpc-provider.ts
chainChanged
event when a network change inside the chain.accountsChanged
event when an account change in wallet.sendJsonRpc
method, it receives transaction hash response in array type.WalletProvider
interface according to the wallet you want to use.Checklist
WalletProvider
interface abstracted for compatibility with various extension wallet.chainChanged
,accountsChanged
actionsasync signAndSendTransaction(transactions: Transaction[]): Promise<FinalExecutionOutcome>
to sign and send transactions to the RPC and wait until transaction is fully complete.