-
Notifications
You must be signed in to change notification settings - Fork 320
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
enhancement: Change signature of ClientOption.signTransaction to be able to pass KeyPair #1063 #1127
base: master
Are you sure you want to change the base?
Conversation
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.
This looks pretty good! Please update at least some of the e2e
tests to show that it works. If it's easiest, you can change just test/e2e/src/util.ts
to pass the keypair instead of using basicNodeSigner
, which will update all the tests. But then we will need one new test using basicNodeSigner
to ensure that both approaches continue to work.
@chadoh changes implemented. |
Hi @chadoh , please review, change has been implemented. |
Hi @chadoh , please review |
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.
I would not like to see the signTransaction get modified to be a signTransaction or a keypair... rather add keypair as an optional parameter to clientoptions, then update the sign and sign and send functions in the assembled_transaction file. I tried to leave comments and suggestions on how to fix. you'll need to also solve the tests tho.
// Check if signTransaction is a Keypair | ||
let signFunction: SignTransaction | undefined; | ||
|
||
if (signTransaction instanceof Keypair) { | ||
const keypair = signTransaction; | ||
signFunction = basicNodeSigner(keypair, this.options.networkPassphrase).signTransaction; | ||
} else if (typeof signTransaction === 'function') { | ||
signFunction = signTransaction; | ||
} | ||
|
||
if (!signFunction) { |
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.
This could be less verbose, and arguably safer...
signFunction
doesn't need to be a let it can be a const
// Check if signTransaction is a Keypair | |
let signFunction: SignTransaction | undefined; | |
if (signTransaction instanceof Keypair) { | |
const keypair = signTransaction; | |
signFunction = basicNodeSigner(keypair, this.options.networkPassphrase).signTransaction; | |
} else if (typeof signTransaction === 'function') { | |
signFunction = signTransaction; | |
} | |
if (!signFunction) { | |
if (!signTransaction && !keyPair) { | |
throw new AssembledTransaction.Errors.NoSigner( | |
"You must provide a signTransaction function, or a keypair either when calling " + | |
"`signAndSend` or when initializing your Client" | |
); | |
}; | |
const signFunction = signTransaction ? signTransaction : basicNodeSigner(keypair, this.options.networkPassphrase).signTransaction |
However you'll also need to remove lines 699-703 also.
Further, you shouldn't modify the type SignTransaction you should just add keypair to clientoptions as an optional and update the sign
function here around line 664... you also need to look at the signandsend
function for consistency and update that correctly.
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.
you might be wondering what !keyPair is yet later in this review i suggest modifying the sign function to accept either the function or the keypair, then narrow it. There's a bunch of ways to do this but there's no easy way for me to show exactly how i would do it without making a whole new pr which i am not gonna do. Anyways if you have questions about my review feel free to reach out. thanks.
// const signOpts: Parameters<NonNullable<ClientOptions['signTransaction']>>[1] = { | ||
// networkPassphrase: this.options.networkPassphrase, | ||
// }; | ||
const signOpts: Parameters<SignTransaction>[1] = { |
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.
I think there's a better way to do this whole thing than modifying the signTransaction
type which is supposed to match the function signature from freighter. Instead, modify the sign function to just either take a signTransaction
OR a Keypair
, then check if signTransaction
is falsey you should build it using that simple node signer...
// const signOpts: Parameters<NonNullable<ClientOptions['signTransaction']>>[1] = { | |
// networkPassphrase: this.options.networkPassphrase, | |
// }; | |
const signOpts: Parameters<SignTransaction>[1] = { | |
const signOpts: Parameters<SignTransaction>[1] = { |
it's fine to remove NonNullable here since it no longer is, but basically it needs to include EITHER signTransaction as a SignTransaction type or a keyPair as a KeyPair type...
@@ -131,7 +132,7 @@ export type ClientOptions = { | |||
* | |||
* Matches signature of `signTransaction` from Freighter. | |||
*/ | |||
signTransaction?: SignTransaction; | |||
signTransaction?: SignTransaction | Keypair; |
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.
signTransaction?: SignTransaction | Keypair; | |
signTransaction?: SignTransaction; | |
keyPair?: Keypair; |
please, let's avoid overloading signTransaction
with a Keypair
. Keep Freighter’s SignTransaction
type intact and handle a Keypair
as a separate field. Then in sign
, pick whichever one is provided—if we have a Keypair
, wrap it with basicNodeSigner
; if not, use signTransaction
. That way, the code stays clean and you don’t break the original type. For example:
sign = async ({
force = false,
signTransaction = this.options.signTransaction,
keypair = this.options.keypair,
}: {
force?: boolean;
signTransaction?: SignTransaction;
keypair?: Keypair;
} = {}): Promise<void> => {
if (!this.built) throw new Error("Transaction has not been simulated yet.");
if (!force && this.isReadCall) {
throw new AssembledTransaction.Errors.NoSignatureNeeded("Read call; no signature needed unless `force: true`.");
}
let fn = signTransaction;
if (keypair) {
fn = basicNodeSigner(keypair, this.options.networkPassphrase).signTransaction;
}
if (!fn) {
throw new AssembledTransaction.Errors.NoSigner("Provide either a `signTransaction` function or a `keypair`.");
}
// Prepare signing options
const signOpts: Parameters<SignTransaction>[1] = {
networkPassphrase: this.options.networkPassphrase,
address: this.options.address,
submit: this.options.submit,
submitUrl: this.options.submitUrl,
};
const { signedTxXdr, error } = await fn(this.built.toXDR(), signOpts);
this.handleWalletError(error);
this.signed = TransactionBuilder.fromXDR(signedTxXdr, this.options.networkPassphrase);
};
That’s it. You either provide a regular signTransaction
method or a Keypair
, never both in the same field, and you don’t tangle up the types.
|
||
it("signs transaction using Keypair", async function () { | ||
const result = await this.context.tx.sign({ | ||
signTransaction: this.context.keypair, |
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.
signTransaction should be the function not a keypair.
Change signature of ClientOption.signTransaction to be able to pass KeyPair #1063
Closes #1063
Pull Request Description
Summary
This pull request modifies the
ClientOptions
type to allow thesignTransaction
property to accept aKeypair
in addition to a function. This change simplifies the process of signing transactions, especially for testing and simple applications, by allowing users to directly use aKeypair
without needing to implement a custom signing function.Changes Made
signTransaction
property to accept either aSignTransaction
function or aKeypair
.sign
method to handle the case wheresignTransaction
is aKeypair
.Keypair
is provided, it uses thebasicNodeSigner
to create a signing function.