-
Notifications
You must be signed in to change notification settings - Fork 10
spike: tree shakeable algorand client #437
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,31 @@ | ||
import algosdk, { Address } from 'algosdk' | ||
import { MultisigAccount, SigningAccount, TransactionSignerAccount } from './account' | ||
import { AccountManager } from './account-manager' | ||
import algosdk, { Address, Algodv2 } from 'algosdk' | ||
import type { AccountManager } from './account-manager' | ||
import { AlgorandClientTransactionCreator } from './algorand-client-transaction-creator' | ||
import { AlgorandClientTransactionSender } from './algorand-client-transaction-sender' | ||
import { AppDeployer } from './app-deployer' | ||
import { AppManager } from './app-manager' | ||
import { AssetManager } from './asset-manager' | ||
import { AlgoSdkClients, ClientManager } from './client-manager' | ||
import type { AppDeployer } from './app-deployer' | ||
import type { AppManager } from './app-manager' | ||
import type { AssetManager } from './asset-manager' | ||
import type { AlgoSdkClients, ClientManager } from './client-manager' | ||
Comment on lines
+5
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove remove the dependency on all of the managers from the AlgorandClient |
||
import { ErrorTransformer, TransactionComposer } from './composer' | ||
import { AlgoConfig } from './network-client' | ||
import Account = algosdk.Account | ||
import LogicSigAccount = algosdk.LogicSigAccount | ||
import { InterfaceOf } from './instance-of' | ||
|
||
type AlgorandClientConfig = Partial<AlgoSdkClients> & { | ||
clientManager?: Partial<InterfaceOf<ClientManager>> | ||
accountManager?: Partial<InterfaceOf<AccountManager>> | ||
appManager?: Partial<InterfaceOf<AppManager>> | ||
assetManager?: Partial<InterfaceOf<AssetManager>> | ||
appDeployer?: Partial<InterfaceOf<AppDeployer>> | ||
Comment on lines
+13
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Partial interfaces of the managers are passed in to AlgorandClient ctor |
||
} | ||
|
||
/** | ||
* A client that brokers easy access to Algorand functionality. | ||
*/ | ||
export class AlgorandClient { | ||
private _clientManager: ClientManager | ||
private _accountManager: AccountManager | ||
private _appManager: AppManager | ||
private _appDeployer: AppDeployer | ||
private _assetManager: AssetManager | ||
private _clientManager: Partial<InterfaceOf<ClientManager>> | ||
private _accountManager: Partial<InterfaceOf<AccountManager>> | ||
private _appManager: Partial<InterfaceOf<AppManager>> | ||
private _appDeployer: Partial<InterfaceOf<AppDeployer>> | ||
private _assetManager: Partial<InterfaceOf<AssetManager>> | ||
private _transactionSender: AlgorandClientTransactionSender | ||
private _transactionCreator: AlgorandClientTransactionCreator | ||
|
||
|
@@ -30,21 +35,30 @@ export class AlgorandClient { | |
|
||
private _defaultValidityWindow: bigint | undefined = undefined | ||
|
||
private _algod: Algodv2 | ||
|
||
/** | ||
* A set of error transformers to use when an error is caught in simulate or execute | ||
* `registerErrorTransformer` and `unregisterErrorTransformer` can be used to add and remove | ||
* error transformers from the set. | ||
*/ | ||
private _errorTransformers: Set<ErrorTransformer> = new Set() | ||
|
||
private constructor(config: AlgoConfig | AlgoSdkClients) { | ||
this._clientManager = new ClientManager(config, this) | ||
this._accountManager = new AccountManager(this._clientManager) | ||
this._appManager = new AppManager(this._clientManager.algod) | ||
this._assetManager = new AssetManager(this._clientManager.algod, () => this.newGroup()) | ||
this._transactionSender = new AlgorandClientTransactionSender(() => this.newGroup(), this._assetManager, this._appManager) | ||
private constructor(config: AlgorandClientConfig) { | ||
const algod = config.algod ?? config.clientManager?.algod | ||
|
||
if (algod === undefined) { | ||
throw new Error('An algod client must be provided in the config or clientManager') | ||
} | ||
|
||
this._algod = algod | ||
this._clientManager = config.clientManager ?? {} | ||
this._accountManager = config.accountManager ?? {} | ||
this._appManager = config.appManager ?? {} | ||
this._assetManager = config.assetManager ?? {} | ||
this._transactionSender = new AlgorandClientTransactionSender(() => this.newGroup(), this._algod) | ||
this._transactionCreator = new AlgorandClientTransactionCreator(() => this.newGroup()) | ||
this._appDeployer = new AppDeployer(this._appManager, this._transactionSender, this._clientManager.indexerIfPresent) | ||
this._appDeployer = config.appDeployer ?? {} | ||
} | ||
|
||
/** | ||
|
@@ -61,57 +75,6 @@ export class AlgorandClient { | |
return this | ||
} | ||
|
||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The static methods would live in a new "Builder" class. This would be the main difference as far as users are concerned. |
||
* Sets the default signer to use if no other signer is specified. | ||
* @param signer The signer to use, either a `TransactionSigner` or a `TransactionSignerAccount` | ||
* @returns The `AlgorandClient` so method calls can be chained | ||
* @example | ||
* ```typescript | ||
* const signer = new SigningAccount(account, account.addr) | ||
* const algorand = AlgorandClient.mainNet().setDefaultSigner(signer) | ||
* ``` | ||
*/ | ||
public setDefaultSigner(signer: algosdk.TransactionSigner | TransactionSignerAccount): AlgorandClient { | ||
this._accountManager.setDefaultSigner(signer) | ||
return this | ||
} | ||
|
||
/** | ||
* Tracks the given account (object that encapsulates an address and a signer) for later signing. | ||
* @param account The account to register, which can be a `TransactionSignerAccount` or | ||
* a `algosdk.Account`, `algosdk.LogicSigAccount`, `SigningAccount` or `MultisigAccount` | ||
* @example | ||
* ```typescript | ||
* const accountManager = AlgorandClient.mainNet() | ||
* .setSignerFromAccount(algosdk.generateAccount()) | ||
* .setSignerFromAccount(new algosdk.LogicSigAccount(program, args)) | ||
* .setSignerFromAccount(new SigningAccount(account, sender)) | ||
* .setSignerFromAccount(new MultisigAccount({version: 1, threshold: 1, addrs: ["ADDRESS1...", "ADDRESS2..."]}, [account1, account2])) | ||
* .setSignerFromAccount({addr: "SENDERADDRESS", signer: transactionSigner}) | ||
* ``` | ||
* @returns The `AlgorandClient` so method calls can be chained | ||
*/ | ||
public setSignerFromAccount(account: TransactionSignerAccount | Account | LogicSigAccount | SigningAccount | MultisigAccount) { | ||
this._accountManager.setSignerFromAccount(account) | ||
return this | ||
} | ||
|
||
/** | ||
* Tracks the given signer against the given sender for later signing. | ||
* @param sender The sender address to use this signer for | ||
* @param signer The signer to sign transactions with for the given sender | ||
* @returns The `AlgorandClient` so method calls can be chained | ||
* @example | ||
* ```typescript | ||
* const signer = new SigningAccount(account, account.addr) | ||
* const algorand = AlgorandClient.mainNet().setSigner(signer.addr, signer.signer) | ||
* ``` | ||
*/ | ||
public setSigner(sender: string | Address, signer: algosdk.TransactionSigner) { | ||
this._accountManager.setSigner(sender, signer) | ||
return this | ||
} | ||
|
||
/** | ||
* Sets a cache value to use for suggested transaction params. | ||
* @param suggestedParams The suggested params to use | ||
|
@@ -155,7 +118,7 @@ export class AlgorandClient { | |
} | ||
} | ||
|
||
this._cachedSuggestedParams = await this._clientManager.algod.getTransactionParams().do() | ||
this._cachedSuggestedParams = await this._algod.getTransactionParams().do() | ||
this._cachedSuggestedParamsExpiry = new Date(new Date().getTime() + this._cachedSuggestedParamsTimeout) | ||
|
||
return { | ||
|
@@ -170,7 +133,7 @@ export class AlgorandClient { | |
* const clientManager = AlgorandClient.mainNet().client; | ||
*/ | ||
public get client() { | ||
return this._clientManager | ||
return this._clientManager ?? { algod: this._algod } | ||
} | ||
|
||
/** | ||
|
@@ -232,12 +195,16 @@ export class AlgorandClient { | |
* const result = await composer.addTransaction(payment).send() | ||
*/ | ||
public newGroup() { | ||
const errorGetSigner = (addr: string | Address) => { | ||
throw new Error(`No signer available for address ${addr}`) | ||
} | ||
const getSigner = this.account.getSigner ?? errorGetSigner | ||
|
||
return new TransactionComposer({ | ||
algod: this.client.algod, | ||
getSigner: (addr: string | Address) => this.account.getSigner(addr), | ||
algod: this._algod, | ||
getSigner, | ||
getSuggestedParams: () => this.getSuggestedParams(), | ||
defaultValidityWindow: this._defaultValidityWindow, | ||
appManager: this._appManager, | ||
errorTransformers: [...this._errorTransformers], | ||
}) | ||
} | ||
|
@@ -269,93 +236,4 @@ export class AlgorandClient { | |
public get createTransaction() { | ||
return this._transactionCreator | ||
} | ||
|
||
// Static methods to create an `AlgorandClient` | ||
|
||
/** | ||
* Creates an `AlgorandClient` pointing at default LocalNet ports and API token. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const algorand = AlgorandClient.defaultLocalNet(); | ||
*/ | ||
public static defaultLocalNet() { | ||
return new AlgorandClient({ | ||
algodConfig: ClientManager.getDefaultLocalNetConfig('algod'), | ||
indexerConfig: ClientManager.getDefaultLocalNetConfig('indexer'), | ||
kmdConfig: ClientManager.getDefaultLocalNetConfig('kmd'), | ||
}) | ||
} | ||
|
||
/** | ||
* Creates an `AlgorandClient` pointing at TestNet using AlgoNode. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const algorand = AlgorandClient.testNet(); | ||
*/ | ||
public static testNet() { | ||
return new AlgorandClient({ | ||
algodConfig: ClientManager.getAlgoNodeConfig('testnet', 'algod'), | ||
indexerConfig: ClientManager.getAlgoNodeConfig('testnet', 'indexer'), | ||
kmdConfig: undefined, | ||
}) | ||
} | ||
|
||
/** | ||
* Creates an `AlgorandClient` pointing at MainNet using AlgoNode. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const algorand = AlgorandClient.mainNet(); | ||
*/ | ||
public static mainNet() { | ||
return new AlgorandClient({ | ||
algodConfig: ClientManager.getAlgoNodeConfig('mainnet', 'algod'), | ||
indexerConfig: ClientManager.getAlgoNodeConfig('mainnet', 'indexer'), | ||
kmdConfig: undefined, | ||
}) | ||
} | ||
|
||
/** | ||
* Creates an `AlgorandClient` pointing to the given client(s). | ||
* @param clients The clients to use. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const algorand = AlgorandClient.fromClients({ algod, indexer, kmd }); | ||
*/ | ||
public static fromClients(clients: AlgoSdkClients) { | ||
return new AlgorandClient(clients) | ||
} | ||
|
||
/** | ||
* Creates an `AlgorandClient` loading the configuration from environment variables. | ||
* | ||
* Retrieve configurations from environment variables when defined or get default LocalNet configuration if they aren't defined. | ||
* | ||
* Expects to be called from a Node.js environment. | ||
* | ||
* If `process.env.ALGOD_SERVER` is defined it will use that along with optional `process.env.ALGOD_PORT` and `process.env.ALGOD_TOKEN`. | ||
* | ||
* If `process.env.INDEXER_SERVER` is defined it will use that along with optional `process.env.INDEXER_PORT` and `process.env.INDEXER_TOKEN`. | ||
* | ||
* If either aren't defined it will use the default LocalNet config. | ||
* | ||
* It will return a KMD configuration that uses `process.env.KMD_PORT` (or port 4002) if `process.env.ALGOD_SERVER` is defined, | ||
* otherwise it will use the default LocalNet config unless it detects testnet or mainnet. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const client = AlgorandClient.fromEnvironment(); | ||
*/ | ||
public static fromEnvironment() { | ||
return new AlgorandClient(ClientManager.getConfigFromEnvironmentOrLocalNet()) | ||
} | ||
|
||
/** | ||
* Creates an `AlgorandClient` from the given config. | ||
* @param config The config to use. | ||
* @returns An instance of the `AlgorandClient`. | ||
* @example | ||
* const client = AlgorandClient.fromConfig({ algodConfig, indexerConfig, kmdConfig }); | ||
*/ | ||
public static fromConfig(config: AlgoConfig) { | ||
return new AlgorandClient(config) | ||
} | ||
} |
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.
We're able to completely remove the dependency on the asset and app manager