Skip to content
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

Transaction builder class rebased #998

Open
wants to merge 23 commits into
base: refactor/transaction-sending-1
Choose a base branch
from

Conversation

esaminu
Copy link
Collaborator

@esaminu esaminu commented Sep 27, 2022

Motivation

Updated and rebased #891

Description

Adds builder pattern transaction creation:

bob.createTransaction("alice.near").transfer("1000000000000000000").signAndSend();

Checklist

  • Read the contributing guidelines
  • Commit messages follow the conventional commits spec
  • Performed a self-review of the PR
  • Added automated tests
  • Manually tested the change

@changeset-bot
Copy link

changeset-bot bot commented Sep 27, 2022

🦋 Changeset detected

Latest commit: e58229d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
near-api-js Minor
@near-js/cookbook Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Collaborator

@morgsmccauley morgsmccauley left a comment

Choose a reason for hiding this comment

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

Looks good, just a few comments :)

.changeset/rich-rice-laugh.md Outdated Show resolved Hide resolved
packages/cookbook/transactions/batch-transactions.js Outdated Show resolved Hide resolved
packages/cookbook/transactions/batch-transactions.js Outdated Show resolved Hide resolved
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/test/account.test.js Outdated Show resolved Hide resolved
packages/near-api-js/test/account.test.js Outdated Show resolved Hide resolved
@esaminu esaminu force-pushed the feat/tx_class branch 2 times, most recently from dcc84e1 to 31810be Compare September 30, 2022 16:27
@esaminu esaminu changed the base branch from master to NAJ-44-make-sign-and-send-transaction-functionality-public September 30, 2022 16:28
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
Base automatically changed from NAJ-44-make-sign-and-send-transaction-functionality-public to master October 3, 2022 11:05
Copy link
Collaborator

@andy-haynes andy-haynes left a comment

Choose a reason for hiding this comment

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

The refactor here seems a little heavy for what it accomplishes, I'd prefer if we explored implementing a "dumb" TransactionBuilder instead of one that overlaps functionality in the Account class.

@@ -687,4 +485,12 @@ export class Account {
total: summary.total.toString(),
};
}

createTransaction(receiver: Account | string): TransactionBuilder {
return new TransactionBuilder(this.connection, this.accountId, typeof receiver === 'string' ? receiver : receiver.accountId);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rather than refactor the Account class to share some functionality, why not pass the signTransaction and signAndSendTransaction methods to the new class? TransactionBuilder doesn't need to know how to send transactions, as evidenced by the name 🙂

Passing the methods would eliminate the need to break up the Account class, which seems to have been done solely for the purpose of factoring out two methods (and their dependencies). What would you think about something like this instead?

createTransaction(receiverId: string): TransactionBuilder {
  return new TransactionBuilder({
    signTransaction: (actions: Action[]) => this.signTransaction(this.accountId, actions),
    signAndSendTransaction: (actions: Action[]) => this.signAndSendTransactions({ receiverId: this.accountId, actions }),
  });
}
...
class TransactionBuilder {
  signTransaction: (actions: Action[]) => Promise<[Uint8Array, SignedTransaction]>;
  signAndSendTransaction: (actions: Action[]) => Promise<FinalExecutionOutcome>;

  constructor({ signTransaction, signAndSendTransaction }) {
    this.signTransaction = signTransaction;
    this.signAndSendTransaction = signAndSendTransaction;
  }
  ...
}

Now TransactionBuilder doesn't need a connection or accountId - it just builds transactions and exposes sign and signAndSend methods that look like

sign(): Promise<[Uint8Array, SignedTransaction]> {
  return this.signTransaction(this.actions);
}

With this the refactor is not required and we can keep Account logic consolidated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is an interesting idea but it makes Account the only possible consumer of TransactionBuilder since there is only one way we'd like to send transactions (by caching accessKeys and hitting rpc with exponential backoff) and that way would reside solely in Account unless the user decided to reimplement it. Users and other classes are more likely to have a Connection instance, senderId and receiverId than an implementation for signing transactions and sending them.

Since we'd like to hold this implementation of sending transactions constant it is not specific to Account instances and should either reside elsewhere or at least as a static method. That way it can be reused independently of Account in future by other implementations attempting to sign and send transactions. Separating out how Account sends transactions from Account specific functionality has been overdue imo.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see what you mean about its use outside of Account, thanks for the clarification.

Separating out how Account sends transactions from Account specific functionality has been overdue imo.

I think you're 100% right on this, however I would argue that factoring this behavior out into a parent class doesn't go far enough in separating these concerns. Inheritance may be more convenient in the short term for this but we have an opportunity to use composition to accomplish the same thing in a more robust way. By passing instances of TransactionSender to Account and TransactionBuilder rather than putting this in a parent class, we can avoid the coupling that inheritance guarantees.

Another benefit is that mocking becomes trivial - different TransactionSender mocks (e.g. one for throwing an error, another returning unexpected data, etc.) could be shared across unit tests, eliminating the need for transaction signing integration tests on consumers of TransactionBuilder.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Great points. I rebased this PR on #1018 and #1019 👍

packages/near-api-js/src/account.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/constants.ts Outdated Show resolved Hide resolved
.changeset/rich-rice-laugh.md Outdated Show resolved Hide resolved
packages/near-api-js/src/transaction_builder.ts Outdated Show resolved Hide resolved
packages/near-api-js/src/transaction_builder.ts Outdated Show resolved Hide resolved
@esaminu esaminu changed the base branch from master to refactor/transaction-sending-1 October 18, 2022 22:38
@morgsmccauley morgsmccauley removed their request for review January 9, 2023 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants