UNPKG

@zlattice/lattice-js

Version:

Lattice blockchain TypeScript SDK with dual module support (CJS + ESM)

269 lines 12.7 kB
import { HEX_PREFIX, TransactionTypes, ZERO_ADDRESS } from "../common/constants.js"; import { LatestBlock } from "../common/types/index.js"; import { createCrypto } from "../crypto/index.js"; import { BlockCacheImpl, newAccountLock } from "../lattice/index.js"; import { log } from "../logger.js"; import { HttpClientImpl, HttpProvider } from "../providers/http_provider.js"; import { FixedDelayStrategy, RetryHandler } from "../utils/index.js"; import { decryptFileKey } from "../wallet/index.js"; import { ResultAsync, errAsync, ok } from "neverthrow"; import { TransactionBuilder } from "./tx/index.js"; class Credentials { constructor(accountAddress, fileKey, passphrase, privateKey) { this.accountAddress = accountAddress; this.passphrase = passphrase; this.fileKey = fileKey; this.privateKey = privateKey; } static fromFileKey(accountAddress, fileKey, passphrase) { const result = decryptFileKey(fileKey, passphrase); if (result.isErr()) { throw result.error; } const privateKey = result.value; return new Credentials(accountAddress, fileKey, passphrase, privateKey); } static fromPrivateKey(accountAddress, privateKey) { return new Credentials(accountAddress, undefined, undefined, privateKey); } getAccountAddress() { return this.accountAddress; } getPrivateKey() { // TODO: check if private key is set, if not, throw an error if (!this.privateKey) { throw new Error("Private key is not set"); } return this.privateKey; } } class ChainConfig { constructor(curve, tokenLess) { this.curve = curve; this.tokenLess = tokenLess; } } class NodeConnectionConfig { constructor(ip, httpPort, wsPort, ginHttpPort, jwtSecret, jwtTokenExpirationDuration = 6 * 60 * 60, // 6 hours insecure = true) { this.insecure = insecure; this.ip = ip; this.httpPort = httpPort; this.wsPort = wsPort; this.ginHttpPort = ginHttpPort; this.jwtSecret = jwtSecret; this.jwtTokenExpirationDuration = jwtTokenExpirationDuration; } } class Options { constructor(options) { this.options = options; } get(key) { return this.options[key]; } } class LatticeClient { constructor(chainConfig, nodeConnectionConfig, options, accountLock, blockCache) { this.httpClient = new HttpClientImpl(new HttpProvider(`${nodeConnectionConfig.insecure ? "http" : "https"}://${nodeConnectionConfig.ip}:${nodeConnectionConfig.httpPort}`)); this.chainConfig = chainConfig; this.nodeConnectionConfig = nodeConnectionConfig; this.options = options ?? new Options({}); this.accountLock = accountLock ?? newAccountLock(); this.blockCache = blockCache ?? new BlockCacheImpl({ backend: "memory" }, 10); } /** * Wait for receipt * @param chainId The chain id * @param hash The hash * @param retryStrategy The retry strategy, default is FixedDelayStrategy.default * @param retries The number of retries, default is 10 * @returns E.Either<Receipt, Error>, left is the receipt, right is the error */ waitReceipt(chainId, hash, retryStrategy = FixedDelayStrategy.default, retries = 10) { const retryHandler = new RetryHandler(retryStrategy, retries); return ResultAsync.fromPromise(retryHandler.execute(async () => { return await this.httpClient.getReceipt(chainId, hash); }), (error) => { log.error(`Failed to get receipt: ${error}`); return error instanceof Error ? error : new Error(String(error)); }); } handleTransaction(chainId, credentials, transaction, block) { const signResult = transaction.signTx(chainId, this.chainConfig.curve, credentials.getPrivateKey()); if (signResult.isErr()) { return errAsync(signResult.error); } return ResultAsync.fromPromise(this.httpClient.sendTransaction(chainId, transaction), (error) => { log.error(`Failed to send transaction: ${error}`); return error instanceof Error ? error : new Error(String(error)); }).andThen((hash) => { block.currentTBlockNumber = block.currentTBlockNumber + 1; block.currentTBlockHash = hash; this.blockCache.putBlock(chainId, credentials.getAccountAddress(), block); return ok(hash); }); } /** * Transfer * @param credentials Your credentials * @param chainId The chain id * @param linker The linker address * @param payload The payload, should be a hex string * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @returns ResultAsync<string, Error>, left is the hash, right is the error */ transfer(credentials, chainId, linker, payload, amount = 0, joule = 0) { return this.accountLock.withLock(chainId, credentials.getAccountAddress(), async () => { const block = await this.blockCache.getBlock(chainId, credentials.getAccountAddress(), async (chainId, address) => { return await this.httpClient.getLatestBlock(chainId, address); }); const tx = TransactionBuilder.builder(TransactionTypes.Send) .setBlock(block) .setOwner(credentials.getAccountAddress()) .setLinker(linker) .setPayload(payload) .setAmount(amount) .setJoule(joule) .build(); return await this.handleTransaction(chainId, credentials, tx, block).match((hash) => hash, (error) => { throw error; }); }); } /** * Transfer and wait for receipt * @param credentials Your credentials * @param chainId The chain id * @param linker The linker address * @param payload The payload, should be a hex string * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @param retryStrategy The retry strategy, default is FixedDelayStrategy.default * @param retries The number of retries, default is 10 * @returns E.Either<Receipt, Error>, left is the receipt, right is the error */ transferWaitReceipt(credentials, chainId, linker, payload, amount = 0, joule = 0, retryStrategy = FixedDelayStrategy.default, retries = 10) { return this.transfer(credentials, chainId, linker, payload, amount, joule).andThen((hash) => { return this.waitReceipt(chainId, hash, retryStrategy, retries); }); } /** * Deploy contract * @param credentials Your credentials * @param chainId The chain id * @param code The code, should be a hex string * @param payload The payload, should be a hex string, default is 0x * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @returns E.Either<string, Error>, left is the hash, right is the error */ deployContract(credentials, chainId, code, payload = HEX_PREFIX, amount = 0, joule = 0) { return this.accountLock.withLock(chainId, credentials.getAccountAddress(), async () => { const block = await this.blockCache.getBlock(chainId, credentials.getAccountAddress(), async (chainId, address) => { return await this.httpClient.getLatestBlock(chainId, address); }); const tx = TransactionBuilder.builder(TransactionTypes.DeployContract) .setBlock(block) .setOwner(credentials.getAccountAddress()) .setLinker(ZERO_ADDRESS) .setCode(code) .setPayload(payload) .setAmount(amount) .setJoule(joule) .build(); const codeHash = createCrypto(this.chainConfig.curve).hash(Buffer.from(code.startsWith(HEX_PREFIX) ? code.slice(2) : code, "hex")); tx.codeHash = `0x${codeHash.toString("hex")}`; return await this.handleTransaction(chainId, credentials, tx, block).match((hash) => hash, (error) => { throw error; }); }); } /** * Deploy contract and wait for receipt * @param credentials Your credentials * @param chainId The chain id * @param code The code, should be a hex string * @param payload The payload, should be a hex string, default is 0x * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @param retryStrategy The retry strategy, default is FixedDelayStrategy.default * @param retries The number of retries, default is 10 * @returns E.Either<Receipt, Error>, left is the receipt, right is the error */ deployContractWaitReceipt(credentials, chainId, code, payload = HEX_PREFIX, amount = 0, joule = 0, retryStrategy = FixedDelayStrategy.default, retries = 10) { return this.deployContract(credentials, chainId, code, payload, amount, joule).andThen((hash) => { return this.waitReceipt(chainId, hash, retryStrategy, retries); }); } /** * Call contract * @param credentials Your credentials * @param chainId The chain id * @param contractAddress The contract address * @param code The code, should be a hex string * @param payload The payload, should be a hex string, default is 0x * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @returns E.Either<string, Error>, left is the hash, right is the error */ callContract(credentials, chainId, contractAddress, code, payload = HEX_PREFIX, amount = 0, joule = 0) { return this.accountLock.withLock(chainId, credentials.getAccountAddress(), async () => { const block = await this.blockCache.getBlock(chainId, credentials.getAccountAddress(), async (chainId, address) => { return await this.httpClient.getLatestBlock(chainId, address); }); const tx = TransactionBuilder.builder(TransactionTypes.CallContract) .setBlock(block) .setOwner(credentials.getAccountAddress()) .setLinker(contractAddress) .setCode(code) .setPayload(payload) .setAmount(amount) .setJoule(joule) .build(); const codeHash = createCrypto(this.chainConfig.curve).hash(Buffer.from(code.startsWith(HEX_PREFIX) ? code.slice(2) : code, "hex")); tx.codeHash = `0x${codeHash.toString("hex")}`; return await this.handleTransaction(chainId, credentials, tx, block).match((hash) => hash, (error) => { throw error; }); }); } /** * Call contract and wait for receipt * @param credentials Your credentials * @param chainId The chain id * @param contractAddress The contract address * @param code The code, should be a hex string * @param payload The payload, should be a hex string, default is 0x * @param amount The amount, default is 0 * @param joule The joule, default is 0 * @returns E.Either<Receipt, Error>, left is the receipt, right is the error */ callContractWaitReceipt(credentials, chainId, contractAddress, code, payload = HEX_PREFIX, amount = 0, joule = 0, retryStrategy = FixedDelayStrategy.default, retries = 10) { return this.callContract(credentials, chainId, contractAddress, code, payload, amount, joule).andThen((hash) => { return this.waitReceipt(chainId, hash, retryStrategy, retries); }); } preCallContract(credentials, chainId, contractAddress, code, payload = HEX_PREFIX, amount = 0, joule = 0) { const unsignedTx = TransactionBuilder.builder(TransactionTypes.CallContract) .setBlock(LatestBlock.emptyBlock()) .setOwner(credentials.getAccountAddress()) .setLinker(contractAddress) .setCode(code) .setPayload(payload) .setAmount(amount) .setJoule(joule) .build(); return ResultAsync.fromPromise(this.httpClient.preCallContract(chainId, unsignedTx), (error) => { log.error(`Failed to pre-call contract: ${error}`); return error instanceof Error ? error : new Error(String(error)); }); } } export { LatticeClient, Credentials, ChainConfig, NodeConnectionConfig, Options }; //# sourceMappingURL=lattice.js.map