diff --git a/.eslintrc b/.eslintrc index 9b11dad..bc86843 100644 --- a/.eslintrc +++ b/.eslintrc @@ -116,6 +116,7 @@ "globals": { "tronWeb": "readonly", "tronWrap": "readonly", + "web3": "readonly", "assert": "readonly", "expect": "readonly", "artifacts": "readonly", diff --git a/CHANGELOG.md b/CHANGELOG.md index baabd10..38c055d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +__4.0.0__ +* Support deploying smart contracts to the EVM chain + __3.4.4__ * Dropping support for Node.js v16 As part of this release, we are dropping support for Node.js v16. This version of Node.js reached its end-of-life in September of last year. diff --git a/package-lock.json b/package-lock.json index f593018..3ce64c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tronbox", - "version": "3.4.4", + "version": "4.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tronbox", - "version": "3.4.4", + "version": "4.0.0", "dependencies": { "@noble/secp256k1": "1.7.1", "@resolver-engine/imports-fs": "^0.3.3", diff --git a/package.json b/package.json index fe86f71..ee8893f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tronbox", "namespace": "tronprotocol", - "version": "3.4.4", + "version": "4.0.0", "description": "TronBox - Simple development framework for Tron", "keywords": [ "TronBox", diff --git a/signature.json b/signature.json index 9fba122..4470c97 100644 --- a/signature.json +++ b/signature.json @@ -18,7 +18,7 @@ }, { "path": ".eslintrc", - "sha512": "707b766eb318efe16596da9c1d9db132f0253abb81167d34420881f2bbd997207ae868aa359dcd70d5eaa242f7d90d16ab6ca294a58e97070b30a33186d9816a" + "sha512": "6b1a8f7be4986cb8f0023f049b10825d33f1ae4e334af07fcd4e71c53d605f65b650b014a66150aee3463e7bb909656b5b7874cba7ad40b685df5406119603a2" }, { "path": ".gitmodules", @@ -38,7 +38,7 @@ }, { "path": "build/components/Artifactor.js", - "sha512": "03d978e6f34c7d3951abbef6d35e7a7075d7a28b5f5f078bc5453ec26d0c2f299971136192e32aff345c0fe94264ecb7ec38d6e2dd30a86af7f569962e1aea3c" + "sha512": "058b869e58e3432168adfd26de6d87da637ae811e425192b76d656271511a9baef6e1d3445e64596b35fe63c9ac8f93f21468687de5ab715d56d9f67da0480e9" }, { "path": "build/lib/assertions.js", @@ -74,7 +74,7 @@ }, { "path": "build/components/Config.js", - "sha512": "43bfc03b5286f5355e413a60e6a3aa9d02a7025b06de96c1b825247c601574485e8b71c4246498da9ee0599f596b82f48935e04ebf5f14bb2483d94ff83d9aa1" + "sha512": "b76679d8af84149e01addb9c335075b00505c725e9b0b72bdb0ada42b69b19dfb7d8fa9b15ab63cf5c70e760f3d5fcb83d6d8f41d5c7331471edd26440187d71" }, { "path": "build/lib/errors/configurationerror.js", @@ -82,19 +82,19 @@ }, { "path": "build/lib/commands/console.js", - "sha512": "6bfffd83cc9e40d7621539982de4104e565a438dd733af8cc5b66b824c29f6b80f9b4dbb41eb61faabcb312fd8ec956ea2126cf987c70be499fc5fa753c89c23" + "sha512": "bbba5214daaa29ffbe22c26c39dcfbc506462f069b5d8a20f3580fc904b50cf0af62646c269d8c39ede5103066b7f44a2dbfe16170c66ffa1e1b18e049b6402e" }, { "path": "build/lib/console.js", - "sha512": "75ef3e9a5f59b5fff090617f340cb6eae2a907728ea6e6d57f9dfe729593554543ff29b2dc770003f182ad8481869eec3e32e27d2ea513b1d8b1ab264fa06f10" + "sha512": "0dcb7b70b53d189dbbdb89986978351a98b1e8bb53e61173f7e985cf490224d889a25fd2cb9acd891c966d37bcb771c334ba109e3d1bce10a05ffd2bf8b34a0e" }, { "path": "build/components/TronWrap/constants.js", - "sha512": "b9cd2ff370691da17055174383a1cc3e80141ff9d947752106ce12bb71f1490335d7c57388dd1064e3212d3bffc2416ba3faef4e1d375ced71233708e5d16c16" + "sha512": "b821977b56f34624b9e2fe56de3410a6e48a518153c6862817ea08d5a98996d91bc9ed94a6d96683323fac6740fbb12bf12794748ebdc9bb8fc2d40a79f9bc05" }, { "path": "build/components/Contract/contract.js", - "sha512": "c3288fce5b7744b280fa755178b716bdea364a62e64b8988bae0712714aae67c910480c0d37334578c4ad1dc336d94bf53b6bc02563360a4772d5a171cf900df" + "sha512": "2e4fe409ba4b6bf6b308159871fc4c8e547ef3166e351ced1467227529a7ed045860b0c274a7b42f8afbcdc1756ce393a11ce4d74c5ab99aac008b9010aabd1d" }, { "path": "build/lib/commands/convert.js", @@ -114,7 +114,7 @@ }, { "path": "build/components/Deployer/src/actions/deploy.js", - "sha512": "290b4283d0e517e676355881d5139259367f5d178541648a4bd6c53ff0ecc20b6689f8d1d716b76a0626e260545b5dceab66fca0dd082d4793b819ca0c2ccd14" + "sha512": "40ca681f0d7fecf93936633e1520cffb539932851658605c66551cfe977b90898db28fb1160d7e24606c4220727e6911c52ef5503568bf35648741d0b1fbecef" }, { "path": "build/lib/commands/deploy.js", @@ -134,7 +134,7 @@ }, { "path": "build/downloader.js", - "sha512": "5d6f6d2cd0b290e030a4291fce14cccfaf38a64af0a8b510f8e75d13933e3d05ff75186b3f5752d84906115f07dce586cfaa8575bc0003371a2ad6dee427c6b2" + "sha512": "4d632f57835d99012153a4ae758bbbe25727ac545014a17a1c1cfb7d0c980061306579e47f46308d57b706d8dd602c471f58975898bb969ab466d380ad8b40f5" }, { "path": "build/lib/environment.js", @@ -194,7 +194,7 @@ }, { "path": "build/components/Migrate/index.js", - "sha512": "bec08abf8b4fe8d7453d92cb9015af73fc9fb6b7748b33bcdf1e465b22c6d5455ebfabe368a36f7dfe10e5da7838ebf8e8533cffc037bdc83ee98f4654dbe8e3" + "sha512": "e7c8f31633d947c93fb473a5bf708b083e6fca1439ceb6732ab37d634b10c91364c457befa3e1adb0edceea699f73afcb9a8dc89173913852055ae5f61e9d28f" }, { "path": "build/components/Provider/index.js", @@ -206,11 +206,11 @@ }, { "path": "build/components/TronWrap/index.js", - "sha512": "c94fa114ddab7c1911aff29d8e09d120e3aecfb546334bcd6f501a12e883ca9c42fe8ab14d71dcff91e005a6399c6206f6a6991002fe642cceb96b9ae9922c8f" + "sha512": "340a76f1103c70f7f498ada61fae7ebb59a338a48ed57ef74760d2e410ede4237dcff656e24a855a3c2e2ca2b64dd541c96d548891780cf930aa0f0bdb9e1b06" }, { "path": "build/index.js", - "sha512": "3c4f30947b0e26c5ea059ab1b3c7e54037911af493e59e160ed1afc6658c4bde001cd4cbc5c443410ff7d87d387af23fbcbd81bf9c0d7bd71c388f4020dcc8e0" + "sha512": "9094d2b85c18f8217487f096602011b1b3d7edbc11f57da477945f8596632f0d587d1306054c26098a9371c78021c3cdc7b5da43b092eb65555a6d11a4465d57" }, { "path": "build/lib/commands/index.js", @@ -234,7 +234,7 @@ }, { "path": "build/lib/commands/migrate.js", - "sha512": "7969ee25a6e06d79fdaa44bd6cbe26080d0a7c441305d6f0eb35a6ee85111b0581b419ccada880c9d12bb31bbba7b08817af587355f52039f7b124ec6c28d5aa" + "sha512": "8cb2ead5c3980e0d293addc3d59e254085cb0d8b09faa36c371d46e9624e7b775a983f3739d19a248bfc1c0426d5f2aa66b1e12bdb73c337352806a884d67c94" }, { "path": "build/lib/networks.js", @@ -268,6 +268,10 @@ "path": "build/components/Provisioner.js", "sha512": "624fe12ee1b80154fd8a08c2e1abfcc02c6552038fed8fb9a27a5c093d1bdb1defc0ea4d0b9df33080c8a47d3d51d2969a734bb30310e12cc2c0e85d4067b38f" }, + { + "path": "build/components/TronWrap/reformat.js", + "sha512": "aa33eb643a80d2fa1763b30449ad2f0f15936de177d45750bb0bd19430204fbe567f6cd7d6a43c807feabbff330aacec09c356558c8879b41b3f0db7b1dfe758" + }, { "path": "build/lib/repl.js", "sha512": "fec7285c96488a1e6c5ebe551b3e5e3b51319bf9ca47c741fa97b80a793e75f487bb68e68cd38f5f29acce759164d1147b0e6e3bfb801c990aa30a88d0146225" @@ -302,11 +306,11 @@ }, { "path": "build/lib/commands/test.js", - "sha512": "14627eb001856954426249fbbcb8646e48e8508498d3e88d0a66ad2851c8041947087803a66fd105f0064b7c029863df9aa7ae9bb5ef22f4a08aaf0e0ed3300e" + "sha512": "32e8f9565730c2e66fb8322714ef8b35c46a3435adaf0c02b23b16a464b9bd8b3484cb9d8212810b692f1dac228ccdec3674b57910c5fde2a0dacee5f321eefa" }, { "path": "build/lib/test.js", - "sha512": "2910a83510d965f8637e603c181fea0b2262ce545e49a1b9ffc9dcbf3ba6f773e8f16400ac6fb68766ea665d8136d3c9c4986d286e35c651c98b6998cb48c331" + "sha512": "8731e84f60819cbfe8dfc4a3f5406ff50c0419876abf8c8ab03519533c9021a16752c7200fe726e350c632f8175ed16e4620297ea0e9c5a0f1018657c52b8a6d" }, { "path": "build/lib/testing/testresolver.js", @@ -326,7 +330,7 @@ }, { "path": "build/components/TronSolc.js", - "sha512": "f00b2793b7603e881c663703ae7257a06ce9dc88ae22c12a14f4a5be4dd947b11900f7f592848e73e78b55af56bae49e94d1c253c8f913eda07726b6a2b0ac55" + "sha512": "8b1864487275cb4d05c01bde98e3b1d0544044099450852ae901d54d8064f3856e232f9f9c5b166dececb4cd5f9b808d1e090171d15e010e10037bb68c4acc24" }, { "path": "build/components/Box/lib/utils/unbox.js", @@ -354,7 +358,7 @@ }, { "path": "build/components/WorkflowCompile.js", - "sha512": "d55f819422e87dad06af23482501134b4e53905d1dd5f42273eb5236154fe510e98d4482d93146c37cd57eed16a9f2fd1893aa7ef26c0cc9b06b4802ccb457d0" + "sha512": "930005582c07b34966d3cfe15b25baa814911f51019c083bf93c40ccabe782ca1b5310fec94b1f22c5bd2afaa6ebc552d1e0106cbf63b721709dc39e987f6e85" }, { "path": "build/components/Provider/wrapper.js", @@ -378,7 +382,7 @@ }, { "path": "CHANGELOG.md", - "sha512": "8070e6e7a902c00bb52b3f9f905c71acfe93305baf630a87f0a9332e852957c98c8ba8609fe7e87cc4a7af0337669e673e67e19903624173548e8cb0762b3c13" + "sha512": "902188032dbf5637726a55ff3dd799d537a350015d60137fcdc81124acea0c8247c98eb240abe8072a799848ca0507ca82632740e5d35b3f63b082644bfd1680" }, { "path": "CONTRIBUTING.md", @@ -433,7 +437,7 @@ "scripts", "version" ], - "sha512": "64e00bb0b16cf4eace2d6428217543c5749598ddd7a50f9f2b1e98b71a0976d70415ebca041ccc6808dddfefcf3a8f65d82febf783825a639c01ff118c0d1e0d" + "sha512": "4e599e9d6c6ba9bbe23defeadee09c51d3141366ed2fd27a3f7344802dcd1d5e51d8896b2e0035982bd8bcdc23f527920df08af9bb5bdc575f18bfd3e55de62d" } }, { @@ -442,7 +446,7 @@ "packageJson": { "name": "tronbox", "namespace": "tronprotocol", - "version": "3.4.4", + "version": "4.0.0", "description": "TronBox - Simple development framework for Tron", "keywords": [ "TronBox", @@ -563,5 +567,5 @@ } } ], - "signature": "-----BEGIN PGP SIGNATURE-----\nVersion: OpenPGP.js v4.10.11\nComment: https://openpgpjs.org\n\nwsFzBAEBCAAGBQJmjKDVACEJEAEScGf7i0tYFiEEuL4l7hUU5AsLzHjPARJw\nZ/uLS1g4Gw//WZN8cXl1nVwwQ7c8v5OZ+p7GA1KMtSk6NJ1Ne2QgL4QL7lPC\nmwKwAuuqT3K47piXl5rQNKCSGoVDUIXA4ykr2UjcYZL4A9ghUdIcEPBJLIer\nXc8Tqmv2mRvu6fg8CBPDfsvuXeXbNNGBTV8LNq3TSgJJXAxUdBeAqpZ9Pg2h\nJMIUy4CrBR8/tkm56FTfhTLC8eNsgAfpqVcrWTUYRup3ao12O3VqpI3SxIzu\n64z7FD/39NDe6R2VFJtpX/TyDAtmt+eUJA5T4MgWseahL86e5Qnt7ojTMCrT\n3+VEkItDXiMfKZGkjSYBq7s1BuNR0BqxklecsWkXMDzTLlT4Rpci7n+1GUkS\nbJ1QzfsrCvAhm8KjdWUKtf394XtTJ2EVYfhlsxPD80+YWh/MA+BIxCgf7sHZ\n34jbqdGd9Z18JSYJd+eV9hbfhxdKIZ3IzeQQEh2/u8uNxdjgBXsAn4TuLl3g\nM7FsecwO++BIKmGRWqAOuy804SUDrYlPCPmJGryROz+/evSWFYVGPDJvUjAf\n70a8DYjCGRMdUuOSAMMky5wwNyeEl8kas5dSn03ZA61AWM7/JRLAvo469Fu6\neWwM4TgnM+lIctDA9UL0iRp38Vh3//D+L5qhl8gOeJqwM40lVmLq++Ukk/WD\ngpmrIkxBUtxcADfnoAo1xvRR+mOkJs9LCeg=\n=WlMf\n-----END PGP SIGNATURE-----\n" + "signature": "-----BEGIN PGP SIGNATURE-----\nVersion: OpenPGP.js v4.10.11\nComment: https://openpgpjs.org\n\nwsFzBAEBCAAGBQJmlNi3ACEJEAEScGf7i0tYFiEEuL4l7hUU5AsLzHjPARJw\nZ/uLS1hs3Q/+K7GyYPlZyYUotPSWNOrgIY6ACDM9sIGfU9ujNeT434lu2L+2\nwsqrrv2DKw83QUU3hDuLMeNJutnALxmiKmo5xlVgrEzO+KEq8Slyh9MNySlj\n22/iSZPWiQmBTga7FY5ZYXkNZ5yybZkkJSR9XCU1LZ4d8GVJqmwXWMOjD8kV\niIh42iSRGVOh3TEPg6M0fRq8AwsohH1/YL9hUyqIgPtIyix3EIQCPqYpu8g6\nJ2k2OBXQnmbk2xj6rD6R+LnuAFUG7A0ICW4g4ZX4nS3dME78nhWbUM0Zsy87\n2e6BITDl45PeQ9Hj8olaaoDPIN7j6J8lVZptxZl+egpKMLdPu7KY3tQ+a2jx\niBE8EUaxXc1FbV++oA6O63Mhk0iBIXaPDJltLKqE8PtmROegnyJoB4AGthSo\ng4cQC22cx3ljLy2YKtRVedsWZEtb+cCH4MFXUxFOXT9fdJHtoZ88+FQVaObG\nCAWXRMaYejKqgbTgmESiD+g9CpkDkxqieLaxrzbJB6B/q35sw3EIJUisqnzf\ncW7VWGSiiQ5t4p4dPJ/DvqqrYKZi4Y1I45McTDC+vNtEconEYfQgf0dOD+IK\nRa1VmQyVxoFOK7EZ/IJkKkAynHIzsERnt5KNr8EoInJAnb2au0Z+JCpWna6p\ni6GFLdPieU/hXwqn0QeBYXWCxQ2+WICBnWg=\n=NYa1\n-----END PGP SIGNATURE-----\n" } \ No newline at end of file diff --git a/src/components/Artifactor.js b/src/components/Artifactor.js index 03422eb..9886639 100755 --- a/src/components/Artifactor.js +++ b/src/components/Artifactor.js @@ -7,7 +7,7 @@ function Artifactor(destination) { this.destination = destination; } -Artifactor.prototype.save = function (object) { +Artifactor.prototype.save = function (object, options) { const self = this; return new Promise(function (accept, reject) { @@ -15,6 +15,10 @@ Artifactor.prototype.save = function (object) { Object.values(object.networks).forEach(_ => (_.address = _.address.toLowerCase().replace(/^0x/, '41'))); + if (options.evm) { + Object.values(object.networks).forEach(_ => (_.address = _.address.toLowerCase().replace(/^41/, '0x'))); + } + if (!object.contractName) { return reject(new Error('You must specify a contract name.')); } @@ -66,7 +70,7 @@ Artifactor.prototype.save = function (object) { }); }; -Artifactor.prototype.saveAll = function (objects) { +Artifactor.prototype.saveAll = function (objects, options) { const self = this; if (Array.isArray(objects)) { @@ -91,7 +95,7 @@ Artifactor.prototype.saveAll = function (objects) { Object.keys(objects).forEach(function (contractName) { const object = objects[contractName]; object.contractName = contractName; - promises.push(self.save(object)); + promises.push(self.save(object, options)); }); return Promise.all(promises); diff --git a/src/components/Config.js b/src/components/Config.js index 253d97a..0e1dd14 100755 --- a/src/components/Config.js +++ b/src/components/Config.js @@ -9,6 +9,7 @@ const originalrequire = require('original-require'); const DEFAULT_CONFIG_FILENAME = 'tronbox.js'; const BACKUP_CONFIG_FILENAME = 'tronbox-config.js'; // For Windows + Command Prompt +const EVM_CONFIG_FILENAME = 'tronbox-evm-config.js'; // For EVM function Config(truffle_directory, working_directory, network) { const self = this; @@ -40,12 +41,7 @@ function Config(truffle_directory, working_directory, network) { registry: '0x8011df4830b4f696cd81393997e5371b93338878', install_provider_uri: 'https://ropsten.infura.io/truffle' }, - solc: { - optimizer: { - enabled: false, - runs: 200 - } - }, + solc: {}, logger: { log: function () {} } @@ -378,6 +374,10 @@ Config.detect = function (options, filename) { !filename ? (search = [DEFAULT_CONFIG_FILENAME, BACKUP_CONFIG_FILENAME]) : (search = filename); + if (options.evm) { + !filename ? (search = [EVM_CONFIG_FILENAME]) : (search = filename); + } + const file = findUp.sync(search, { cwd: options.working_directory || options.workingDirectory }); diff --git a/src/components/Contract/contract.js b/src/components/Contract/contract.js index 4e1081f..bee6a64 100755 --- a/src/components/Contract/contract.js +++ b/src/components/Contract/contract.js @@ -426,9 +426,10 @@ const contract = (function (module) { newContract.setNetwork(this.network_id); } newContract.defaults(this.class_defaults); - newContract.address = TronWrap().address.toHex(address); + newContract.address = tronWrap.address.toHex(address); return newContract.deployed(); }, + call: function (methodName, ...args) { const self = this; let methodArgs = {}; @@ -481,6 +482,7 @@ const contract = (function (module) { tronWrap.triggerContract(option, _callback); }); }, + deployed: function () { const self = this; return new Promise(function (accept, reject) { @@ -488,10 +490,27 @@ const contract = (function (module) { if (!self.isDeployed()) { throw new Error(self.contractName + ' has not been deployed to detected network'); } - self.address = self.address.toLowerCase().replace(/^0x/, '41'); - TronWrap() - .trx.getContract(self.address) - .then(() => { + + let getContract; + if (tronWrap._web3) { + self.address = self.address.toLowerCase().replace(/^41/, '0x'); + getContract = tronWrap._web3.eth.getCode(self.address); + } else { + self.address = self.address.toLowerCase().replace(/^0x/, '41'); + getContract = tronWrap.trx.getContract(self.address); + } + getContract + .then(res => { + const noCodeMsg = `${self.contractName} has not been deployed to detected network; no code at address ${self.address}`; + if (tronWrap._web3) { + if (res === '0x') { + throw new Error(noCodeMsg); + } + } else { + if (!res.contract_address) { + throw new Error(noCodeMsg); + } + } const abi = self.abi || []; for (let i = 0; i < abi.length; i++) { const item = abi[i]; @@ -546,6 +565,7 @@ const contract = (function (module) { return !!this.network.address; }, + setNetwork: function (network_id) { if (!network_id) return; this.network_id = network_id + ''; diff --git a/src/components/Deployer/src/actions/deploy.js b/src/components/Deployer/src/actions/deploy.js index f04471b..dd5e301 100755 --- a/src/components/Deployer/src/actions/deploy.js +++ b/src/components/Deployer/src/actions/deploy.js @@ -42,13 +42,17 @@ module.exports = function (contract, args, deployer) { .then(function (instance) { const tronWrap = TronWrap(); if (should_deploy === true) { - deployer.logger.log( - contract.contract_name + - ':\n (base58) ' + - tronWrap.address.fromHex(instance.address) + - '\n (hex) ' + - instance.address - ); + if (tronWrap._web3) { + deployer.logger.log(contract.contract_name + ':\n (hex) ' + instance.address); + } else { + deployer.logger.log( + contract.contract_name + + ':\n (base58) ' + + tronWrap.address.fromHex(instance.address) + + '\n (hex) ' + + instance.address + ); + } } else { deployer.logger.log("Didn't deploy " + contract.contract_name + '; using ' + instance.address); } diff --git a/src/components/Migrate/index.js b/src/components/Migrate/index.js index 341f090..aef5736 100755 --- a/src/components/Migrate/index.js +++ b/src/components/Migrate/index.js @@ -28,6 +28,9 @@ Migration.prototype.run = function (options, callback) { const context = { tronWrap: tronWrap }; + if (tronWrap._web3) { + context.web3 = tronWrap._web3; + } const deployer = new Deployer({ options, @@ -71,7 +74,7 @@ Migration.prototype.run = function (options, callback) { .then(function () { if (options.save === false) return; logger.log('Saving artifacts...'); - return options.artifactor.saveAll(resolver.contracts()); + return options.artifactor.saveAll(resolver.contracts(), { evm: options.evm }); }) .then(function () { // Use process.nextTicK() to prevent errors thrown in the callback from triggering the below catch() diff --git a/src/components/TronSolc.js b/src/components/TronSolc.js index 22e755e..c504e19 100644 --- a/src/components/TronSolc.js +++ b/src/components/TronSolc.js @@ -66,7 +66,7 @@ function getWrapper(options = {}) { ); let compilerVersion = '0.5.4'; - const solcDir = path.join(homedir(), '.tronbox', 'solc'); + const solcDir = path.join(homedir(), '.tronbox', options.evm ? 'evm-solc' : 'solc'); if (options.networks) { if (options.networks.useZeroFourCompiler) { @@ -83,7 +83,7 @@ function getWrapper(options = {}) { version = options.compilers.solc.version; } - if (supportedVersions.includes(version)) { + if (supportedVersions.includes(version) || options.evm) { compilerVersion = version; } else { console.error(`Error: @@ -104,7 +104,7 @@ ${supportedVersions.join(' - ')} if (process.env.TRONBOX_NAME) { name = process.env.TRONBOX_NAME; } - const output = execSync(`${name} --download-compiler ${compilerVersion}`).toString(); + const output = execSync(`${name} --download-compiler ${compilerVersion} ${options.evm ? '--evm' : ''}`).toString(); if (output.indexOf('Permission required') !== -1) { console.error(` Error: Permissions required. @@ -112,11 +112,17 @@ Error: Permissions required. Most likely, you installed Node as root. Please, download the compiler manually, running: -tronbox --download-compiler ${compilerVersion} +tronbox --download-compiler ${compilerVersion} ${options.evm ? '--evm' : ''} `); // eslint-disable-next-line no-process-exit process.exit(); } + + if (output.indexOf('Error:') !== -1) { + console.error(output); + // eslint-disable-next-line no-process-exit + process.exit(); + } } const soljson = eval('require')(soljsonPath); return wrapper(soljson); diff --git a/src/components/TronWrap/constants.js b/src/components/TronWrap/constants.js index aeb305c..352da2f 100644 --- a/src/components/TronWrap/constants.js +++ b/src/components/TronWrap/constants.js @@ -6,6 +6,16 @@ module.exports = { callValue: 0, tokenValue: undefined, tokenId: undefined, - from: undefined + from: undefined, + + // evm + gasPrice: undefined, + gas: undefined, + gasLimit: undefined, + maxPriorityFeePerGas: undefined, + maxFeePerGas: undefined, + nonce: undefined, + type: undefined, + value: 0 } }; diff --git a/src/components/TronWrap/index.js b/src/components/TronWrap/index.js index 08ff2d9..2a2ea54 100644 --- a/src/components/TronWrap/index.js +++ b/src/components/TronWrap/index.js @@ -1,8 +1,10 @@ +const Web3 = require('web3'); const _TronWeb = require('tronweb'); const chalk = require('chalk'); const constants = require('./constants'); const axios = require('axios'); const ConsoleLogger = require('../ConsoleLogger'); +const reformat = require('./reformat'); let instance; @@ -58,7 +60,12 @@ function filterNetworkConfig(options) { callValue: options.callValue || options.call_value || constants.deployParameters.callValue, tokenValue: options.tokenValue || options.token_value || options.call_token_value, tokenId: options.tokenId || options.token_id, - userFeePercentage + userFeePercentage, + + gas: options.gas || options.gasLimit, + gasPrice: options.gasPrice, + maxPriorityFeePerGas: options.maxPriorityFeePerGas, + maxFeePerGas: options.maxFeePerGas }; } @@ -73,19 +80,20 @@ function init(options, extraOptions = {}) { !(options.privateKey || options.mnemonic) || !(options.fullHost || (options.fullNode && options.solidityNode && options.eventServer))) ) { + const configFile = extraOptions.evm ? 'tronbox-evm-config.js' : 'tronbox.js'; if (!options) { throw new Error( - 'It was not possible to instantiate TronWeb. The chosen network does not exist in your "tronbox.js".' + `It was not possible to instantiate TronWeb. The chosen network does not exist in your "${configFile}".` ); } else { if (!options.privateKey) { - throw new Error('It was not possible to instantiate TronWeb. Private key is missing in your "tronbox.js".'); + throw new Error(`It was not possible to instantiate TronWeb. Private key is missing in your "${configFile}".`); } if (!(options.fullHost || (options.fullNode && options.solidityNode && options.eventServer))) { - throw new Error('It was not possible to instantiate TronWeb. Fullhost url is missing in your "tronbox.js".'); + throw new Error(`It was not possible to instantiate TronWeb. Fullhost url is missing in your "${configFile}".`); } throw new Error( - 'It was not possible to instantiate TronWeb. Some required parameters are missing in your "tronbox.js".' + `It was not possible to instantiate TronWeb. Some required parameters are missing in your "${configFile}".` ); } } @@ -116,6 +124,12 @@ function init(options, extraOptions = {}) { if (extraOptions.log) { tronWrap._log = extraOptions.log; } + if (extraOptions.evm) { + const web3 = new Web3(options.fullNode || options.fullHost); + const account = web3.eth.accounts.wallet.add(getPrivateKey()); + tronWrap._web3 = web3; + tronWrap._web3_accounts = [account.address]; + } tronWrap._getNetworkInfo = async function () { const info = { @@ -140,6 +154,8 @@ function init(options, extraOptions = {}) { tronWrap._privateKeyByAccount[defaultAddress] = tronWrap.defaultPrivateKey; tronWrap._getAccounts = function (callback) { + if (extraOptions.evm) return tronWrap._evmGetAccounts(callback); + const self = this; return new Promise(accept => { @@ -191,6 +207,8 @@ function init(options, extraOptions = {}) { }; tronWrap._deployContract = function (option, callback) { + if (extraOptions.evm) return tronWrap._evmDeployContract(option, callback); + const myContract = this.contract(); const originEnergyLimit = option.originEnergyLimit || this.networkConfig.originEnergyLimit; if (originEnergyLimit < 0 || originEnergyLimit > constants.deployParameters.originEnergyLimit) { @@ -293,7 +311,7 @@ function init(options, extraOptions = {}) { myContract.loadAbi(JSON.parse(JSON.stringify(options.abi || []))); myContract.transactionHash = transaction.txID; - dlog('Contract deployed'); + dlog('Contract deployed:', options.name); return Promise.resolve(myContract); } catch (ex) { let e; @@ -314,6 +332,8 @@ function init(options, extraOptions = {}) { }; tronWrap.triggerContract = function (option, callback) { + if (extraOptions.evm) return tronWrap._evmTriggerContract(option, callback); + const myContract = this.contract(option.abi, option.address); let callSend = 'send'; // constructor and fallback option.abi.forEach(function (val) { @@ -497,6 +517,158 @@ function init(options, extraOptions = {}) { }); }; + tronWrap._evmGetAccounts = async function (callback) { + const accounts = [...tronWrap._web3_accounts]; + tronWrap._privateKeyByAccount[accounts[0]] = tronWrap._web3.eth.accounts.wallet[accounts[0]].privateKey; + if (callback) { + return callback(null, accounts); + } + return accounts; + }; + + tronWrap._evmDeployContract = async function (option, callback) { + const web3 = tronWrap._web3; + const contract = new web3.eth.Contract(option.abi); + const deployFunc = contract.deploy({ data: option.data, arguments: option.parameters }); + const opt = { + from: option.from || tronWrap._web3_accounts[0], + gas: option.gas || option.gasLimit || this.networkConfig.gas, + gasPrice: option.gasPrice || this.networkConfig.gasPrice, + maxPriorityFeePerGas: option.maxPriorityFeePerGas || this.networkConfig.maxPriorityFeePerGas, + maxFeePerGas: option.maxFeePerGas || this.networkConfig.maxFeePerGas, + value: option.value || option.callValue || option.call_value, + nonce: option.nonce, + type: option.type + }; + + if (opt.maxPriorityFeePerGas || opt.maxFeePerGas) { + delete opt.gasPrice; + } + + try { + if (!opt.gas) { + dlog('Estimate the gas used for deploying'); + opt.gas = await deployFunc.estimateGas(opt); + } + let transactionHash = null; + dlog('Deploying contract:', option.contractName); + const newContract = await deployFunc.send(opt, (err, hash) => { + transactionHash = hash; + }); + const { address } = newContract.options; + dlog('Contract broadcasted', { + address, + transactionHash + }); + + try { + const receipt = await tronWrap._evmWaitForTransaction(transactionHash); + if (!receipt || !receipt.status) { + return callback(new Error('Contract deployment failed')); + } + } catch (err) { + return callback(err); + } + + dlog('Contract deployed:', option.contractName); + callback(null, { + address, + transactionHash + }); + } catch (error) { + callback(error); + } + }; + + tronWrap._evmTriggerContract = async function (option, callback) { + const web3 = tronWrap._web3; + const contract = new web3.eth.Contract(option.abi, option.address); + const methodFunc = contract.methods[option.methodName](...option.args); + const { methodArgs } = option; + const opt = { + from: methodArgs.from || tronWrap._web3_accounts[0], + gas: methodArgs.gas || methodArgs.gasLimit || this.networkConfig.gas, + gasPrice: methodArgs.gasPrice || this.networkConfig.gasPrice, + maxPriorityFeePerGas: methodArgs.maxPriorityFeePerGas || this.networkConfig.maxPriorityFeePerGas, + maxFeePerGas: methodArgs.maxFeePerGas || this.networkConfig.maxFeePerGas, + value: methodArgs.value || methodArgs.callValue || methodArgs.call_value, + nonce: methodArgs.nonce, + type: methodArgs.type + }; + + if (opt.maxPriorityFeePerGas || opt.maxFeePerGas) { + delete opt.gasPrice; + } + + let callSend = 'send'; + let abiOutputs = []; + option.abi.forEach(function (val) { + if (val.name === option.methodName) { + callSend = /payable/.test(val.stateMutability) ? 'send' : 'call'; + abiOutputs = val.outputs; + } + }); + + try { + if (callSend === 'call') { + const callRes = await methodFunc.call(opt); + const result = reformat(callRes, abiOutputs); + return callback(null, result); + } + + if (!opt.gas) { + dlog('Estimate the gas used for sending transaction'); + opt.gas = await methodFunc.estimateGas(opt); + } + dlog('Sending transaction'); + const tx = await methodFunc.send(opt); + const { transactionHash } = tx; + dlog('Transaction sent'); + try { + const receipt = await tronWrap._evmWaitForTransaction(transactionHash); + if (!receipt || !receipt.status) { + return callback(new Error(`Transaction: ${transactionHash} exited with an error (status 0).`)); + } + } catch (err) { + return callback(err); + } + callback(null, transactionHash); + } catch (error) { + callback(error); + } + }; + + tronWrap._evmWaitForTransaction = async function (txHash) { + const web3 = tronWrap._web3; + const curBlockNumber = await web3.eth.getBlockNumber(); + const transactionBlockTimeout = 50; + const getReceipt = async () => { + let timeout = false; + try { + const blockNumber = await web3.eth.getBlockNumber(); + if (blockNumber - curBlockNumber > transactionBlockTimeout) { + timeout = true; + } else { + dlog('Requesting transaction', txHash); + const receipt = await web3.eth.getTransactionReceipt(txHash); + if (receipt) return receipt; + } + } catch (error) {} + + if (timeout) { + throw new Error( + `Transaction was not mined within ${transactionBlockTimeout} blocks, please make sure your transaction was properly sent. Be aware that it might still be mined!` + ); + } + + await sleep(1000); + return await getReceipt(); + }; + + dlog('Waiting for transaction'); + return await getReceipt(); + }; + return new TronWrap(); } diff --git a/src/components/TronWrap/reformat.js b/src/components/TronWrap/reformat.js new file mode 100644 index 0000000..ad3fff3 --- /dev/null +++ b/src/components/TronWrap/reformat.js @@ -0,0 +1,91 @@ +/** + * Utilities for reformatting web3 outputs + */ +const BigNumber = require('bignumber.js/bignumber'); +const web3Utils = require('web3-utils'); + +/** + * Converts from string to other number format + * @param {String} val number string returned by web3 + * @param {String} format name of format to convert to + * @return {Object|String} converted value + */ +const _convertNumber = function (val, format) { + const badFormatMsg = `Attempting to convert to unknown number format: ${format}`; + + switch (format) { + case 'BigNumber': + return new BigNumber(val); + case 'BN': + return web3Utils.toBN(val); + case 'String': + return val; + case 'BigInt': + return BigInt(val); + default: + throw new Error(badFormatMsg); + } +}; + +/** + * Converts arrays of number strings to other number formats + * @param {String[]} arr number string array returned by web3 + * @param {String} format name of format to convert to + * @return {Object[]|String[]} array of converted values + */ +const _convertNumberArray = function (arr, format, depth = 0) { + if (depth === 0) return arr.map(item => _convertNumber(item, format)); + // arr is nested + return arr.map(item => _convertNumberArray(item, format, depth - 1)); +}; + +/** + * Reformats numbers in the result/result-object of a web3 call. + * Possible forms of `result` are: + * - object (with index keys and optionally, named keys) + * - array + * - single primitive + * @param {String|Object|Array} result web3 call result + * @param {Array} abiSegment event params OR .call outputs + * @return {String|Object|Array} reformatted result + */ +const numbers = function (result, abiSegment, format = 'BN') { + abiSegment.forEach((output, i) => { + // output is a number type (uint || int); + if (output.type.includes('int')) { + // output is an array type + if (output.type.includes('[')) { + // larger than zero if nested array + const depth = output.type.split('[').length - 2; + + // result is array + if (Array.isArray(result)) { + result = _convertNumberArray(result, format, depth); + + // result is object + } else { + // output has name + if (output.name.length) { + result[output.name] = _convertNumberArray(result[output.name], format, depth); + } + // output will always have an index key + result[i] = _convertNumberArray(result[i], format, depth); + } + // + } else if (typeof result === 'object') { + // output has name + if (output.name.length) { + result[output.name] = _convertNumber(result[output.name], format); + } + + // output will always have an index key + result[i] = _convertNumber(result[i], format); + } else { + result = _convertNumber(result, format); + } + } + }); + return result; +}; + +module.exports = numbers; diff --git a/src/components/WorkflowCompile.js b/src/components/WorkflowCompile.js index 80d58d0..6883ff1 100755 --- a/src/components/WorkflowCompile.js +++ b/src/components/WorkflowCompile.js @@ -18,6 +18,7 @@ async function getCompilerVersion(options) { let tronWrap; try { tronWrap = TronWrap(config.networks[config.network], { + evm: options.evm, verify: true, log: options.log }); @@ -65,7 +66,7 @@ const Contracts = { let solcVersion = options.networks?.compilers ? options.networks?.compilers?.solc?.version : options.compilers?.solc?.version; - logger.log(` - solc: ${solcVersion}`); + logger.log(` - solc${options.evm ? '(EVM)' : ''}: ${solcVersion}`); }); } else { callback(null, [], paths); diff --git a/src/downloader.js b/src/downloader.js index e64f291..4ea26a8 100755 --- a/src/downloader.js +++ b/src/downloader.js @@ -1,31 +1,53 @@ +const chalk = require('chalk'); const path = require('path'); const fs = require('fs-extra'); const homedir = require('homedir'); const req = require('superagent'); -async function downloader(compilerVersion) { - const dir = path.join(homedir(), '.tronbox', 'solc'); +async function downloader(compilerVersion, evm) { + const dir = path.join(homedir(), '.tronbox', evm ? 'evm-solc' : 'solc'); const soljsonPath = path.join(dir, `soljson_v${compilerVersion}.js`); await fs.ensureDir(path.join(dir)); - const res = await req - .get(`https://tronsuper.github.io/tron-solc-bin/bin/soljson_v${compilerVersion}.js`) - .responseType('blob'); + let soljsonUrl = `https://tronsuper.github.io/tron-solc-bin/bin/soljson_v${compilerVersion}.js`; + if (evm) { + try { + const solidityUrl = 'https://binaries.soliditylang.org/bin'; + const list = await req.get(`${solidityUrl}/list.json`); + if (list && list.body) { + if (list.body.releases && list.body.releases[compilerVersion]) { + soljsonUrl = `${solidityUrl}/${list.body.releases[compilerVersion]}`; + } else { + console.info(chalk.red(chalk.bold('Error:'), 'Wrong Solidity compiler version.')); + // eslint-disable-next-line no-process-exit + process.exit(); + } + } + } catch (error) { + console.info(chalk.red(chalk.bold('Error:'), 'Failed to fetch compiler list.')); + // eslint-disable-next-line no-process-exit + process.exit(); + } + } + + try { + const res = await req.get(soljsonUrl).responseType('blob'); - if (res && res.body) { - await fs.writeFile(soljsonPath, res.body); - // double check - if (!fs.existsSync(soljsonPath)) { - console.error('Error. Permission required.'); - } else { - console.info('Compiler downloaded.'); + if (res && res.body) { + await fs.writeFile(soljsonPath, res.body); + // double check + if (!fs.existsSync(soljsonPath)) { + console.info(chalk.red(chalk.bold('Error:'), 'Permission required.')); + } else { + console.info('Compiler downloaded.'); + } } - } else { - console.error('Error. Wrong Solidity compiler version.'); + } catch (error) { + console.info(chalk.red(chalk.bold('Error:'), 'Wrong Solidity compiler version.')); + // eslint-disable-next-line no-process-exit + process.exit(); } - // eslint-disable-next-line no-process-exit - process.exit(); } module.exports = downloader; diff --git a/src/index.js b/src/index.js index 6a1fd61..46b9639 100644 --- a/src/index.js +++ b/src/index.js @@ -16,7 +16,7 @@ const options = { const commands = process.argv.slice(2); if (commands[0] === '--download-compiler' && commands[1]) { - downloader(commands[1]); + downloader(commands[1], commands[2]); } else { command.run(process.argv.slice(2), options, function (err) { if (err) { diff --git a/src/lib/commands/console.js b/src/lib/commands/console.js index 00f9163..52cd481 100755 --- a/src/lib/commands/console.js +++ b/src/lib/commands/console.js @@ -19,6 +19,7 @@ const command = { // init TronWeb try { TronWrap(config.networks[config.network], { + evm: options.evm, verify: true, log: options.log }); diff --git a/src/lib/commands/migrate.js b/src/lib/commands/migrate.js index 6201b9c..81b6278 100755 --- a/src/lib/commands/migrate.js +++ b/src/lib/commands/migrate.js @@ -45,6 +45,7 @@ const command = { // init TronWeb try { TronWrap(config.networks[config.network], { + evm: options.evm, verify: true, log: options.log }); diff --git a/src/lib/commands/test.js b/src/lib/commands/test.js index bf7e265..e090bf3 100755 --- a/src/lib/commands/test.js +++ b/src/lib/commands/test.js @@ -30,6 +30,7 @@ const command = { try { TronWrap(config.networks[config.network], { + evm: options.evm, verify: true, tre: true, log: options.log diff --git a/src/lib/console.js b/src/lib/console.js index 2590265..b655030 100755 --- a/src/lib/console.js +++ b/src/lib/console.js @@ -43,6 +43,7 @@ function Console(tasks, options) { try { this.tronWrap = TronWrap(options.networks[options.network], { + evm: options.evm, verify: true, log: options.log }); @@ -79,7 +80,8 @@ Console.prototype.start = function (callback) { self.repl.start({ prompt: 'tronbox(' + self.options.network + ')> ', context: { - tronWrap: self.tronWrap + tronWrap: self.tronWrap, + web3: self.tronWrap._web3 ? self.tronWrap._web3 : undefined }, interpreter: self.interpret.bind(self), done: callback diff --git a/src/lib/test.js b/src/lib/test.js index e9e9b23..7590ee9 100755 --- a/src/lib/test.js +++ b/src/lib/test.js @@ -231,6 +231,9 @@ const Test = { }; global.tronWrap = TronWrap(); + if (global.tronWrap._web3) { + global.web3 = global.tronWrap._web3; + } accept(); }); diff --git a/test/evm/.gitignore b/test/evm/.gitignore new file mode 100644 index 0000000..710eb82 --- /dev/null +++ b/test/evm/.gitignore @@ -0,0 +1,6 @@ + +src/js/metacoin-config.js +node_modules +build +.env + diff --git a/test/evm/contracts/ConvertLib.sol b/test/evm/contracts/ConvertLib.sol new file mode 100644 index 0000000..f85b7c4 --- /dev/null +++ b/test/evm/contracts/ConvertLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +library ConvertLib { + function convert(uint amount, uint conversionRate) public pure returns (uint convertedAmount) { + return amount * conversionRate; + } +} diff --git a/test/evm/contracts/MetaCoin.sol b/test/evm/contracts/MetaCoin.sol new file mode 100644 index 0000000..744d932 --- /dev/null +++ b/test/evm/contracts/MetaCoin.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +import './ConvertLib.sol'; + +// This is just a simple example of a coin-like contract. +// It is not standards compatible and cannot be expected to talk to other +// coin/token contracts. + +contract MetaCoin { + mapping(address => uint) balances; + + event Transfer(address _from, address _to, uint256 _value); + + address owner; + + constructor(uint initialBalance) { + owner = msg.sender; + balances[msg.sender] = initialBalance; + } + + function sendCoin(address receiver, uint amount) public returns (bool sufficient) { + if (balances[msg.sender] < amount) return false; + balances[msg.sender] -= amount; + balances[receiver] += amount; + emit Transfer(msg.sender, receiver, amount); + return true; + } + + function getConvertedBalance(address addr) public view returns (uint) { + return ConvertLib.convert(getBalance(addr), 2); + } + + function getBalance(address addr) public view returns (uint) { + return balances[addr]; + } + + function getOwner() public view returns (address) { + return owner; + } +} diff --git a/test/evm/contracts/Migrations.sol b/test/evm/contracts/Migrations.sol new file mode 100644 index 0000000..a5e6738 --- /dev/null +++ b/test/evm/contracts/Migrations.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +contract Migrations { + address public owner = msg.sender; + uint public last_completed_migration; + + modifier restricted() { + require(msg.sender == owner, "This function is restricted to the contract's owner"); + _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/test/evm/contracts/MyContract1.sol b/test/evm/contracts/MyContract1.sol new file mode 100644 index 0000000..37e4bb4 --- /dev/null +++ b/test/evm/contracts/MyContract1.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract MyContract1 { + uint256 public myNumber; + + constructor(uint256 _myNumber) { + myNumber = _myNumber; + } + + function setMyNumber(uint256 _myNumber) public { + myNumber = _myNumber; + } + + function getBalance(address _addr) public view returns (uint256 balance) { + balance = _addr.balance; + } + + function myAddress() public view returns (address addr) { + addr = address(this); + } + + function getSender() public view returns (address addr) { + addr = msg.sender; + } +} diff --git a/test/evm/contracts/MyContract2.sol b/test/evm/contracts/MyContract2.sol new file mode 100644 index 0000000..7578e70 --- /dev/null +++ b/test/evm/contracts/MyContract2.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract MyContract2 { + uint256 public myNumber; + + constructor(uint256 _myNumber) payable { + myNumber = _myNumber; + } + + function setMyNumber(uint256 _myNumber) public payable { + myNumber = _myNumber; + } + + function getBalance(address _addr) public view returns (uint256 balance) { + balance = _addr.balance; + } + + function myAddress() public view returns (address addr) { + addr = address(this); + } + + function getSender() public view returns (address addr) { + addr = msg.sender; + } +} diff --git a/test/evm/migrations/1_initial_migration.js b/test/evm/migrations/1_initial_migration.js new file mode 100644 index 0000000..a6aa34c --- /dev/null +++ b/test/evm/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require('./Migrations.sol'); + +module.exports = async function (deployer) { + await deployer.deploy(Migrations); +}; diff --git a/test/evm/migrations/2_deploy_contracts.js b/test/evm/migrations/2_deploy_contracts.js new file mode 100644 index 0000000..6984273 --- /dev/null +++ b/test/evm/migrations/2_deploy_contracts.js @@ -0,0 +1,13 @@ +const MyContract1 = artifacts.require('./MyContract1.sol'); +const MyContract2 = artifacts.require('./MyContract2.sol'); +const ConvertLib = artifacts.require('./ConvertLib.sol'); +const MetaCoin = artifacts.require('./MetaCoin.sol'); + +module.exports = async function (deployer) { + await deployer.deploy(MyContract1, 1); + await deployer.deploy(MyContract2, 2, { value: 1 }); + + await deployer.deploy(ConvertLib); + deployer.link(ConvertLib, MetaCoin); + await deployer.deploy(MetaCoin, 10000); +}; diff --git a/test/evm/test/.git-folder-keeper b/test/evm/test/.git-folder-keeper new file mode 100644 index 0000000..3ed31e1 --- /dev/null +++ b/test/evm/test/.git-folder-keeper @@ -0,0 +1 @@ +This is a placeholder file to ensure the parent directory in the git repository. Feel free to remove. diff --git a/test/evm/test/metacoin.js b/test/evm/test/metacoin.js new file mode 100644 index 0000000..175f75e --- /dev/null +++ b/test/evm/test/metacoin.js @@ -0,0 +1,48 @@ +const MetaCoin = artifacts.require('./MetaCoin.sol'); + +contract('MetaCoin', function (accounts) { + let meta; + + before(async function () { + meta = await MetaCoin.deployed(); + }); + + it('should verify that the contract has been deployed by accounts[0]', async function () { + assert.equal(await meta.getOwner(), web3.eth.accounts.wallet[0].address); + }); + + it('should put 10000 MetaCoin in the first account', async function () { + const balance = await meta.getBalance(accounts[0], { from: accounts[0] }); + assert.equal(balance, 10000, "10000 wasn't in the first account"); + }); + + it('should call a function that depends on a linked library', async function () { + this.timeout(10000); + const metaCoinBalance = (await meta.getBalance.call(accounts[0])).toNumber(); + const metaCoinConvertedBalance = (await meta.getConvertedBalance.call(accounts[0])).toNumber(); + assert.equal( + metaCoinConvertedBalance, + 2 * metaCoinBalance, + 'Library function returned unexpected function, linkage may be broken' + ); + }); + + it('should send coins from account 0 to 1', async function () { + const account1 = meta.address; + const account_one_starting_balance = (await meta.getBalance.call(accounts[0])).toNumber(); + const account_two_starting_balance = (await meta.getBalance.call(account1)).toNumber(); + await meta.sendCoin(account1, 10, { + from: accounts[0] + }); + assert.equal( + await meta.getBalance.call(accounts[0]), + account_one_starting_balance - 10, + "Amount wasn't correctly taken from the sender" + ); + assert.equal( + await meta.getBalance.call(account1), + account_two_starting_balance + 10, + "Amount wasn't correctly sent to the receiver" + ); + }); +}); diff --git a/test/evm/test/mycontract.js b/test/evm/test/mycontract.js new file mode 100644 index 0000000..483211c --- /dev/null +++ b/test/evm/test/mycontract.js @@ -0,0 +1,57 @@ +const MyContract1 = artifacts.require('./MyContract1.sol'); +const MyContract2 = artifacts.require('./MyContract2.sol'); + +contract('MyContract', function (accounts) { + let c1; + let c2; + + before(async function () { + c1 = await MyContract1.deployed(); + c2 = await MyContract2.deployed(); + }); + + it('should check the initial value of the contract', async function () { + assert.equal(await c1.myNumber(), 1, 'The value in MyContract1 should be 1'); + assert.equal(await c2.myNumber(), 2, 'The value in MyContract2 should be 2'); + }); + + it('should check the initial balance of the contract', async function () { + assert.equal(await c1.getBalance(c1.address), 0, 'The balance of MyContract1 should be 0'); + assert.equal(await c1.getBalance(c2.address), 1, 'The balance of MyContract2 should be 1'); + }); + + it('should set the value in the contract', async function () { + await c1.setMyNumber(100); + assert.equal(await c1.myNumber(), 100, 'The value in MyContract1 should be set to 100'); + + try { + await c1.setMyNumber(200, { value: 1 }); + } catch {} + assert.equal(await c1.myNumber(), 100, 'The value in MyContract1 should remain 100'); + assert.equal(await c1.getBalance(c1.address), 0, 'The balance of MyContract1 should be 0'); + + await c2.setMyNumber(200); + assert.equal(await c2.myNumber(), 200, 'The value in MyContract2 should be set to 200'); + await c2.setMyNumber(300, { value: 1 }); + assert.equal(await c2.myNumber(), 300, 'The value in MyContract2 should be set to 300'); + assert.equal(await c1.getBalance(c2.address), 2, 'The balance of MyContract2 should be 2'); + }); + + it('should set the from', async function () { + const sender1 = await c1.getSender(); + assert.equal(sender1, accounts[0], 'The sender should be accounts[0]'); + + const from = '0x1234567890123456789012345678901234567890'; + const sender2 = await c1.getSender({ from }); + assert.equal(sender2, from, 'The sender should be 0x1234567890123456789012345678901234567890'); + }); + + it('should return the contract address', async function () { + const c1Address = await c1.myAddress(); + assert.equal(c1Address.toLowerCase(), c1.address, 'The address should be the address of MyContract1'); + + const c2Ins = await c1.at(c2.address); + const c2Address = await c2Ins.myAddress(); + assert.equal(c2Address.toLowerCase(), c2.address, 'The address should be the address of MyContract2'); + }); +}); diff --git a/test/evm/tronbox-config.js b/test/evm/tronbox-config.js new file mode 100644 index 0000000..f4d6253 --- /dev/null +++ b/test/evm/tronbox-config.js @@ -0,0 +1,3 @@ +module.exports = { + +}; diff --git a/test/evm/tronbox-evm-config.js b/test/evm/tronbox-evm-config.js new file mode 100644 index 0000000..f6c9310 --- /dev/null +++ b/test/evm/tronbox-evm-config.js @@ -0,0 +1,40 @@ +module.exports = { + networks: { + bttc: { + // Don't put your private key here: + privateKey: process.env.PRIVATE_KEY_BTTC, + /* +Create a .env file (it must be gitignored) containing something like + + export PRIVATE_KEY_BTTC=4E7FEC...656243 + +Then, run the migration with: + + source .env && tronbox migrate --network bttc --evm + + */ + fullHost: 'https://rpc.bt.io', + gas: 8500000, // Gas sent with each transaction + gasPrice: '500000000000000', // 500,000 gwei (in wei) + network_id: '1' + }, + development: { + privateKey: process.env.PRIVATE_KEY_DEV, + fullHost: 'http://127.0.0.1:8545', + network_id: '9' + }, + compilers: { + solc: { + version: '0.8.6' + } + } + }, + // solc compiler optimize + solc: { + // optimizer: { + // enabled: true, + // runs: 200 + // }, + // evmVersion: 'istanbul' + } +}; diff --git a/test/evm/tronbox.js b/test/evm/tronbox.js new file mode 100644 index 0000000..41b0848 --- /dev/null +++ b/test/evm/tronbox.js @@ -0,0 +1,59 @@ +const port = process.env.HOST_PORT || 9090; + +module.exports = { + networks: { + mainnet: { + // Don't put your private key here: + privateKey: process.env.PRIVATE_KEY_MAINNET, + /* +Create a .env file (it must be gitignored) containing something like + + export PRIVATE_KEY_MAINNET=4E7FEC...656243 + +Then, run the migration with: + + source .env && tronbox migrate --network mainnet + + */ + userFeePercentage: 100, + feeLimit: 1000 * 1e6, + fullHost: 'https://api.trongrid.io', + network_id: '1' + }, + shasta: { + privateKey: process.env.PRIVATE_KEY, + userFeePercentage: 50, + feeLimit: 1000 * 1e6, + fullHost: 'https://api.shasta.trongrid.io', + network_id: '2' + }, + nile: { + privateKey: process.env.PRIVATE_KEY, + userFeePercentage: 100, + feeLimit: 1000 * 1e6, + fullHost: 'https://nile.trongrid.io', + network_id: '3' + }, + development: { + // For tronbox/tre docker image + privateKey: '0000000000000000000000000000000000000000000000000000000000000001', + userFeePercentage: 0, + feeLimit: 1000 * 1e6, + fullHost: 'http://127.0.0.1:' + port, + network_id: '9' + }, + compilers: { + solc: { + version: '0.8.6' + } + } + }, + // solc compiler optimize + solc: { + // optimizer: { + // enabled: true, + // runs: 200 + // }, + // evmVersion: 'istanbul' + } +}; diff --git a/test/runNile.sh b/test/runNile.sh index e9f61a3..eb5ae57 100755 --- a/test/runNile.sh +++ b/test/runNile.sh @@ -29,4 +29,8 @@ cd .. rm -rf build - +echo 'Test evm' +cd evm +rm -rf build +../../tronbox.dev migrate --network bttc --evm +cd .. diff --git a/test/runTest.sh b/test/runTest.sh index 77422d7..2b797f9 100755 --- a/test/runTest.sh +++ b/test/runTest.sh @@ -39,3 +39,9 @@ echo 'Test Console.log' cd consolelogs sh runTest.sh cd .. + +echo 'Test evm' +cd evm +rm -rf build +../../tronbox.dev test --evm +cd ..