diff --git a/CHANGELOG.md b/CHANGELOG.md index b8af918bf..8f9a98118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. ## Unreleased - - TBD + - [Breaking changes: cleanup and minor improvements prior release (step 1)](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/190) ## 10.0.0-alpha.5 - [Breaking change: adjustements to transaction awaitening and completion, transaction watcher](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/173) @@ -41,8 +41,14 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how - Moved network providers and contract wrappers to separate repositories: - https://github.com/ElrondNetwork/elrond-sdk-erdjs-network-providers - https://github.com/ElrondNetwork/elrond-sdk-erdjs-contract-wrappers - - Rename `methods` to `methodsExplicit`. Rename `methodsAuto` (only added in erdjs 10 beta) to `methods` (default choice). Therefore, by default, interactions are created without having to pass `TypedValue` objects as parameters. Automatic type inference is applied. - - Remove `SmartContractController` (downgraded to a mere test utility). + - Renamed `methods` to `methodsExplicit`. Rename `methodsAuto` (only added in erdjs 10 beta) to `methods` (default choice). Therefore, by default, interactions are created without having to pass `TypedValue` objects as parameters. Automatic type inference is applied. + - Removed `SmartContractController` (downgraded to a mere test utility). + - erdjs does not depend on `axios` and `fs` anymore. Changed the way an `AbiRegistry` is created. Removed function `Code.fromFile()` (was relying on `fs`). + - Removed `smartContract.getOwner()` (surprising behavior of function, usage not recommended). + - Removed `transaction.awaitHashed()` (lacks use-case). + - Removed `transaction.awaitSigned()` (it isn't useful in relation with any of our signing providers). + - Removed not used interfaces (e.g. `ISignable` is not needed in erdjs, but in walletcore). + - Removed `interaction.getContract()`. Add `interaction.getContractAddress()`. ## [10.0.0-beta.3] - [Extract dapp / signing providers to separate repositories](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/170) diff --git a/package-lock.json b/package-lock.json index 737c3c45b..1c250e2a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,9 @@ "license": "GPL-3.0-or-later", "dependencies": { "@elrondnetwork/transaction-decoder": "0.1.0", - "abort-controller": "3.0.0", - "axios": "0.24.0", "bech32": "1.1.4", "bignumber.js": "9.0.1", "buffer": "6.0.3", - "json-bigint": "1.0.0", "json-duplicate-key-handle": "1.0.0", "keccak": "3.0.1", "protobufjs": "6.10.2" @@ -29,6 +26,7 @@ "@types/node": "13.13.2", "@types/protobufjs": "6.0.0", "assert": "2.0.0", + "axios": "0.24.0", "browserify": "17.0.0", "chai": "4.2.0", "esmify": "2.1.1", @@ -641,17 +639,6 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -809,6 +796,7 @@ "version": "0.24.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dev": true, "dependencies": { "follow-redirects": "^1.14.4" } @@ -2438,14 +2426,6 @@ "es5-ext": "~0.10.14" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -2554,6 +2534,7 @@ "version": "1.14.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true, "funding": [ { "type": "individual", @@ -3326,6 +3307,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, "dependencies": { "bignumber.js": "^9.0.0" } @@ -3569,9 +3551,9 @@ "dev": true }, "node_modules/marked": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.13.tgz", - "integrity": "sha512-lS/ZCa4X0gsRcfWs1eoh6dLnHr9kVH3K1t2X4M/tTtNouhZ7anS1Csb6464VGLQHv8b2Tw1cLeZQs58Jav8Rzw==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", + "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -6189,14 +6171,6 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -6323,6 +6297,7 @@ "version": "0.24.0", "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dev": true, "requires": { "follow-redirects": "^1.14.4" } @@ -7735,11 +7710,6 @@ "es5-ext": "~0.10.14" } }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -7833,7 +7803,8 @@ "follow-redirects": { "version": "1.14.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true }, "foreach": { "version": "2.0.5", @@ -8380,6 +8351,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, "requires": { "bignumber.js": "^9.0.0" } @@ -8567,9 +8539,9 @@ "dev": true }, "marked": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.13.tgz", - "integrity": "sha512-lS/ZCa4X0gsRcfWs1eoh6dLnHr9kVH3K1t2X4M/tTtNouhZ7anS1Csb6464VGLQHv8b2Tw1cLeZQs58Jav8Rzw==", + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", + "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", "dev": true }, "md5.js": { diff --git a/package.json b/package.json index 528bdb0c6..96f45d1d2 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,8 @@ "tests-mainnet": "mocha $(find . -name '*.main.net.spec.ts')", "compile-browser": "tsc -p tsconfig.json && browserify out/index.js -o out-browser/erdjs.js --standalone erdjs -p esmify", "compile-browser-min": "tsc -p tsconfig.json && browserify out/index.js -o out-browser/erdjs.min.js --standalone erdjs -p esmify -p tinyify", - "compile": "tsc -p tsconfig.json && npm run copy-files", + "compile": "tsc -p tsconfig.json", "compile-proto": "npx pbjs -t static-module -w commonjs -o src/proto/compiled.js src/proto/transaction.proto && npx pbts -o src/proto/compiled.d.ts src/proto/compiled.js", - "copy-files": "mkdir -p out/testutils/ && mkdir -p out/abi/ && cp -R src/abi/* out/abi/", "browser-tests": "make clean && make browser-tests && http-server --port=9876 -o browser-tests/index.html", "lint": "tslint --project .", "pretest": "npm run compile", @@ -29,12 +28,9 @@ "license": "GPL-3.0-or-later", "dependencies": { "@elrondnetwork/transaction-decoder": "0.1.0", - "abort-controller": "3.0.0", - "axios": "0.24.0", "bech32": "1.1.4", "bignumber.js": "9.0.1", "buffer": "6.0.3", - "json-bigint": "1.0.0", "json-duplicate-key-handle": "1.0.0", "keccak": "3.0.1", "protobufjs": "6.10.2" @@ -48,6 +44,7 @@ "@types/node": "13.13.2", "@types/protobufjs": "6.0.0", "assert": "2.0.0", + "axios": "0.24.0", "browserify": "17.0.0", "chai": "4.2.0", "esmify": "2.1.1", diff --git a/src/abi/builtinFunctions.abi.json b/src/abi/builtinFunctions.abi.json deleted file mode 100644 index 5fa212215..000000000 --- a/src/abi/builtinFunctions.abi.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "name": "builtinFunctions", - "endpoints": [ - { - "name": "ESDTTransfer", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "value", - "type": "BigUint" - }, - { - "name": "functionAndArguments", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "name": "ESDTNFTTransfer", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "nonce", - "type": "U64" - }, - { - "name": "quantity", - "type": "BigUint" - }, - { - "name": "destination", - "type": "Address" - }, - { - "name": "functionAndArguments", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "name": "ESDTNFTCreate", - "inputs": [ - { - "name": "tokenIdentifier", - "type": "bytes" - }, - { - "name": "initialQuantity", - "type": "BigUint" - }, - { - "name": "NFTName", - "type": "bytes" - }, - { - "name": "royalties", - "type": "BigUint" - }, - { - "name": "hash", - "type": "bytes" - }, - { - "name": "attributes", - "type": "bytes" - }, - { - "name": "URIs", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "name": "tokenNonce", - "type": "BigUint" - } - ] - }, - { - "name": "ESDTNFTAddQuantity", - "inputs": [ - { - "name": "tokenIdentifier", - "type": "bytes" - }, - { - "name": "tokenNonce", - "type": "BigUint" - }, - { - "name": "amount", - "type": "BigUint" - } - ], - "outputs": [] - } - ] -} diff --git a/src/abi/esdtSystemContract.abi.json b/src/abi/esdtSystemContract.abi.json deleted file mode 100644 index 660b39713..000000000 --- a/src/abi/esdtSystemContract.abi.json +++ /dev/null @@ -1,384 +0,0 @@ -{ - "name": "esdt", - "endpoints": [ - { - "name": "issue", - "payableInTokens": [ - "EGLD" - ], - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "tickerName", - "type": "bytes" - }, - { - "name": "initialSupply", - "type": "BigUint" - }, - { - "name": "numOfDecimals", - "type": "u32" - }, - { - "name": "properties", - "type": "variadic>", - "multi_arg": true - } - ], - "outputs": [ - { - "name": "tokenIdentifier", - "type": "bytes" - } - ] - }, - { - "name": "issueSemiFungible", - "payableInTokens": [ - "EGLD" - ], - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "tickerName", - "type": "bytes" - }, - { - "name": "properties", - "type": "variadic>", - "multi_arg": true - } - ], - "outputs": [ - { - "name": "tokenIdentifier", - "type": "bytes" - } - ] - }, - { - "name": "issueNonFungible", - "payableInTokens": [ - "EGLD" - ], - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "tickerName", - "type": "bytes" - }, - { - "name": "properties", - "type": "variadic>", - "multi_arg": true - } - ], - "outputs": [ - { - "name": "tokenIdentifier", - "type": "bytes" - } - ] - }, - { - "name": "ESDTBurn", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "burnAmount", - "type": "BigUint" - } - ], - "outputs": [] - }, - { - "name": "mint", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "mintAmount", - "type": "BigUint" - }, - { - "name": "mintedTokensOwner", - "type": "optional
" - } - ], - "outputs": [] - }, - { - "name": "freeze", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "addressToFreezeFor", - "type": "Address" - } - ], - "outputs": [] - }, - { - "name": "unFreeze", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "addressToUnFreezeFor", - "type": "Address" - } - ], - "outputs": [] - }, - { - "name": "wipe", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "addressToWipeFor", - "type": "Address" - } - ], - "outputs": [] - }, - { - "name": "pause", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - } - ], - "outputs": [] - }, - { - "name": "unPause", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - } - ], - "outputs": [] - }, - { - "name": "claim", - "inputs": [], - "outputs": [] - }, - { - "name": "configChange", - "inputs": [ - { - "name": "ownerAddress", - "type": "Address" - }, - { - "name": "baseIssuingCost", - "type": "BigUint" - }, - { - "name": "minTokenNameLength", - "type": "u32" - }, - { - "name": "maxTokenNameLength", - "type": "u32" - } - ], - "outputs": [] - }, - { - "name": "controlChanges", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "properties", - "type": "variadic>", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "name": "transferOwnership", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "newOwner", - "type": "Address" - } - ], - "outputs": [] - }, - { - "name": "getTokenProperties", - "comment": "Properties have the format: Name-value, ex: NumDecimals-5, IsPaused-true", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - } - ], - "outputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "tokenType", - "type": "bytes" - }, - { - "name": "ownerAddress", - "type": "Address" - }, - { - "name": "totalMinted", - "type": "bytes" - }, - { - "name": "totalBurned", - "type": "bytes" - }, - { - "name": "numDecimals", - "type": "bytes" - }, - { - "name": "isPaused", - "type": "bytes" - }, - { - "name": "canUpgrade", - "type": "bytes" - }, - { - "name": "canMint", - "type": "bytes" - }, - { - "name": "canBurn", - "type": "bytes" - }, - { - "name": "canChangeOwner", - "type": "bytes" - }, - { - "name": "canPause", - "type": "bytes" - }, - { - "name": "canFreeze", - "type": "bytes" - }, - { - "name": "canWipe", - "type": "bytes" - }, - { - "name": "canAddSpecialRoles", - "type": "bytes" - }, - { - "name": "canTransferNftCreateRole", - "type": "bytes" - }, - { - "name": "nftCreateStopped", - "type": "bytes" - }, - { - "name": "numWiped", - "type": "bytes" - } - ] - }, - { - "name": "getSpecialRoles", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - } - ], - "outputs": [ - { - "name": "addressesWithRoles", - "type": "variadic" - } - ] - }, - { - "name": "setSpecialRole", - "inputs": [ - { - "name": "tokenName", - "type": "bytes" - }, - { - "name": "addressToSetRolesFor", - "type": "Address" - }, - { - "name": "roles", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "name": "getContractConfig", - "inputs": [], - "outputs": [ - { - "name": "ownerAddress", - "type": "Address" - }, - { - "name": "baseIssuingCost", - "type": "BigUint" - }, - { - "name": "minTokenNameLength", - "type": "BigUint" - }, - { - "name": "maxTokenNameLength", - "type": "BigUint" - } - ] - } - ] -} diff --git a/src/abi/sendWrapper.abi.json b/src/abi/sendWrapper.abi.json deleted file mode 100644 index 200d9a427..000000000 --- a/src/abi/sendWrapper.abi.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "send", - "endpoints": [ - { - "name": "", - "payableInTokens": [ - "*" - ], - "inputs": [], - "outputs": [] - } - ] -} diff --git a/src/boundaryAdapters.ts b/src/boundaryAdapters.ts deleted file mode 100644 index 6278ad21f..000000000 --- a/src/boundaryAdapters.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Address } from "./address"; -import { ErrInvariantFailed } from "./errors"; -import { IBech32Address, ISignature } from "./interface"; -import { Signature } from "./signature"; - -/** - * Adapts a signature created by other components (e.g. erdjs-walletcore, erdjs-hw-provider) to one understood by erdjs. - */ -export function adaptToSignature(obj: ISignature): Signature { - if (!obj.hex || typeof obj.hex() !== "string") { - throw new ErrInvariantFailed("adaptToSignature: bad or missing hex()") - } - - return new Signature(obj.hex()); -} - -/** - * Adapts an address created by other components (e.g. erdjs-walletcore, erdjs-hw-provider) to one understood by erdjs. - */ -export function adaptToAddress(obj: IBech32Address): Address { - if (!obj.bech32 || typeof obj.bech32() !== "string") { - throw new ErrInvariantFailed("adaptToSignature: bad or missing bech32()") - } - - return new Address(obj.bech32()); -} diff --git a/src/constants.ts b/src/constants.ts index fef0a968e..eeb3a6dd2 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,3 @@ -const JSONbig = require("json-bigint"); - export const TRANSACTION_MIN_GAS_PRICE = 1000000000; export const TRANSACTION_OPTIONS_DEFAULT = 0; export const TRANSACTION_OPTIONS_TX_HASH_SIGN = 1; @@ -10,14 +8,3 @@ export const ESDT_TRANSFER_FUNCTION_NAME = "ESDTTransfer"; export const ESDTNFT_TRANSFER_FUNCTION_NAME = "ESDTNFTTransfer"; export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer"; export const ESDT_TRANSFER_VALUE = "0"; - -// TODO: Rename fo "AxiosDefaultConfig" (less ambiguous). -export const defaultConfig = { - timeout: 1000, - // See: https://github.com/axios/axios/issues/983 regarding transformResponse - transformResponse: [ - function(data: any) { - return JSONbig.parse(data); - }, - ], -}; diff --git a/src/errors.ts b/src/errors.ts index fc0b01466..55298341f 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -226,15 +226,6 @@ export class ErrTransactionHashUnknown extends Err { } } -/** - * Signals that a {@link Transaction} cannot be used within an operation, since it isn't signed. - */ -export class ErrTransactionNotSigned extends Err { - public constructor() { - super(`Transaction isn't signed`); - } -} - /** * Signals an error related to signing a message (a transaction). */ @@ -317,6 +308,15 @@ export class ErrContract extends Err { } } +export class ErrContractHasNoAddress extends ErrContract { + public constructor() { + super(` +The smart contract has no address set. Make sure you provide the address in the constructor, or call setAddress() appropriately. +If you need to recompute the address of the contract, make use of SmartContract.computeAddress() (static method). +`); + } +} + /** * Signals an error thrown by the mock-like test objects. */ diff --git a/src/events.ts b/src/events.ts deleted file mode 100644 index 8fc38d3df..000000000 --- a/src/events.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Disposable } from "./interface"; - -/** - * An interface that defines a Listener (an "Observer", in the context of the Observer pattern). - */ -export interface Listener { - (event: T): any; -} - -/** - * An event (a "Subject" in the context of the Observer pattern). - * One or more {@link Listener} objects can register to this. - * - * Based on: https://basarat.gitbook.io/typescript/main-1/typed-event - */ -export class TypedEvent { - private listeners: Listener[] = []; - private listenersOnce: Listener[] = []; - - /** - * Registers a listener to this event. - */ - on(listener: Listener): Disposable { - this.listeners.push(listener); - return { - dispose: () => this.off(listener) - }; - } - - /** - * Registers a one-time listener to this event. - */ - once(listener: Listener): void { - this.listenersOnce.push(listener); - } - - /** - * Unregisters a listener from this event. - */ - off(listener: Listener) { - var callbackIndex = this.listeners.indexOf(listener); - if (callbackIndex > -1) { - this.listeners.splice(callbackIndex, 1); - } - } - - /** - * Emits an event (with a payload). - */ - emit(event: T) { - // Notify all listeners - this.listeners.forEach((listener) => listener(event)); - - // Notify (then clear) "once" listeners - this.listenersOnce.forEach((listener) => listener(event)); - this.listenersOnce = []; - } -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 6732d635f..045b14e6e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,6 @@ export * from "./transactionPayload"; export * from "./transactionWatcher"; export * from "./balance"; export * from "./balanceBuilder"; -export * from "./boundaryAdapters"; export * from "./logger"; export * from "./networkParams"; export * from "./signableMessage"; diff --git a/src/interface.ts b/src/interface.ts index ded6757f0..c82a60894 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,55 +1,15 @@ -import { Signature } from "./signature"; import { ITransactionOnNetwork } from "./interfaceOfNetwork"; export interface ITransactionFetcher { /** * Fetches the state of a {@link Transaction}. */ - getTransaction(txHash: IHash, hintSender?: IBech32Address, withResults?: boolean): Promise; -} - -/** - * An interface that defines a signable object (e.g. a {@link Transaction}). - */ -export interface ISignable { - /** - * Returns the signable object in its raw form - a sequence of bytes to be signed. - */ - serializeForSigning(signedBy: IBech32Address): Buffer; - - /** - * Applies the computed signature on the object itself. - * - * @param signature The computed signature - * @param signedBy The address of the signer - */ - applySignature(signature: ISignature, signedBy: IBech32Address): void; -} - -/** - * Interface that defines a signed and verifiable object - */ -export interface IVerifiable { - /** - * Returns the signature that should be verified - */ - getSignature(): Signature; - /** - * Returns the signable object in its raw form - a sequence of bytes to be verified. - */ - serializeForSigning(signedBy?: IBech32Address): Buffer; -} - -/** - * An interface that defines a disposable object. - */ -export interface Disposable { - dispose(): void; + getTransaction(txHash: IHash, hintSender?: IAddress, withResults?: boolean): Promise; } export interface ISignature { hex(): string; } export interface IHash { hex(): string; } -export interface IBech32Address { bech32(): string; } +export interface IAddress { bech32(): string; } export interface ITransactionValue { toString(): string; } export interface IAccountBalance { toString(): string; } export interface ITransactionPayload { encoded(): string; } diff --git a/src/interfaceOfNetwork.ts b/src/interfaceOfNetwork.ts index a4f4d4430..7b7838272 100644 --- a/src/interfaceOfNetwork.ts +++ b/src/interfaceOfNetwork.ts @@ -1,4 +1,4 @@ -import { IAccountBalance, IBech32Address, IChainID, IGasLimit, IHash, INonce, ITransactionPayload, ITransactionValue } from "./interface"; +import { IAccountBalance, IAddress, IChainID, IGasLimit, IHash, INonce, ITransactionPayload, ITransactionValue } from "./interface"; export interface IAccountOnNetwork { nonce: INonce; @@ -18,8 +18,8 @@ export interface ITransactionOnNetwork { hash: IHash; type: string; value: ITransactionValue; - receiver: IBech32Address; - sender: IBech32Address; + receiver: IAddress; + sender: IAddress; data: ITransactionPayload; status: ITransactionStatus; receipt: ITransactionReceipt; @@ -45,8 +45,8 @@ export interface IContractResults { export interface IContractResultItem { hash: IHash; nonce: INonce; - receiver: IBech32Address; - sender: IBech32Address; + receiver: IAddress; + sender: IAddress; data: string; returnMessage: string; logs: ITransactionLogs; @@ -71,7 +71,7 @@ export interface ITransactionLogs { } export interface ITransactionEvent { - readonly address: IBech32Address; + readonly address: IAddress; readonly identifier: string; readonly topics: ITransactionEventTopic[]; readonly data: string; diff --git a/src/proto/serializer.ts b/src/proto/serializer.ts index f14b6f6fe..2f841eb0c 100644 --- a/src/proto/serializer.ts +++ b/src/proto/serializer.ts @@ -4,6 +4,7 @@ import { bigIntToBuffer } from "../smartcontracts/codec/utils"; import { Transaction } from "../transaction"; import { proto } from "./compiled"; import {TRANSACTION_OPTIONS_DEFAULT} from "../constants"; +import { Address } from "../address"; /** * Hides away the serialization complexity, for each type of object (e.g. transactions). @@ -15,13 +16,16 @@ export class ProtoSerializer { * Serializes a Transaction object to a Buffer. Handles low-level conversion logic and field-mappings as well. */ serializeTransaction(transaction: Transaction): Buffer { + let receiverPubkey = new Address(transaction.getReceiver().bech32()).pubkey(); + let senderPubkey = new Address(transaction.getSender().bech32()).pubkey(); + let protoTransaction = new proto.Transaction({ // elrond-go's serializer handles nonce == 0 differently, thus we treat 0 as "undefined". Nonce: transaction.getNonce().valueOf() ? transaction.getNonce().valueOf() : undefined, Value: this.serializeBalance(transaction.getValue()), - RcvAddr: transaction.getReceiver().pubkey(), + RcvAddr: receiverPubkey, RcvUserName: null, - SndAddr: transaction.getSender().pubkey(), + SndAddr: senderPubkey, SndUserName: null, GasPrice: transaction.getGasPrice().valueOf(), GasLimit: transaction.getGasLimit().valueOf(), diff --git a/src/signableMessage.ts b/src/signableMessage.ts index 43b484e41..889f70f0a 100644 --- a/src/signableMessage.ts +++ b/src/signableMessage.ts @@ -1,12 +1,11 @@ -import { ISignable, ISignature } from "./interface"; +import { ISignature } from "./interface"; import { Signature } from "./signature"; import { Address } from "./address"; -import { adaptToSignature } from "./boundaryAdapters"; const createKeccakHash = require("keccak"); export const MESSAGE_PREFIX = "\x17Elrond Signed Message:\n"; -export class SignableMessage implements ISignable { +export class SignableMessage { /** * Actual message being signed. @@ -15,7 +14,7 @@ export class SignableMessage implements ISignable { /** * Signature obtained by a signer of type @param signer . */ - signature: Signature; + signature: ISignature; /** * Address of the wallet that performed the signing operation @@ -54,12 +53,12 @@ export class SignableMessage implements ISignable { return Buffer.concat([this.getMessageSize(), this.message]); } - getSignature(): Signature { + getSignature(): ISignature { return this.signature; } applySignature(signature: ISignature): void { - this.signature = adaptToSignature(signature); + this.signature = signature; } getMessageSize(): Buffer { diff --git a/src/signature.ts b/src/signature.ts index 43508a57e..9ff9e9a36 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -55,18 +55,6 @@ export class Signature { } hex() { - this.assertNotEmpty(); - return this.valueHex; } - - isEmpty(): boolean { - return !this.valueHex; - } - - private assertNotEmpty() { - if (this.isEmpty()) { - throw new errors.ErrSignatureEmpty(); - } - } } diff --git a/src/smartcontracts/abi.ts b/src/smartcontracts/abi.ts index 9076fd4bc..37ce3b423 100644 --- a/src/smartcontracts/abi.ts +++ b/src/smartcontracts/abi.ts @@ -10,19 +10,7 @@ export class SmartContractAbi { constructor(registry: AbiRegistry, implementsInterfaces: string[]) { this.interfaces.push(...registry.getInterfaces(implementsInterfaces)); } - - static async fromAbiPath(abiPath: string): Promise { - let abiRegistry = await AbiRegistry.load({ files: [abiPath] }); - let interfaceNames = abiRegistry.interfaces.map(iface => iface.name); - return new SmartContractAbi(abiRegistry, interfaceNames); - } - - static async fromAbiUrl(abiUrl: string): Promise { - let abiRegistry = await AbiRegistry.load({ urls: [abiUrl] }); - let interfaceNames = abiRegistry.interfaces.map(iface => iface.name); - return new SmartContractAbi(abiRegistry, interfaceNames); - } - + getAllEndpoints(): EndpointDefinition[] { let endpoints = []; diff --git a/src/smartcontracts/code.ts b/src/smartcontracts/code.ts index 6cf1f2b2e..5dcd8b2d1 100644 --- a/src/smartcontracts/code.ts +++ b/src/smartcontracts/code.ts @@ -1,7 +1,3 @@ -import { PathLike } from "fs"; -import * as fs from "fs"; -import axios, { AxiosResponse } from "axios"; - /** * Bytecode of a Smart Contract, as an abstraction. */ @@ -19,37 +15,6 @@ export class Code { return new Code(code.toString("hex")); } - /** - * Creates a Code object by loading the bytecode from a specified WASM file. - */ - static async fromFile(file: PathLike): Promise { - let buffer: Buffer = await fs.promises.readFile(file); - return Code.fromBuffer(buffer); - } - - /** - * Creates a Code object by loading the bytecode from a specified URL (WASM file). - */ - static async fromUrl(url: string): Promise { - let response: AxiosResponse = await axios.get(url, { - responseType: 'arraybuffer', - transformResponse: [], - headers: { - "Accept": "application/wasm" - } - }); - - let buffer = Buffer.from(response.data); - return Code.fromBuffer(buffer); - } - - /** - * Null-object pattern: creates an empty Code object. - */ - static nothing(): Code { - return new Code(""); - } - /** * Returns the bytecode as a hex-encoded string. */ diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index fd8bde863..ab7948087 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -1,6 +1,6 @@ import { ContractController } from "../testutils/contractController"; import { SmartContract } from "./smartContract"; -import { loadAbiRegistry, loadContractCode, loadTestWallets, TestWallet } from "../testutils"; +import { prepareDeployment, loadAbiRegistry, loadTestWallets, TestWallet } from "../testutils"; import { SmartContractAbi } from "./abi"; import { assert } from "chai"; import { Interaction } from "./interaction"; @@ -21,7 +21,7 @@ describe("test smart contract interactor", function () { it("should interact with 'answer' (local testnet)", async function () { this.timeout(80000); - let abiRegistry = await loadAbiRegistry(["src/testdata/answer.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/answer.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["answer"]); let contract = new SmartContract({ abi: abi }); let controller = new ContractController(provider); @@ -30,15 +30,15 @@ describe("test smart contract interactor", function () { await alice.sync(provider); // Deploy the contract - let deployTransaction = contract.deploy({ - code: await loadContractCode("src/testdata/answer.wasm"), + let deployTransaction = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/answer.wasm", gasLimit: new GasLimit(3000000), initArguments: [], chainID: network.ChainID }); - deployTransaction.setNonce(alice.account.getNonceThenIncrement()); - await alice.signer.sign(deployTransaction); let { bundle: { returnCode } } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); @@ -69,7 +69,7 @@ describe("test smart contract interactor", function () { it("should interact with 'counter' (local testnet)", async function () { this.timeout(120000); - let abiRegistry = await loadAbiRegistry(["src/testdata/counter.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/counter.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["counter"]); let contract = new SmartContract({ abi: abi }); let controller = new ContractController(provider); @@ -78,15 +78,15 @@ describe("test smart contract interactor", function () { await alice.sync(provider); // Deploy the contract - let deployTransaction = contract.deploy({ - code: await loadContractCode("src/testdata/counter.wasm"), + let deployTransaction = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/counter.wasm", gasLimit: new GasLimit(3000000), initArguments: [], chainID: network.ChainID }); - deployTransaction.setNonce(alice.account.getNonceThenIncrement()); - await alice.signer.sign(deployTransaction); let { bundle: { returnCode } } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); @@ -122,7 +122,7 @@ describe("test smart contract interactor", function () { it("should interact with 'lottery-esdt' (local testnet)", async function () { this.timeout(140000); - let abiRegistry = await loadAbiRegistry(["src/testdata/lottery-esdt.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["Lottery"]); let contract = new SmartContract({ abi: abi }); let controller = new ContractController(provider); @@ -131,15 +131,15 @@ describe("test smart contract interactor", function () { await alice.sync(provider); // Deploy the contract - let deployTransaction = contract.deploy({ - code: await loadContractCode("src/testdata/lottery-esdt.wasm"), + let deployTransaction = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/lottery-esdt.wasm", gasLimit: new GasLimit(100000000), initArguments: [], chainID: network.ChainID }); - deployTransaction.setNonce(alice.account.getNonceThenIncrement()); - await alice.signer.sign(deployTransaction); let { bundle: { returnCode } } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); diff --git a/src/smartcontracts/interaction.spec.ts b/src/smartcontracts/interaction.spec.ts index 5ac9b6b73..e107e4402 100644 --- a/src/smartcontracts/interaction.spec.ts +++ b/src/smartcontracts/interaction.spec.ts @@ -67,7 +67,7 @@ describe("test smart contract interactor", function() { const hexBar = "4241522d356263303866"; const hexLKMEX = "4c4b4d45582d616162393130"; const hexStrămoși = "4d4f532d623962346232"; - const hexContractAddress = contract.getAddress().hex(); + const hexContractAddress = new Address(contract.getAddress().bech32()).hex(); const hexDummyFunction = "64756d6d79"; // ESDT, single @@ -109,7 +109,7 @@ describe("test smart contract interactor", function() { it("should interact with 'answer'", async function () { setupUnitTestWatcherTimeouts(); - let abiRegistry = await loadAbiRegistry(["src/testdata/answer.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/answer.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["answer"]); let contract = new SmartContract({ address: dummyAddress, abi: abi }); let controller = new ContractController(provider); @@ -171,7 +171,7 @@ describe("test smart contract interactor", function() { it("should interact with 'counter'", async function() { setupUnitTestWatcherTimeouts(); - let abiRegistry = await loadAbiRegistry(["src/testdata/counter.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/counter.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["counter"]); let contract = new SmartContract({ address: dummyAddress, abi: abi }); let controller = new ContractController(provider); @@ -218,7 +218,7 @@ describe("test smart contract interactor", function() { it("should interact with 'lottery-esdt'", async function() { setupUnitTestWatcherTimeouts(); - let abiRegistry = await loadAbiRegistry(["src/testdata/lottery-esdt.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["Lottery"]); let contract = new SmartContract({ address: dummyAddress, abi: abi }); let controller = new ContractController(provider); diff --git a/src/smartcontracts/interaction.ts b/src/smartcontracts/interaction.ts index 4ad6f3094..2b190809f 100644 --- a/src/smartcontracts/interaction.ts +++ b/src/smartcontracts/interaction.ts @@ -9,7 +9,7 @@ import { Nonce } from "../nonce"; import { ESDTNFT_TRANSFER_FUNCTION_NAME, ESDT_TRANSFER_FUNCTION_NAME, MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME } from "../constants"; import { Account } from "../account"; import { CallArguments } from "./interface"; -import { IChainID, IGasLimit, IGasPrice } from "../interface"; +import { IAddress, IChainID, IGasLimit, IGasPrice } from "../interface"; import { InteractionChecker } from "./interactionChecker"; /** @@ -17,7 +17,7 @@ import { InteractionChecker } from "./interactionChecker"; */ interface ISmartContractWithinInteraction { call({ func, args, value, gasLimit, receiver }: CallArguments): Transaction; - getAddress(): Address; + getAddress(): IAddress; getEndpoint(name: ContractFunction): EndpointDefinition; } @@ -31,36 +31,33 @@ export class Interaction { private readonly contract: ISmartContractWithinInteraction; private readonly function: ContractFunction; private readonly args: TypedValue[]; - private readonly receiver?: Address; private nonce: Nonce = new Nonce(0); private value: Balance = Balance.Zero(); private gasLimit: IGasLimit = new GasLimit(0); private gasPrice: IGasPrice | undefined = undefined; private chainID: IChainID = ChainID.unspecified(); - private querent: Address = new Address(); + private querent: IAddress = new Address(); private isWithSingleESDTTransfer: boolean = false; private isWithSingleESDTNFTTransfer: boolean = false; private isWithMultiESDTNFTTransfer: boolean = false; private tokenTransfers: TokenTransfersWithinInteraction; - private tokenTransfersSender: Address = new Address(); + private tokenTransfersSender: IAddress = new Address(); constructor( contract: ISmartContractWithinInteraction, func: ContractFunction, - args: TypedValue[], - receiver?: Address, + args: TypedValue[] ) { this.contract = contract; this.function = func; this.args = args; - this.receiver = receiver; this.tokenTransfers = new TokenTransfersWithinInteraction([], this); } - getContract(): ISmartContractWithinInteraction { - return this.contract; + getContractAddress(): IAddress { + return this.contract.getAddress(); } getFunction(): ContractFunction { @@ -88,7 +85,7 @@ export class Interaction { } buildTransaction(): Transaction { - let receiver = this.receiver; + let receiver = this.contract.getAddress(); let func: ContractFunction = this.function; let args = this.args; @@ -107,7 +104,6 @@ export class Interaction { args = this.tokenTransfers.buildArgsForMultiESDTNFTTransfer(); } - // TODO: create as "deploy" transaction if the function is "init" (or find a better pattern for deployments). let transaction = this.contract.call({ func: func, // GasLimit will be set using "withGasLimit()". @@ -291,7 +287,7 @@ class TokenTransfersWithinInteraction { private getTypedTokensReceiver(): TypedValue { // The actual receiver of the token(s): the contract - return new AddressValue(this.interaction.getContract().getAddress()); + return new AddressValue(this.interaction.getContractAddress()); } private getTypedInteractionFunction(): TypedValue { diff --git a/src/smartcontracts/interactionChecker.spec.ts b/src/smartcontracts/interactionChecker.spec.ts index ef29e23fc..f009383d4 100644 --- a/src/smartcontracts/interactionChecker.spec.ts +++ b/src/smartcontracts/interactionChecker.spec.ts @@ -15,7 +15,7 @@ describe("integration tests: test checker within interactor", function () { let checker = new InteractionChecker(); it("should detect errors for 'ultimate answer'", async function () { - let abiRegistry = await loadAbiRegistry(["src/testdata/answer.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/answer.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["answer"]); let contract = new SmartContract({ address: dummyAddress, abi: abi }); let endpoint = abi.getEndpoint("getUltimateAnswer"); @@ -33,7 +33,7 @@ describe("integration tests: test checker within interactor", function () { }); it("should detect errors for 'lottery'", async function () { - let abiRegistry = await loadAbiRegistry(["src/testdata/lottery-esdt.abi.json"]); + let abiRegistry = await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"); let abi = new SmartContractAbi(abiRegistry, ["Lottery"]); let contract = new SmartContract({ address: dummyAddress, abi: abi }); let endpoint = abi.getEndpoint("start"); diff --git a/src/smartcontracts/interface.ts b/src/smartcontracts/interface.ts index df7973b18..0ef51b531 100644 --- a/src/smartcontracts/interface.ts +++ b/src/smartcontracts/interface.ts @@ -1,6 +1,5 @@ -import { Address } from "../address"; import { Balance } from "../balance"; -import { IChainID, IGasLimit, IGasPrice } from "../interface"; +import { IAddress, IChainID, IGasLimit, IGasPrice } from "../interface"; import { IContractQueryResponse, ITransactionOnNetwork } from "../interfaceOfNetwork"; import { Transaction } from "../transaction"; import { Code } from "./code"; @@ -16,7 +15,7 @@ export interface ISmartContract { /** * Gets the address of the Smart Contract. */ - getAddress(): Address; + getAddress(): IAddress; /** * Creates a {@link Transaction} for deploying the Smart Contract to the Network. @@ -59,7 +58,7 @@ export interface CallArguments { args?: TypedValue[]; value?: Balance; gasLimit: IGasLimit; - receiver?: Address; + receiver?: IAddress; gasPrice?: IGasPrice; chainID: IChainID; } @@ -68,7 +67,7 @@ export interface QueryArguments { func: ContractFunction; args?: TypedValue[]; value?: Balance; - caller?: Address + caller?: IAddress } export interface TypedOutcomeBundle { @@ -86,11 +85,3 @@ export interface UntypedOutcomeBundle { returnMessage: string; values: Buffer[]; } - -export interface IResultsParser { - parseQueryResponse(queryResponse: IContractQueryResponse, endpoint: EndpointDefinition): TypedOutcomeBundle; - parseUntypedQueryResponse(queryResponse: IContractQueryResponse): UntypedOutcomeBundle; - - parseOutcome(transaction: ITransactionOnNetwork, endpoint: EndpointDefinition): TypedOutcomeBundle; - parseUntypedOutcome(transaction: ITransactionOnNetwork): UntypedOutcomeBundle; -} diff --git a/src/smartcontracts/query.ts b/src/smartcontracts/query.ts index 67f1fea23..c34fc4a50 100644 --- a/src/smartcontracts/query.ts +++ b/src/smartcontracts/query.ts @@ -4,11 +4,11 @@ import { Address } from "../address"; import { guardValueIsSet } from "../utils"; import { TypedValue } from "./typesystem"; import { ArgSerializer } from "./argSerializer"; -import { IBech32Address, ITransactionValue } from "../interface"; +import { IAddress, ITransactionValue } from "../interface"; export class Query { - caller: Address; - address: IBech32Address; + caller: IAddress; + address: IAddress; func: ContractFunction; args: TypedValue[]; value: ITransactionValue; @@ -38,7 +38,7 @@ export class Query { "value": this.value.toString() }; - if (!this.caller.isEmpty()) { + if (this.caller.bech32()) { request["caller"] = this.caller.bech32(); } diff --git a/src/smartcontracts/resultsParser.ts b/src/smartcontracts/resultsParser.ts index ac04bb192..83f366103 100644 --- a/src/smartcontracts/resultsParser.ts +++ b/src/smartcontracts/resultsParser.ts @@ -4,11 +4,10 @@ import { ErrCannotParseContractResults } from "../errors"; import { Logger } from "../logger"; import { IContractQueryResponse, IContractResults, ITransactionLogs, ITransactionOnNetwork } from "../interfaceOfNetwork"; import { ArgSerializer } from "./argSerializer"; -import { TypedOutcomeBundle, IResultsParser, UntypedOutcomeBundle } from "./interface"; +import { TypedOutcomeBundle, UntypedOutcomeBundle } from "./interface"; import { ReturnCode } from "./returnCode"; import { EndpointDefinition } from "./typesystem"; -import { adaptToAddress } from "../boundaryAdapters"; -import { IBech32Address } from "../interface"; +import { IAddress } from "../interface"; enum WellKnownEvents { OnTransactionCompleted = "completedTxEvent", @@ -24,7 +23,7 @@ enum WellKnownTopics { * Parses contract query responses and smart contract results. * The parsing involves some heuristics, in order to handle slight inconsistencies (e.g. some SCRs are present on API, but missing on Gateway). */ -export class ResultsParser implements IResultsParser { +export class ResultsParser { parseQueryResponse(queryResponse: IContractQueryResponse, endpoint: EndpointDefinition): TypedOutcomeBundle { let parts = queryResponse.getReturnDataParts(); let values = new ArgSerializer().buffersToValues(parts, endpoint.output); @@ -217,8 +216,8 @@ export class ResultsParser implements IResultsParser { }; } - private createBundleOnWriteLogWhereFirstTopicEqualsAddress(logs: ITransactionLogs, address: IBech32Address): UntypedOutcomeBundle | null { - let hexAddress = adaptToAddress(address).hex(); + private createBundleOnWriteLogWhereFirstTopicEqualsAddress(logs: ITransactionLogs, address: IAddress): UntypedOutcomeBundle | null { + let hexAddress = new Address(address.bech32()).hex(); let eventWriteLogWhereTopicIsSender = logs.findSingleOrNoneEvent( WellKnownEvents.OnWriteLog, diff --git a/src/smartcontracts/smartContract.local.net.spec.ts b/src/smartcontracts/smartContract.local.net.spec.ts index 87bbcf4a3..f8e9a8a59 100644 --- a/src/smartcontracts/smartContract.local.net.spec.ts +++ b/src/smartcontracts/smartContract.local.net.spec.ts @@ -3,7 +3,7 @@ import { GasLimit } from "../networkParams"; import { TransactionWatcher } from "../transactionWatcher"; import { ContractFunction } from "./function"; import { loadTestWallets, TestWallet } from "../testutils/wallets"; -import { loadContractCode } from "../testutils"; +import { prepareDeployment } from "../testutils"; import { Logger } from "../logger"; import { assert } from "chai"; import { AddressValue, BigUIntValue, OptionalValue, OptionValue, TokenIdentifierValue, U32Value } from "./typesystem"; @@ -33,17 +33,16 @@ describe("test on local testnet", function () { // Deploy let contract = new SmartContract({}); - let transactionDeploy = contract.deploy({ - code: await loadContractCode("src/testdata/counter.wasm"), + + let transactionDeploy = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/counter.wasm", gasLimit: new GasLimit(3000000), + initArguments: [], chainID: network.ChainID }); - transactionDeploy.setNonce(alice.account.nonce); - await alice.signer.sign(transactionDeploy); - - alice.account.incrementNonce(); - // ++ let transactionIncrement = contract.call({ func: new ContractFunction("increment"), @@ -105,17 +104,16 @@ describe("test on local testnet", function () { // Deploy let contract = new SmartContract({}); - let transactionDeploy = contract.deploy({ - code: await loadContractCode("src/testdata/counter.wasm"), + + let transactionDeploy = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/counter.wasm", gasLimit: new GasLimit(3000000), + initArguments: [], chainID: network.ChainID }); - transactionDeploy.setNonce(alice.account.nonce); - await alice.signer.sign(transactionDeploy); - - alice.account.incrementNonce(); - // ++ let transactionIncrementFirst = contract.call({ func: new ContractFunction("increment"), @@ -166,19 +164,16 @@ describe("test on local testnet", function () { // Deploy let contract = new SmartContract({}); - let transactionDeploy = contract.deploy({ - code: await loadContractCode("src/testdata/erc20.wasm"), + + let transactionDeploy = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/erc20.wasm", gasLimit: new GasLimit(50000000), initArguments: [new U32Value(10000)], chainID: network.ChainID }); - // The deploy transaction should be signed, so that the address of the contract - // (required for the subsequent transactions) is computed. - transactionDeploy.setNonce(alice.account.nonce); - await alice.signer.sign(transactionDeploy); - alice.account.incrementNonce(); - // Minting let transactionMintBob = contract.call({ func: new ContractFunction("transferToken"), @@ -253,19 +248,16 @@ describe("test on local testnet", function () { // Deploy let contract = new SmartContract({}); - let transactionDeploy = contract.deploy({ - code: await loadContractCode("src/testdata/lottery-esdt.wasm"), + + let transactionDeploy = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/lottery-esdt.wasm", gasLimit: new GasLimit(50000000), initArguments: [], chainID: network.ChainID }); - // The deploy transaction should be signed, so that the address of the contract - // (required for the subsequent transactions) is computed. - transactionDeploy.setNonce(alice.account.nonce); - await alice.signer.sign(transactionDeploy); - alice.account.incrementNonce(); - // Start let transactionStart = contract.call({ func: new ContractFunction("start"), diff --git a/src/smartcontracts/smartContract.spec.ts b/src/smartcontracts/smartContract.spec.ts index 146127340..bece92d44 100644 --- a/src/smartcontracts/smartContract.spec.ts +++ b/src/smartcontracts/smartContract.spec.ts @@ -25,11 +25,9 @@ describe("test contract", () => { let owner = new Address("93ee6143cdc10ce79f15b2a6c2ad38e9b6021c72a1779051f47154fd54cfbd5e"); let firstContractAddress = SmartContract.computeAddress(owner, new Nonce(0)); - assert.equal(firstContractAddress.hex(), "00000000000000000500bb652200ed1f994200ab6699462cab4b1af7b11ebd5e"); assert.equal(firstContractAddress.bech32(), "erd1qqqqqqqqqqqqqpgqhdjjyq8dr7v5yq9tv6v5vt9tfvd00vg7h40q6779zn"); let secondContractAddress = SmartContract.computeAddress(owner, new Nonce(1)); - assert.equal(secondContractAddress.hex(), "000000000000000005006e4f90488e27342f9a46e1809452c85ee7186566bd5e"); assert.equal(secondContractAddress.bech32(), "erd1qqqqqqqqqqqqqpgqde8eqjywyu6zlxjxuxqfg5kgtmn3setxh40qen8egy"); }); @@ -55,11 +53,13 @@ describe("test contract", () => { assert.equal(deployTransaction.getGasLimit().valueOf(), 1000000); assert.equal(deployTransaction.getNonce().valueOf(), 42); - // Sign transaction, then check contract address (should be computed upon signing) - alice.signer.sign(deployTransaction); - assert.equal(contract.getOwner().bech32(), alice.address.bech32()); + // Compute & set the contract address + contract.setAddress(SmartContract.computeAddress(alice.address, 42)); assert.equal(contract.getAddress().bech32(), "erd1qqqqqqqqqqqqqpgq3ytm9m8dpeud35v3us20vsafp77smqghd8ss4jtm0q"); + // Sign the transaction + alice.signer.sign(deployTransaction); + // Now let's broadcast the deploy transaction, and wait for its execution. let hash = await provider.sendTransaction(deployTransaction); diff --git a/src/smartcontracts/smartContract.ts b/src/smartcontracts/smartContract.ts index 239d7f55a..b29940bd6 100644 --- a/src/smartcontracts/smartContract.ts +++ b/src/smartcontracts/smartContract.ts @@ -2,11 +2,9 @@ import { Balance } from "../balance"; import { Address } from "../address"; import { Transaction } from "../transaction"; import { TransactionPayload } from "../transactionPayload"; -import { Code } from "./code"; import { CodeMetadata } from "./codeMetadata"; import { CallArguments, DeployArguments, ISmartContract, QueryArguments, UpgradeArguments } from "./interface"; import { ArwenVirtualMachine } from "./transactionPayloadBuilders"; -import { Nonce } from "../nonce"; import { ContractFunction } from "./function"; import { Query } from "./query"; import { SmartContractAbi } from "./abi"; @@ -16,16 +14,15 @@ import { bigIntToBuffer } from "./codec/utils"; import BigNumber from "bignumber.js"; import { Interaction } from "./interaction"; import { NativeSerializer } from "./nativeSerializer"; +import { IAddress, INonce } from "../interface"; +import { ErrContractHasNoAddress } from "../errors"; const createKeccakHash = require("keccak"); /** * An abstraction for deploying and interacting with Smart Contracts. */ export class SmartContract implements ISmartContract { - private owner: Address = new Address(); - private address: Address = new Address(); - private code: Code = Code.nothing(); - private codeMetadata: CodeMetadata = new CodeMetadata(); + private address: IAddress = new Address(); private abi?: SmartContractAbi; /** @@ -46,7 +43,7 @@ export class SmartContract implements ISmartContract { /** * Create a SmartContract object by providing its address on the Network. */ - constructor({ address, abi }: { address?: Address, abi?: SmartContractAbi }) { + constructor({ address, abi }: { address?: IAddress, abi?: SmartContractAbi }) { this.address = address || new Address(); this.abi = abi; @@ -84,42 +81,17 @@ export class SmartContract implements ISmartContract { /** * Sets the address, as on Network. */ - setAddress(address: Address) { + setAddress(address: IAddress) { this.address = address; } /** * Gets the address, as on Network. */ - getAddress(): Address { + getAddress(): IAddress { return this.address; } - /** - * Gets the owner address. - * - * Note that this function doesn't query the Network, but uses the information acquired when signing a deployment transaction. - * Therefore, currently, this function is useful only in the context of deploying Smart Contracts. - */ - getOwner(): Address { - this.owner.assertNotEmpty(); - return this.owner; - } - - /** - * Gets the {@link Code} of the Smart Contract. Does not query the Network. - */ - getCode(): Code { - return this.code; - } - - /** - * Gets the {@link CodeMetadata} of the Smart Contract. Does not query the Network. - */ - getCodeMetadata(): CodeMetadata { - return this.codeMetadata; - } - setAbi(abi: SmartContractAbi) { this.abi = abi; } @@ -156,24 +128,15 @@ export class SmartContract implements ISmartContract { chainID: chainID }); - this.code = code; - this.codeMetadata = codeMetadata; - transaction.onSigned.on(this.onDeploySigned.bind(this)); - return transaction; } - private onDeploySigned({ transaction, signedBy }: { transaction: Transaction, signedBy: Address }) { - this.owner = signedBy; - let nonce = transaction.getNonce(); - let address = SmartContract.computeAddress(this.owner, nonce); - this.setAddress(address); - } - /** * Creates a {@link Transaction} for upgrading the Smart Contract on the Network. */ upgrade({ code, codeMetadata, initArguments, value, gasLimit, gasPrice, chainID }: UpgradeArguments): Transaction { + this.ensureHasAddress(); + codeMetadata = codeMetadata || new CodeMetadata(); initArguments = initArguments || []; value = value || Balance.Zero(); @@ -193,9 +156,6 @@ export class SmartContract implements ISmartContract { chainID: chainID }); - this.code = code; - this.codeMetadata = codeMetadata; - return transaction; } @@ -203,6 +163,8 @@ export class SmartContract implements ISmartContract { * Creates a {@link Transaction} for calling (a function of) the Smart Contract. */ call({ func, args, value, gasLimit, receiver, gasPrice, chainID }: CallArguments): Transaction { + this.ensureHasAddress(); + args = args || []; value = value || Balance.Zero(); @@ -224,8 +186,10 @@ export class SmartContract implements ISmartContract { } createQuery({ func, args, value, caller }: QueryArguments): Query { + this.ensureHasAddress(); + return new Query({ - address: this.address, + address: this.getAddress(), func: func, args: args, value: value, @@ -233,6 +197,12 @@ export class SmartContract implements ISmartContract { }); } + private ensureHasAddress() { + if (!this.getAddress().bech32()) { + throw new ErrContractHasNoAddress(); + } + } + /** * Computes the address of a Smart Contract. * The address is computed deterministically, from the address of the owner and the nonce of the deployment transaction. @@ -240,9 +210,9 @@ export class SmartContract implements ISmartContract { * @param owner The owner of the Smart Contract * @param nonce The owner nonce used for the deployment transaction */ - static computeAddress(owner: Address, nonce: Nonce): Address { + static computeAddress(owner: IAddress, nonce: INonce): IAddress { let initialPadding = Buffer.alloc(8, 0); - let ownerPubkey = owner.pubkey(); + let ownerPubkey = new Address(owner.bech32()).pubkey(); let shardSelector = ownerPubkey.slice(30); let ownerNonceBytes = Buffer.alloc(8); diff --git a/src/smartcontracts/smartContractResults.local.net.spec.ts b/src/smartcontracts/smartContractResults.local.net.spec.ts index abf42d4f6..176fcb180 100644 --- a/src/smartcontracts/smartContractResults.local.net.spec.ts +++ b/src/smartcontracts/smartContractResults.local.net.spec.ts @@ -1,4 +1,4 @@ -import { loadContractCode, loadTestWallets, TestWallet } from "../testutils"; +import { loadTestWallets, prepareDeployment, TestWallet } from "../testutils"; import { TransactionWatcher } from "../transactionWatcher"; import { GasLimit } from "../networkParams"; import { assert } from "chai"; @@ -28,17 +28,16 @@ describe("fetch transactions from local testnet", function () { // Deploy let contract = new SmartContract({}); - let transactionDeploy = contract.deploy({ - code: await loadContractCode("src/testdata/counter.wasm"), + + let transactionDeploy = await prepareDeployment({ + contract: contract, + deployer: alice, + codePath: "src/testdata/counter.wasm", gasLimit: new GasLimit(3000000), + initArguments: [], chainID: network.ChainID }); - transactionDeploy.setNonce(alice.account.nonce); - await alice.signer.sign(transactionDeploy); - - alice.account.incrementNonce(); - // ++ let transactionIncrement = contract.call({ func: new ContractFunction("increment"), diff --git a/src/smartcontracts/typesystem/abiRegistry.spec.ts b/src/smartcontracts/typesystem/abiRegistry.spec.ts index a45a5f314..30e41de64 100644 --- a/src/smartcontracts/typesystem/abiRegistry.spec.ts +++ b/src/smartcontracts/typesystem/abiRegistry.spec.ts @@ -1,7 +1,6 @@ import { assert } from "chai"; -import { extendAbiRegistry, loadAbiRegistry } from "../../testutils"; +import { loadAbiRegistry } from "../../testutils"; import { BinaryCodec } from "../codec"; -import { AbiRegistry } from "./abiRegistry"; import { AddressType } from "./address"; import { OptionalType } from "./algebraic"; import { BytesType } from "./bytes"; @@ -12,46 +11,21 @@ import { StructType } from "./struct"; import { TokenIdentifierType } from "./tokenIdentifier"; describe("test abi registry", () => { - it("should extend", async () => { - let registry = new AbiRegistry(); - - await extendAbiRegistry(registry, "src/testdata/answer.abi.json"); - assert.lengthOf(registry.interfaces, 1); - assert.lengthOf(registry.customTypes, 0); - assert.lengthOf(registry.getInterface("answer").endpoints, 1); - - await extendAbiRegistry(registry, "src/testdata/counter.abi.json"); - assert.lengthOf(registry.interfaces, 2); - assert.lengthOf(registry.customTypes, 0); - assert.lengthOf(registry.getInterface("counter").endpoints, 3); - - await extendAbiRegistry(registry, "src/testdata/lottery-esdt.abi.json"); - assert.lengthOf(registry.interfaces, 3); - assert.lengthOf(registry.customTypes, 2); - - assert.lengthOf(registry.getInterface("Lottery").endpoints, 7); - assert.lengthOf(registry.getStruct("LotteryInfo").getFieldsDefinitions(), 7); - assert.lengthOf(registry.getEnum("Status").variants, 3); - }); - it("load should also remap known to types", async () => { - let registry = await loadAbiRegistry([ - "src/testdata/answer.abi.json", - "src/testdata/counter.abi.json", - "src/testdata/lottery-esdt.abi.json", - ]); - // Ultimate answer + let registry = await loadAbiRegistry("src/testdata/answer.abi.json"); let answer = registry.getInterface("answer"); let getUltimateAnswer = answer.getEndpoint("getUltimateAnswer"); assert.instanceOf(getUltimateAnswer.output[0].type, I64Type); // Counter + registry = await loadAbiRegistry("src/testdata/counter.abi.json"); let counter = registry.getInterface("counter"); let getCounter = counter.getEndpoint("get"); assert.instanceOf(getCounter.output[0].type, I64Type); // Lottery + registry = await loadAbiRegistry("src/testdata/lottery-esdt.abi.json"); let lottery = registry.getInterface("Lottery"); let start = lottery.getEndpoint("start"); let getStatus = lottery.getEndpoint("status"); @@ -90,7 +64,7 @@ describe("test abi registry", () => { "hex" ); - let registry = await loadAbiRegistry(["src/testdata/multisig.abi.json"]); + let registry = await loadAbiRegistry("src/testdata/multisig.abi.json"); let multisig = registry.getInterface("Multisig"); let performAction = multisig.getEndpoint("getActionData"); assert.equal(performAction.output[0].type.getName(), "Action"); diff --git a/src/smartcontracts/typesystem/abiRegistry.ts b/src/smartcontracts/typesystem/abiRegistry.ts index aae72569c..495a9274f 100644 --- a/src/smartcontracts/typesystem/abiRegistry.ts +++ b/src/smartcontracts/typesystem/abiRegistry.ts @@ -1,6 +1,4 @@ -import * as fs from "fs"; import * as errors from "../../errors"; -import axios, { AxiosResponse } from "axios"; import { guardValueIsSetWithMessage } from "../../utils"; import { StructType } from "./struct"; import { ContractInterface } from "./contractInterface"; @@ -12,38 +10,14 @@ import { EndpointDefinition, EndpointParameterDefinition } from "./endpoint"; export class AbiRegistry { readonly interfaces: ContractInterface[] = []; readonly customTypes: CustomType[] = []; - /** - * Convenience factory function to load ABIs (from files or URLs). - * This function will also remap ABI types to know types (on best-efforts basis). - */ - static async load(json: { files?: string[]; urls?: string[] }): Promise { - let registry = new AbiRegistry(); - for (const file of json.files || []) { - await registry.extendFromFile(file); - } - for (const url of json.urls || []) { - await registry.extendFromUrl(url); - } - registry = registry.remapToKnownTypes(); - return registry; - } - /** - * Generally, one should use {@link AbiRegistry.load} instead. - */ - async extendFromFile(file: string): Promise { - let jsonContent: string = await fs.promises.readFile(file, { encoding: "utf8" }); - let json = JSON.parse(jsonContent); - return this.extend(json); - } - /** - * Generally, one should use {@link AbiRegistry.load} instead. - */ - async extendFromUrl(url: string): Promise { - let response: AxiosResponse = await axios.get(url); - let json = response.data; - return this.extend(json); + + static create(json: { name: string; endpoints: any[]; types: any[] }): AbiRegistry { + let registry = new AbiRegistry().extend(json); + let remappedRegistry = registry.remapToKnownTypes(); + return remappedRegistry; } - extend(json: { name: string; endpoints: any[]; types: any[] }): AbiRegistry { + + private extend(json: { name: string; endpoints: any[]; types: any[] }): AbiRegistry { json.types = json.types || {}; // The "endpoints" collection is interpreted by "ContractInterface". let iface = ContractInterface.fromJSON(json); @@ -92,7 +66,7 @@ export class AbiRegistry { return names.map((name) => this.getEnum(name)); } /** - * Right after loading ABI definitions into a registry (e.g. from a file), the endpoints and the custom types (structs, enums) + * Right after loading ABI definitions into a registry, the endpoints and the custom types (structs, enums) * use raw types for their I/O parameters (in the case of endpoints), or for their fields (in the case of structs). * * A raw type is merely an instance of {@link Type}, with a given name and type parameters (if it's a generic type). diff --git a/src/smartcontracts/typesystem/address.ts b/src/smartcontracts/typesystem/address.ts index 54bea43bc..25a869840 100644 --- a/src/smartcontracts/typesystem/address.ts +++ b/src/smartcontracts/typesystem/address.ts @@ -1,4 +1,5 @@ import { Address } from "../../address"; +import { IAddress } from "../../interface"; import { PrimitiveType, PrimitiveValue } from "./types"; export class AddressType extends PrimitiveType { @@ -20,9 +21,9 @@ export class AddressValue extends PrimitiveValue { static ClassName = "AddressValue"; private readonly value: Address; - constructor(value: Address) { + constructor(value: IAddress) { super(new AddressType()); - this.value = value; + this.value = new Address(value.bech32()); } getClassName(): string { diff --git a/src/testutils/contractController.ts b/src/testutils/contractController.ts index cae9e28cc..4806d0953 100644 --- a/src/testutils/contractController.ts +++ b/src/testutils/contractController.ts @@ -1,6 +1,6 @@ import { Interaction } from "../smartcontracts/interaction"; import { Transaction } from "../transaction"; -import { TypedOutcomeBundle, IResultsParser, UntypedOutcomeBundle } from "../smartcontracts/interface"; +import { TypedOutcomeBundle, UntypedOutcomeBundle } from "../smartcontracts/interface"; import { ResultsParser } from "../smartcontracts/resultsParser"; import { Logger } from "../logger"; import { TransactionWatcher } from "../transactionWatcher"; @@ -8,7 +8,7 @@ import { ITransactionOnNetwork } from "../interfaceOfNetwork"; import { INetworkProvider } from "./networkProviders"; export class ContractController { - private readonly parser: IResultsParser; + private readonly parser: ResultsParser; private readonly provider: INetworkProvider; private readonly transactionCompletionAwaiter: TransactionWatcher; diff --git a/src/testutils/mockProvider.ts b/src/testutils/mockProvider.ts index 4a3d00c21..7dac160d4 100644 --- a/src/testutils/mockProvider.ts +++ b/src/testutils/mockProvider.ts @@ -1,4 +1,4 @@ -import { IBech32Address, IHash } from "../interface"; +import { IAddress, IHash } from "../interface"; import { Transaction, TransactionHash } from "../transaction"; import { Address } from "../address"; import { Nonce } from "../nonce"; @@ -6,7 +6,6 @@ import { AsyncTimer } from "../asyncTimer"; import { Balance } from "../balance"; import * as errors from "../errors"; import { Query } from "../smartcontracts/query"; -import { TypedEvent } from "../events"; import { IAccountOnNetwork, IContractQueryResponse, INetworkConfig, ITransactionOnNetwork, ITransactionStatus } from "../interfaceOfNetwork"; import { ErrMock } from "../errors"; import { AccountOnNetwork, ContractResultItem, ContractResults, TransactionOnNetwork, TransactionStatus } from "@elrondnetwork/erdjs-network-providers"; @@ -17,14 +16,13 @@ export class MockProvider { static AddressOfCarol = new Address("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"); private readonly transactions: Map; - private readonly onTransactionSent: TypedEvent<{ transaction: Transaction }>; + private nextTransactionTimelinePoints: any[] = []; private readonly accounts: Map; private readonly queryContractResponders: QueryContractResponder[] = []; private readonly getTransactionResponders: GetTransactionResponder[] = []; constructor() { this.transactions = new Map(); - this.onTransactionSent = new TypedEvent(); this.accounts = new Map(); this.accounts.set( @@ -78,19 +76,11 @@ export class MockProvider { } async mockTransactionTimeline(transaction: Transaction, timelinePoints: any[]): Promise { - await transaction.awaitHashed(); return this.mockTransactionTimelineByHash(transaction.getHash(), timelinePoints); } async mockNextTransactionTimeline(timelinePoints: any[]): Promise { - let transaction = await this.nextTransactionSent(); - return this.mockTransactionTimelineByHash(transaction.getHash(), timelinePoints); - } - - private async nextTransactionSent(): Promise { - return new Promise((resolve, _reject) => { - this.onTransactionSent.on((eventArgs) => resolve(eventArgs.transaction)); - }); + this.nextTransactionTimelinePoints = timelinePoints; } async mockTransactionTimelineByHash(hash: TransactionHash, timelinePoints: any[]): Promise { @@ -113,7 +103,7 @@ export class MockProvider { } } - async getAccount(address: IBech32Address): Promise { + async getAccount(address: IAddress): Promise { let account = this.accounts.get(address.bech32()); if (account) { return account; @@ -133,8 +123,7 @@ export class MockProvider { }) ); - this.onTransactionSent.emit({ transaction: transaction }); - + this.mockTransactionTimeline(transaction, this.nextTransactionTimelinePoints); return transaction.getHash(); } @@ -144,7 +133,7 @@ export class MockProvider { async getTransaction( txHash: IHash, - _hintSender?: IBech32Address, + _hintSender?: IAddress, _withResults?: boolean ): Promise { // At first, try to use a mock responder diff --git a/src/testutils/networkProviders.ts b/src/testutils/networkProviders.ts index a45668787..01ca92299 100644 --- a/src/testutils/networkProviders.ts +++ b/src/testutils/networkProviders.ts @@ -1,5 +1,5 @@ import { createProxyNetworkProvider } from "@elrondnetwork/erdjs-network-providers"; -import { IBech32Address, IHash } from "../interface"; +import { IAddress, IHash } from "../interface"; import { IAccountOnNetwork, IContractQueryResponse, INetworkConfig, ITransactionOnNetwork, ITransactionStatus } from "../interfaceOfNetwork"; import { Query } from "../smartcontracts/query"; import { Transaction } from "../transaction"; @@ -10,7 +10,7 @@ export function createLocalnetProvider(): INetworkProvider { export interface INetworkProvider { getNetworkConfig(): Promise; - getAccount(address: IBech32Address): Promise; + getAccount(address: IAddress): Promise; getTransaction(txHash: IHash): Promise; getTransactionStatus(txHash: IHash): Promise; sendTransaction(tx: Transaction): Promise; diff --git a/src/testutils/utils.ts b/src/testutils/utils.ts index cdedc5c52..fb2df2853 100644 --- a/src/testutils/utils.ts +++ b/src/testutils/utils.ts @@ -1,34 +1,70 @@ import { PathLike } from "fs"; +import * as fs from "fs"; +import { SmartContract } from "../smartcontracts/smartContract"; import { Code } from "../smartcontracts/code"; -import { AbiRegistry } from "../smartcontracts/typesystem"; +import { AbiRegistry, TypedValue } from "../smartcontracts/typesystem"; +import { Transaction } from "../transaction"; import { TransactionWatcher } from "../transactionWatcher"; +import { IChainID, IGasLimit } from "../interface"; +import { TestWallet } from "./wallets"; +import axios, { AxiosResponse } from "axios"; -export async function loadContractCode(path: PathLike): Promise { - if (isOnBrowserTests()) { - return Code.fromUrl(path.toString()); - } +export async function prepareDeployment(obj: { + deployer: TestWallet, + contract: SmartContract, + codePath: string, + initArguments: TypedValue[], + gasLimit: IGasLimit, + chainID: IChainID +}): Promise { + let contract = obj.contract; + let deployer = obj.deployer; - return Code.fromFile(path); -} + let transaction = obj.contract.deploy({ + code: await loadContractCode(obj.codePath), + gasLimit: obj.gasLimit, + initArguments: obj.initArguments, + chainID: obj.chainID + }); -export async function loadAbiRegistry(paths: PathLike[]): Promise { - let sources = paths.map(e => e.toString()); + let nonce = deployer.account.getNonceThenIncrement(); + let contractAddress = SmartContract.computeAddress(deployer.address, nonce); + transaction.setNonce(nonce); + contract.setAddress(contractAddress); + await deployer.signer.sign(transaction); + + return transaction; +} +export async function loadContractCode(path: PathLike): Promise { if (isOnBrowserTests()) { - return AbiRegistry.load({ urls: sources }); - } + let response: AxiosResponse = await axios.get(path.toString(), { + responseType: 'arraybuffer', + transformResponse: [], + headers: { + "Accept": "application/wasm" + } + }); - return AbiRegistry.load({ files: sources }); + let buffer = Buffer.from(response.data); + return Code.fromBuffer(buffer); + } + + // Load from file. + let buffer: Buffer = await fs.promises.readFile(path); + return Code.fromBuffer(buffer); } -export async function extendAbiRegistry(registry: AbiRegistry, path: PathLike): Promise { - let source = path.toString(); - +export async function loadAbiRegistry(path: PathLike): Promise { if (isOnBrowserTests()) { - return registry.extendFromUrl(source); + let response: AxiosResponse = await axios.get(path.toString()); + return AbiRegistry.create(response.data); } - return registry.extendFromFile(source); + // Load from files + let jsonContent: string = await fs.promises.readFile(path, { encoding: "utf8" }); + let json = JSON.parse(jsonContent); + return AbiRegistry.create(json); } export function isOnBrowserTests() { diff --git a/src/testutils/wallets.ts b/src/testutils/wallets.ts index e758eb85e..e7fef128e 100644 --- a/src/testutils/wallets.ts +++ b/src/testutils/wallets.ts @@ -3,13 +3,13 @@ import * as fs from "fs"; import * as path from "path"; import { Account } from "../account"; import { Address } from "../address"; -import { IBech32Address } from "../interface"; +import { IAddress } from "../interface"; import { isOnBrowserTests } from "./utils"; import { UserSecretKey, UserSigner } from "@elrondnetwork/erdjs-walletcore" import { IAccountOnNetwork } from "../interfaceOfNetwork"; interface IAccountFetcher { - getAccount(address: IBech32Address): Promise; + getAccount(address: IAddress): Promise; } export async function loadAndSyncTestWallets(provider: IAccountFetcher): Promise> { diff --git a/src/transaction.ts b/src/transaction.ts index 9cbb7f622..896008580 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,5 +1,5 @@ import { BigNumber } from "bignumber.js"; -import { IBech32Address, IChainID, IGasLimit, IGasPrice, ISignable, ISignature } from "./interface"; +import { IAddress, IChainID, IGasLimit, IGasPrice, ISignature } from "./interface"; import { Address } from "./address"; import { Balance } from "./balance"; import { @@ -14,10 +14,8 @@ import { Signature } from "./signature"; import { guardNotEmpty } from "./utils"; import { TransactionPayload } from "./transactionPayload"; import * as errors from "./errors"; -import { TypedEvent } from "./events"; import { ProtoSerializer } from "./proto"; import { Hash } from "./hash"; -import { adaptToAddress, adaptToSignature } from "./boundaryAdapters"; import { INetworkConfig } from "./interfaceOfNetwork"; const createTransactionHasher = require("blake2b"); @@ -26,11 +24,7 @@ const TRANSACTION_HASH_LENGTH = 32; /** * An abstraction for creating, signing and broadcasting Elrond transactions. */ -export class Transaction implements ISignable { - readonly onSigned: TypedEvent<{ - transaction: Transaction; - signedBy: Address; - }>; +export class Transaction { /** * The nonce of the transaction (the account sequence number of the sender). */ @@ -44,12 +38,12 @@ export class Transaction implements ISignable { /** * The address of the sender. */ - private sender: Address; + private sender: IAddress; /** * The address of the receiver. */ - private readonly receiver: Address; + private readonly receiver: IAddress; /** * The gas price to be used. @@ -84,7 +78,7 @@ export class Transaction implements ISignable { /** * The signature. */ - private signature: Signature; + private signature: ISignature; /** * The transaction hash, also used as a transaction identifier. @@ -108,8 +102,8 @@ export class Transaction implements ISignable { }: { nonce?: Nonce; value?: Balance; - receiver: Address; - sender?: Address; + receiver: IAddress; + sender?: IAddress; gasPrice?: IGasPrice; gasLimit: IGasLimit; data?: TransactionPayload; @@ -130,8 +124,6 @@ export class Transaction implements ISignable { this.signature = Signature.empty(); this.hash = TransactionHash.empty(); - - this.onSigned = new TypedEvent(); } getNonce(): Nonce { @@ -166,11 +158,11 @@ export class Transaction implements ISignable { this.value = value; } - getSender(): Address { + getSender(): IAddress { return this.sender; } - getReceiver(): Address { + getReceiver(): IAddress { return this.receiver; } @@ -210,8 +202,7 @@ export class Transaction implements ISignable { return this.options; } - getSignature(): Signature { - guardNotEmpty(this.signature, "signature"); + getSignature(): ISignature { return this.signature; } @@ -226,11 +217,9 @@ export class Transaction implements ISignable { * * @param signedBy The address of the future signer */ - serializeForSigning(signedBy: IBech32Address): Buffer { - let adaptedSignedBy = adaptToAddress(signedBy); - + serializeForSigning(signedBy: IAddress): Buffer { // TODO: for appropriate tx.version, interpret tx.options accordingly and sign using the content / data hash - let plain = this.toPlainObject(adaptedSignedBy); + let plain = this.toPlainObject(signedBy); // Make sure we never sign the transaction with another signature set up (useful when using the same method for verification) if (plain.signature) { delete plain.signature; @@ -246,7 +235,7 @@ export class Transaction implements ISignable { * * @param sender The address of the sender (will be provided when called within the signing procedure) */ - toPlainObject(sender?: Address): any { + toPlainObject(sender?: IAddress): any { return { nonce: this.nonce.valueOf(), value: this.value.toString(), @@ -258,7 +247,7 @@ export class Transaction implements ISignable { chainID: this.chainID.valueOf(), version: this.version.valueOf(), options: this.options.valueOf() == 0 ? undefined : this.options.valueOf(), - signature: this.signature.isEmpty() ? undefined : this.signature.hex(), + signature: this.signature.hex() ? this.signature.hex() : undefined, }; } @@ -294,15 +283,10 @@ export class Transaction implements ISignable { * @param signature The signature, as computed by a signer. * @param signedBy The address of the signer. */ - applySignature(signature: ISignature, signedBy: IBech32Address) { - let adaptedSignature = adaptToSignature(signature); - let adaptedSignedBy = adaptToAddress(signedBy); - - this.signature = adaptedSignature; - this.sender = adaptedSignedBy; - + applySignature(signature: ISignature, signedBy: IAddress) { + this.signature = signature; + this.sender = signedBy; this.hash = TransactionHash.compute(this); - this.onSigned.emit({ transaction: this, signedBy: adaptedSignedBy }); } /** @@ -310,33 +294,9 @@ export class Transaction implements ISignable { * Called internally by the network provider. */ toSendable(): any { - if (this.signature.isEmpty()) { - throw new errors.ErrTransactionNotSigned(); - } - return this.toPlainObject(); } - async awaitSigned(): Promise { - if (!this.signature.isEmpty()) { - return; - } - - return new Promise((resolve, _reject) => { - this.onSigned.on(() => resolve()); - }); - } - - async awaitHashed(): Promise { - if (!this.hash.isEmpty()) { - return; - } - - return new Promise((resolve, _reject) => { - this.onSigned.on(() => resolve()); - }); - } - /** * Computes the current transaction fee based on the {@link NetworkConfig} and transaction properties * @param networkConfig {@link NetworkConfig}