From bef0e5950b6600d8cb64f15adc2fc5b8dcacfdf2 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:02:22 +0800 Subject: [PATCH 01/20] feat: (wip) e2e tests --- package-lock.json | 357 ++++++++++++++++++ package.json | 4 + src/__tests__/token-registry/accounts.ts | 27 ++ .../token-registry/deploy.e2e.test.ts | 60 +++ src/__tests__/token-registry/mint.e2e.test.ts | 26 ++ src/__tests__/token-registry/utils.ts | 106 ++++++ .../document-store/issue.test.ts | 2 - .../document-store/revoke.test.ts | 2 - .../document-store/transfer-ownership.test.ts | 2 - 9 files changed, 580 insertions(+), 6 deletions(-) create mode 100644 src/__tests__/token-registry/accounts.ts create mode 100644 src/__tests__/token-registry/deploy.e2e.test.ts create mode 100644 src/__tests__/token-registry/mint.e2e.test.ts create mode 100644 src/__tests__/token-registry/utils.ts diff --git a/package-lock.json b/package-lock.json index af0ad84d..14b27425 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3882,6 +3882,16 @@ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.7.tgz", "integrity": "sha512-4g1jrL98mdOIwSOUh6LTlB0Cs9I0dQPwINUhBg7C6pN4HLr8GS8xsksJxilW6S6dQHVi2K/o+lQuQcg7LroCnw==" }, + "@types/shelljs": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.11.tgz", + "integrity": "sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, "@types/signale": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@types/signale/-/signale-1.4.2.tgz", @@ -7938,6 +7948,327 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "ganache": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/ganache/-/ganache-7.5.0.tgz", + "integrity": "sha512-afNTJYBEaFrLPRrn7eUxH39TgnrffvHn/4T6THzQrc3rpfe4DOxw2nY2XEQxfsq1t4OqKSXtxomzyo26RZiOzw==", + "dev": true, + "requires": { + "@trufflesuite/bigint-buffer": "1.1.10", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "5.1.1", + "@types/seedrandom": "3.0.1", + "bufferutil": "4.0.5", + "emittery": "0.10.0", + "keccak": "3.0.2", + "leveldown": "6.1.0", + "secp256k1": "4.0.3", + "utf-8-validate": "5.0.7" + }, + "dependencies": { + "@trufflesuite/bigint-buffer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz", + "integrity": "sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw==", + "dev": true, + "requires": { + "node-gyp-build": "4.4.0" + }, + "dependencies": { + "node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true + } + } + }, + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "@types/node": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz", + "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==", + "dev": true + }, + "@types/seedrandom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.1.tgz", + "integrity": "sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "dev": true, + "optional": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "catering": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.0.tgz", + "integrity": "sha512-M5imwzQn6y+ODBfgi+cfgZv2hIUI6oYU/0f35Mdb1ujGeqeoI5tOnl9Q13DTH7LW+7er+NYq8stNOKZD/Z3U/A==", + "dev": true, + "requires": { + "queue-tick": "^1.0.0" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "emittery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.0.tgz", + "integrity": "sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "dev": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "leveldown": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-6.1.0.tgz", + "integrity": "sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w==", + "dev": true, + "requires": { + "abstract-leveldown": "^7.2.0", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + }, + "dependencies": { + "abstract-leveldown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", + "integrity": "sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ==", + "dev": true, + "requires": { + "buffer": "^6.0.3", + "catering": "^2.0.0", + "is-buffer": "^2.0.5", + "level-concat-iterator": "^3.0.0", + "level-supports": "^2.0.1", + "queue-microtask": "^1.2.3" + } + }, + "level-concat-iterator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz", + "integrity": "sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ==", + "dev": true, + "requires": { + "catering": "^2.1.0" + } + }, + "level-supports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-2.1.0.tgz", + "integrity": "sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA==", + "dev": true + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "queue-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.0.tgz", + "integrity": "sha512-ULWhjjE8BmiICGn3G8+1L9wFpERNxkf8ysxkAer4+TFdRefDaXOCV5m92aMB9FtBVmn/8sETXLXY6BfW7hyaWQ==", + "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "requires": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "dev": true, + "optional": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + } + } + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -8750,6 +9081,12 @@ } } }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, "into-stream": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", @@ -13416,6 +13753,15 @@ } } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, "redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -14216,6 +14562,17 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", diff --git a/package.json b/package.json index ffe2fda9..7d338dae 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", + "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", @@ -47,6 +48,7 @@ "@types/node": "^15.3.0", "@types/node-fetch": "^2.5.10", "@types/rimraf": "^3.0.0", + "@types/shelljs": "^0.8.11", "@types/signale": "^1.4.1", "@types/tmp": "^0.2.0", "@typescript-eslint/eslint-plugin": "^4.24.0", @@ -56,6 +58,7 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-jest": "^24.3.6", "eslint-plugin-prettier": "^3.4.0", + "ganache": "^7.4.3", "git-cz": "^4.7.6", "jest": "^26.1.0", "jest-watch-typeahead": "^0.6.3", @@ -64,6 +67,7 @@ "proxyquire": "^2.1.3", "rimraf": "^3.0.2", "semantic-release": "^17.4.3", + "shelljs": "^0.8.5", "tmp": "^0.2.1", "ts-jest": "^26.5.6", "ts-node": "^9.1.1", diff --git a/src/__tests__/token-registry/accounts.ts b/src/__tests__/token-registry/accounts.ts new file mode 100644 index 00000000..aa90ad06 --- /dev/null +++ b/src/__tests__/token-registry/accounts.ts @@ -0,0 +1,27 @@ +import { constants } from "@govtechsg/token-registry"; + +export const { contractAddress } = constants; + +export const network = "local"; +export const chainId = 1337 +export const forkedNetwork = 5 + +export const mnemonic = "indicate swing place chair flight used hammer soon photo region volume shuffle"; + +export const owner = { + ethAddress: "0xe0A71284EF59483795053266CB796B65E48B5124", + publicKey: "0x02de2454a05cdb55780b85c04128233e31ac9179235607e4d6fa0c6b38140fb51a", + privateKey: "0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7", +}; + +export const receiver = { + ethAddress: "0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60", + publicKey: "0x0396762cb3d373ddab0685bbd5e45ccaf7481d8deb5b75ab38704fba089abed629", + privateKey: "0xc58c1ff75001afdca8cecb61b47f36964febe4188b8f7b26252286ecae5a8879", +}; + +export const creators = { + titleEscrowFactory: contractAddress.TitleEscrowFactory[forkedNetwork], + deployer: contractAddress.Deployer[forkedNetwork], + tokenImplementation: contractAddress.TokenImplementation[forkedNetwork], +} diff --git a/src/__tests__/token-registry/deploy.e2e.test.ts b/src/__tests__/token-registry/deploy.e2e.test.ts new file mode 100644 index 00000000..aaca5d93 --- /dev/null +++ b/src/__tests__/token-registry/deploy.e2e.test.ts @@ -0,0 +1,60 @@ +import shell, { ShellString } from "shelljs"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; +import { isAddress } from "web3-utils"; +import { creators, network, owner } from "./accounts"; +import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "./utils"; + +const defaults = { + factoryAddress: creators.titleEscrowFactory, + tokenImplementationAddress: creators.tokenImplementation, + deployerAddress: creators.deployer, + network: network, + dryRun: false, +} + +describe("deploy token-registry", () => { + jest.setTimeout(90000); + + + it("should be able to deploy token-registry", async () => { + const tokenRegistryParameter: DeployTokenRegistryCommand = { + registryName: "Test Token", + registrySymbol: "TKN", + ...defaults + }; + + const command = generateDeployTokenRegistryCommand(tokenRegistryParameter, owner.privateKey); + const results: ShellString = shell.exec(command); + const tokenRegistrySuccessFormat = "✔ success Token registry deployed at "; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const splitResults = results.trim().split("\n"); + const tokenRegistryAddressLine = splitResults[splitResults.length - 2] + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) + expect(isAddress(tokenRegistryAddress)).toBe(true); + }); +}); + + +describe("deploy document-store", () => { + jest.setTimeout(90000); + + + it("should be able to deploy document-store", async () => { + + const documentStoreParameters: DeployDocumentStoreCommand = { + storeName: "Test Document Store", + ...defaults + }; + + const command = generateDeployDocumentStoreCommand(documentStoreParameters, owner.privateKey); + const results: ShellString = shell.exec(command); + const tokenRegistrySuccessFormat = "✔ success Document store Test Document Store deployed at "; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const splitResults = results.trim().split("\n"); + const tokenRegistryAddressLine = splitResults[splitResults.length - 2] + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) + expect(isAddress(tokenRegistryAddress)).toBe(true); + }); +}); diff --git a/src/__tests__/token-registry/mint.e2e.test.ts b/src/__tests__/token-registry/mint.e2e.test.ts new file mode 100644 index 00000000..28fed12d --- /dev/null +++ b/src/__tests__/token-registry/mint.e2e.test.ts @@ -0,0 +1,26 @@ +import shell, { ShellString } from "shelljs"; +import { deployTokenRegistry, generateTokenId } from "./utils"; +import { network, owner } from "./accounts"; +import { isAddress } from "web3-utils"; + +describe("deploy token-registry", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress: string = ''; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }) + + it("should be able to mint title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}` + const results: ShellString = shell.exec(command); + const tokenRegistrySuccessFormat = "✔ success Token with hash "; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const splitResults = results.trim().split("\n"); + const titleEscrowAddressLine = splitResults[splitResults.length - 2] + const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42) + expect(isAddress(titleEscrowAddress)).toBe(true); + }); +}); \ No newline at end of file diff --git a/src/__tests__/token-registry/utils.ts b/src/__tests__/token-registry/utils.ts new file mode 100644 index 00000000..7af4d5db --- /dev/null +++ b/src/__tests__/token-registry/utils.ts @@ -0,0 +1,106 @@ +import { isAddress } from "web3-utils"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; +import { creators, network } from "./accounts"; +import shell, { ShellString } from "shelljs"; +import { Wallet } from "ethers"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; + +const defaults = { + factoryAddress: creators.titleEscrowFactory, + tokenImplementationAddress: creators.tokenImplementation, + deployerAddress: creators.deployer, + network: network, + dryRun: false, +} + + +export const generateDeployTokenRegistryCommand = (tokenRegistryParameter: DeployTokenRegistryCommand, privateKey: string) => { + return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}` +} + +export const generateDeployDocumentStoreCommand = (documentStoreParameter: DeployDocumentStoreCommand, privateKey: string) => { + return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}` +} + +export const generateMintTitleEscrowCommand = (titleEscrowParameter: TokenRegistryIssueCommand, privateKey: string) => { + return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} --n ${titleEscrowParameter.network}` +} + + + +export const deployTokenRegistry = (privateKey: string, tokenRegistryParameters?: DeployTokenRegistryCommand): string => { + if (!tokenRegistryParameters) { + tokenRegistryParameters = { + registryName: "Test Token", + registrySymbol: "TKN", + ...defaults + }; + } + + const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); + const results: ShellString = shell.exec(command); + const tokenRegistrySuccessFormat = "✔ success Token registry deployed at "; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + if (!checkSuccess) throw new Error("Unable to deploy the token registry"); + const splitResults = results.trim().split("\n"); + const tokenRegistryAddressLine = splitResults[splitResults.length - 2] + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) + if (!isAddress(tokenRegistryAddress)) throw new Error("Unable to find token registry address"); + return tokenRegistryAddress; +} + +export const deployDocumentStore = (privateKey: string, documentStoreParameters?: DeployDocumentStoreCommand): string => { + if (!documentStoreParameters) { + documentStoreParameters = { + storeName: "Test Document Store", + ...defaults + }; + } + const command = generateDeployDocumentStoreCommand(documentStoreParameters, privateKey); + const results: ShellString = shell.exec(command); + const documentStoreSuccessFormat = "✔ success Document store Test Document Store deployed at "; + const checkSuccess = results.includes(documentStoreSuccessFormat); + if (!checkSuccess) throw new Error("Unable to deploy document store"); + const splitResults = results.trim().split("\n"); + const documentStoreAddressLine = splitResults[splitResults.length - 2] + const documentStoreAddress = documentStoreAddressLine.trim().substring(documentStoreSuccessFormat.length) + if (!isAddress(documentStoreAddress)) throw new Error("Unable to find document store address"); + return documentStoreAddress; +} + +const usedTokenIds = new Set(); +export const generateTokenId = () => { + for (let count = 0; count < 10; count = count + 1) { + const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')}`; + const unique = !usedTokenIds.has(generatedTokenId); + if(unique) { + usedTokenIds.add(generatedTokenId); + return generatedTokenId; + } + } + throw new Error("Unable to generate tokenIds") +} + +export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand) => { + if (!titleEscrowParameter) { + const wallet = new Wallet(privateKey); + titleEscrowParameter = { + address: deployTokenRegistry(privateKey), + beneficiary: wallet.address, + holder: wallet.address, + tokenId: generateTokenId(), + ...defaults, + } + } + + const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); + const results: ShellString = shell.exec(command); + const tokenRegistrySuccessFormat = "✔ success Token with hash "; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + if (!checkSuccess) throw new Error("Unable to mint token"); + const splitResults = results.trim().split("\n"); + const titleEscrowAddressLine = splitResults[splitResults.length - 2] + const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42) + if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); +} + diff --git a/src/implementations/document-store/issue.test.ts b/src/implementations/document-store/issue.test.ts index c2a83a6a..c2ab352a 100644 --- a/src/implementations/document-store/issue.test.ts +++ b/src/implementations/document-store/issue.test.ts @@ -14,8 +14,6 @@ const deployParams: DocumentStoreIssueCommand = { dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of DocumentStoreFactory -// ideally must setup ganache, and run the function over it describe("document-store", () => { // increase timeout because ethers is throttling jest.setTimeout(30000); diff --git a/src/implementations/document-store/revoke.test.ts b/src/implementations/document-store/revoke.test.ts index a3770605..4c3d8c97 100644 --- a/src/implementations/document-store/revoke.test.ts +++ b/src/implementations/document-store/revoke.test.ts @@ -15,8 +15,6 @@ const deployParams: DocumentStoreRevokeCommand = { dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of DocumentStoreFactory -// ideally must setup ganache, and run the function over it describe("document-store", () => { // increase timeout because ethers is throttling jest.setTimeout(30000); diff --git a/src/implementations/document-store/transfer-ownership.test.ts b/src/implementations/document-store/transfer-ownership.test.ts index c1c69395..e5e7ab94 100644 --- a/src/implementations/document-store/transfer-ownership.test.ts +++ b/src/implementations/document-store/transfer-ownership.test.ts @@ -15,8 +15,6 @@ const deployParams: DocumentStoreTransferOwnershipCommand = { dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of DocumentStoreFactory -// ideally must setup ganache, and run the function over it describe("document-store", () => { // increase timeout because ethers is throttling jest.setTimeout(30000); From 6bcaf9c892c9b595a6b08b7a1c0d2b89dff8d016 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Tue, 6 Dec 2022 14:11:05 +0800 Subject: [PATCH 02/20] fix: resolve ganache transaction fee spike --- package.json | 2 +- src/__tests__/fixture/e2e/commands.ts | 23 ++++ .../accounts.ts => fixture/e2e/constants.ts} | 17 ++- src/__tests__/fixture/e2e/shell.ts | 72 ++++++++++++ src/__tests__/fixture/e2e/utils.ts | 108 ++++++++++++++++++ .../token-registry/deploy.e2e.test.ts | 36 +++--- src/__tests__/token-registry/mint.e2e.test.ts | 22 ++-- src/__tests__/token-registry/utils.ts | 106 ----------------- 8 files changed, 242 insertions(+), 144 deletions(-) create mode 100644 src/__tests__/fixture/e2e/commands.ts rename src/__tests__/{token-registry/accounts.ts => fixture/e2e/constants.ts} (71%) create mode 100644 src/__tests__/fixture/e2e/shell.ts create mode 100644 src/__tests__/fixture/e2e/utils.ts delete mode 100644 src/__tests__/token-registry/utils.ts diff --git a/package.json b/package.json index 7d338dae..ec970697 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", - "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli", + "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli --miner.blockTime 1", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", diff --git a/src/__tests__/fixture/e2e/commands.ts b/src/__tests__/fixture/e2e/commands.ts new file mode 100644 index 00000000..150875e7 --- /dev/null +++ b/src/__tests__/fixture/e2e/commands.ts @@ -0,0 +1,23 @@ +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; + +export const generateDeployTokenRegistryCommand = ( + tokenRegistryParameter: DeployTokenRegistryCommand, + privateKey: string +): string => { + return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}`; +}; + +export const generateDeployDocumentStoreCommand = ( + documentStoreParameter: DeployDocumentStoreCommand, + privateKey: string +): string => { + return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}`; +}; + +export const generateMintTitleEscrowCommand = ( + titleEscrowParameter: TokenRegistryIssueCommand, + privateKey: string +): string => { + return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} --n ${titleEscrowParameter.network}`; +}; diff --git a/src/__tests__/token-registry/accounts.ts b/src/__tests__/fixture/e2e/constants.ts similarity index 71% rename from src/__tests__/token-registry/accounts.ts rename to src/__tests__/fixture/e2e/constants.ts index aa90ad06..64ebd81b 100644 --- a/src/__tests__/token-registry/accounts.ts +++ b/src/__tests__/fixture/e2e/constants.ts @@ -3,8 +3,8 @@ import { constants } from "@govtechsg/token-registry"; export const { contractAddress } = constants; export const network = "local"; -export const chainId = 1337 -export const forkedNetwork = 5 +export const chainId = 1337; +export const forkedNetwork = 5; export const mnemonic = "indicate swing place chair flight used hammer soon photo region volume shuffle"; @@ -21,7 +21,12 @@ export const receiver = { }; export const creators = { - titleEscrowFactory: contractAddress.TitleEscrowFactory[forkedNetwork], - deployer: contractAddress.Deployer[forkedNetwork], - tokenImplementation: contractAddress.TokenImplementation[forkedNetwork], -} + titleEscrowFactory: contractAddress.TitleEscrowFactory[forkedNetwork], + deployer: contractAddress.Deployer[forkedNetwork], + tokenImplementation: contractAddress.TokenImplementation[forkedNetwork], +}; + +export const emoji = { + tick: "✔", + cross: "✖", +}; diff --git a/src/__tests__/fixture/e2e/shell.ts b/src/__tests__/fixture/e2e/shell.ts new file mode 100644 index 00000000..f04badd9 --- /dev/null +++ b/src/__tests__/fixture/e2e/shell.ts @@ -0,0 +1,72 @@ +import shell, { ShellString } from "shelljs"; +import { log } from "signale"; +import { emoji } from "./constants"; +shell.config.silent = true; + +interface LineInfo { + lineNumber: number; + lineContent: string; +} + +const run = (command: string, silent = false): string => { + const rawResults: ShellString = shell.exec(command); + const results = stripAnsi(rawResults.trim()); + if (!silent) { + const success = `${emoji.tick} success`; + const failure = `${emoji.cross} error`; + let successLines = extractLine(results, success); + if (!successLines) successLines = []; + let failureLines = extractLine(results, failure); + if (!failureLines) failureLines = []; + const statusLines: LineInfo[] = [...successLines, ...failureLines]; + log(command); + printLines(statusLines); + } + return results; +}; + +const printLines = (lines: LineInfo[]): void => { + lines.sort((a: LineInfo, b: LineInfo): number => { + return a.lineNumber - b.lineNumber; + }); + for (const line of lines) { + log(`${line.lineNumber}: ${line.lineContent}`); + } +}; + +const extractLine = (result: string, query: string): LineInfo[] | void => { + const splitResults = result.trim().split("\n"); + const matchedLines = []; + for (let count = 0; count < splitResults.length; count++) { + const line = splitResults[count].trim(); + const containsQueryString = line.includes(query); + if (containsQueryString) { + matchedLines.push({ + lineNumber: count, + lineContent: line, + }); + } + } + if (matchedLines.length > 0) return matchedLines; + else return; +}; + +// https://github.com/chalk/strip-ansi/blob/main/index.js +export function stripAnsi(ansiString: string): string { + if (typeof ansiString !== "string") { + throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); + } + return ansiString.replace(ansiRegex(), ""); +} + +// https://github.com/chalk/ansi-regex/blob/main/index.js +export function ansiRegex({ onlyFirst = false } = {}): RegExp { + const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", + ].join("|"); + + return new RegExp(pattern, onlyFirst ? undefined : "g"); +} + +export { shell, ShellString, run }; diff --git a/src/__tests__/fixture/e2e/utils.ts b/src/__tests__/fixture/e2e/utils.ts new file mode 100644 index 00000000..7123663f --- /dev/null +++ b/src/__tests__/fixture/e2e/utils.ts @@ -0,0 +1,108 @@ +import { isAddress } from "web3-utils"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { creators, emoji, network } from "./constants"; +import { run } from "./shell"; +import { Wallet } from "ethers"; +import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; +import { + generateDeployDocumentStoreCommand, + generateDeployTokenRegistryCommand, + generateMintTitleEscrowCommand, +} from "./commands"; + +const defaults = { + factoryAddress: creators.titleEscrowFactory, + tokenImplementationAddress: creators.tokenImplementation, + deployerAddress: creators.deployer, + network: network, + dryRun: false, +}; + +const numberGenerator = (range: number): number => { + return Math.floor(Math.random() * range); +}; + +export const deployTokenRegistry = ( + privateKey: string, + tokenRegistryParameters?: DeployTokenRegistryCommand +): string => { + if (!tokenRegistryParameters) { + const index = numberGenerator(2); + tokenRegistryParameters = { + registryName: `Test Tokenx ${index}`, + registrySymbol: `TKNx${index}`, + ...defaults, + }; + } + + const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); + const results = run(command, true); + const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + if (!checkSuccess) throw new Error("Unable to deploy the token registry"); + const splitResults = results.trim().split("\n"); + const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); + if (!isAddress(tokenRegistryAddress)) throw new Error("Unable to find token registry address"); + return tokenRegistryAddress; +}; + +export const deployDocumentStore = ( + privateKey: string, + documentStoreParameters?: DeployDocumentStoreCommand +): string => { + if (!documentStoreParameters) { + const index = numberGenerator(2); + documentStoreParameters = { + storeName: `Test Document Store ${index}`, + ...defaults, + }; + } + const command = generateDeployDocumentStoreCommand(documentStoreParameters, privateKey); + const results = run(command, true); + const documentStoreSuccessFormat = `${emoji.tick} success Document store Test Document Store deployed at `; + const checkSuccess = results.includes(documentStoreSuccessFormat); + if (!checkSuccess) throw new Error("Unable to deploy document store"); + const splitResults = results.trim().split("\n"); + const documentStoreAddressLine = splitResults[splitResults.length - 2]; + const documentStoreAddress = documentStoreAddressLine.trim().substring(documentStoreSuccessFormat.length); + if (!isAddress(documentStoreAddress)) throw new Error("Unable to find document store address"); + return documentStoreAddress; +}; + +const usedTokenIds = new Set(); +export const generateTokenId = (): string => { + for (let count = 0; count < 10; count = count + 1) { + const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join("")}`; + const unique = !usedTokenIds.has(generatedTokenId); + if (unique) { + usedTokenIds.add(generatedTokenId); + return generatedTokenId; + } + } + throw new Error("Unable to generate tokenIds"); +}; + +export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand): string => { + if (!titleEscrowParameter) { + const wallet = new Wallet(privateKey); + titleEscrowParameter = { + address: deployTokenRegistry(privateKey), + beneficiary: wallet.address, + holder: wallet.address, + tokenId: generateTokenId(), + ...defaults, + }; + } + + const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); + const results = run(command, true); + const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; + const checkSuccess = results.includes(tokenRegistrySuccessFormat); + if (!checkSuccess) throw new Error("Unable to mint token"); + const splitResults = results.trim().split("\n"); + const titleEscrowAddressLine = splitResults[splitResults.length - 2]; + const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); + if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); + return titleEscrowAddress; +}; diff --git a/src/__tests__/token-registry/deploy.e2e.test.ts b/src/__tests__/token-registry/deploy.e2e.test.ts index aaca5d93..33f42ba0 100644 --- a/src/__tests__/token-registry/deploy.e2e.test.ts +++ b/src/__tests__/token-registry/deploy.e2e.test.ts @@ -1,8 +1,8 @@ -import shell, { ShellString } from "shelljs"; +import { run } from "../fixture/e2e/shell"; import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; import { isAddress } from "web3-utils"; -import { creators, network, owner } from "./accounts"; -import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "./utils"; +import { creators, emoji, network, owner } from "../fixture/e2e/constants"; +import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "../fixture/e2e/commands"; const defaults = { factoryAddress: creators.titleEscrowFactory, @@ -10,51 +10,47 @@ const defaults = { deployerAddress: creators.deployer, network: network, dryRun: false, -} +}; describe("deploy token-registry", () => { jest.setTimeout(90000); - it("should be able to deploy token-registry", async () => { const tokenRegistryParameter: DeployTokenRegistryCommand = { registryName: "Test Token", registrySymbol: "TKN", - ...defaults + ...defaults, }; const command = generateDeployTokenRegistryCommand(tokenRegistryParameter, owner.privateKey); - const results: ShellString = shell.exec(command); - const tokenRegistrySuccessFormat = "✔ success Token registry deployed at "; + const results = run(command); + const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); expect(checkSuccess).toBe(true); const splitResults = results.trim().split("\n"); - const tokenRegistryAddressLine = splitResults[splitResults.length - 2] - const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) + const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); expect(isAddress(tokenRegistryAddress)).toBe(true); }); }); - describe("deploy document-store", () => { jest.setTimeout(90000); - it("should be able to deploy document-store", async () => { - const documentStoreParameters: DeployDocumentStoreCommand = { storeName: "Test Document Store", - ...defaults - }; - + ...defaults, + }; + const command = generateDeployDocumentStoreCommand(documentStoreParameters, owner.privateKey); - const results: ShellString = shell.exec(command); - const tokenRegistrySuccessFormat = "✔ success Document store Test Document Store deployed at "; + const results = run(command); + const tokenRegistrySuccessFormat = `${emoji.tick} success Document store Test Document Store deployed at `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); expect(checkSuccess).toBe(true); const splitResults = results.trim().split("\n"); - const tokenRegistryAddressLine = splitResults[splitResults.length - 2] - const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) + const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; + const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); expect(isAddress(tokenRegistryAddress)).toBe(true); }); }); diff --git a/src/__tests__/token-registry/mint.e2e.test.ts b/src/__tests__/token-registry/mint.e2e.test.ts index 28fed12d..a6414fa8 100644 --- a/src/__tests__/token-registry/mint.e2e.test.ts +++ b/src/__tests__/token-registry/mint.e2e.test.ts @@ -1,26 +1,26 @@ -import shell, { ShellString } from "shelljs"; -import { deployTokenRegistry, generateTokenId } from "./utils"; -import { network, owner } from "./accounts"; +import { deployTokenRegistry, generateTokenId } from "../fixture/e2e/utils"; +import { run } from "../fixture/e2e/shell"; +import { emoji, network, owner } from "../fixture/e2e/constants"; import { isAddress } from "web3-utils"; describe("deploy token-registry", () => { jest.setTimeout(90000); - let tokenRegistryAddress: string = ''; + let tokenRegistryAddress = ""; beforeAll(() => { tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }) + }); it("should be able to mint title-escrow on token-registry", async () => { const tokenId = generateTokenId(); - const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}` - const results: ShellString = shell.exec(command); - const tokenRegistrySuccessFormat = "✔ success Token with hash "; + const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; + const results = run(command); + const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); expect(checkSuccess).toBe(true); const splitResults = results.trim().split("\n"); - const titleEscrowAddressLine = splitResults[splitResults.length - 2] - const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42) + const titleEscrowAddressLine = splitResults[splitResults.length - 2]; + const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); expect(isAddress(titleEscrowAddress)).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/src/__tests__/token-registry/utils.ts b/src/__tests__/token-registry/utils.ts deleted file mode 100644 index 7af4d5db..00000000 --- a/src/__tests__/token-registry/utils.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { isAddress } from "web3-utils"; -import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; -import { creators, network } from "./accounts"; -import shell, { ShellString } from "shelljs"; -import { Wallet } from "ethers"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; - -const defaults = { - factoryAddress: creators.titleEscrowFactory, - tokenImplementationAddress: creators.tokenImplementation, - deployerAddress: creators.deployer, - network: network, - dryRun: false, -} - - -export const generateDeployTokenRegistryCommand = (tokenRegistryParameter: DeployTokenRegistryCommand, privateKey: string) => { - return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}` -} - -export const generateDeployDocumentStoreCommand = (documentStoreParameter: DeployDocumentStoreCommand, privateKey: string) => { - return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}` -} - -export const generateMintTitleEscrowCommand = (titleEscrowParameter: TokenRegistryIssueCommand, privateKey: string) => { - return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} --n ${titleEscrowParameter.network}` -} - - - -export const deployTokenRegistry = (privateKey: string, tokenRegistryParameters?: DeployTokenRegistryCommand): string => { - if (!tokenRegistryParameters) { - tokenRegistryParameters = { - registryName: "Test Token", - registrySymbol: "TKN", - ...defaults - }; - } - - const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); - const results: ShellString = shell.exec(command); - const tokenRegistrySuccessFormat = "✔ success Token registry deployed at "; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - if (!checkSuccess) throw new Error("Unable to deploy the token registry"); - const splitResults = results.trim().split("\n"); - const tokenRegistryAddressLine = splitResults[splitResults.length - 2] - const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length) - if (!isAddress(tokenRegistryAddress)) throw new Error("Unable to find token registry address"); - return tokenRegistryAddress; -} - -export const deployDocumentStore = (privateKey: string, documentStoreParameters?: DeployDocumentStoreCommand): string => { - if (!documentStoreParameters) { - documentStoreParameters = { - storeName: "Test Document Store", - ...defaults - }; - } - const command = generateDeployDocumentStoreCommand(documentStoreParameters, privateKey); - const results: ShellString = shell.exec(command); - const documentStoreSuccessFormat = "✔ success Document store Test Document Store deployed at "; - const checkSuccess = results.includes(documentStoreSuccessFormat); - if (!checkSuccess) throw new Error("Unable to deploy document store"); - const splitResults = results.trim().split("\n"); - const documentStoreAddressLine = splitResults[splitResults.length - 2] - const documentStoreAddress = documentStoreAddressLine.trim().substring(documentStoreSuccessFormat.length) - if (!isAddress(documentStoreAddress)) throw new Error("Unable to find document store address"); - return documentStoreAddress; -} - -const usedTokenIds = new Set(); -export const generateTokenId = () => { - for (let count = 0; count < 10; count = count + 1) { - const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')}`; - const unique = !usedTokenIds.has(generatedTokenId); - if(unique) { - usedTokenIds.add(generatedTokenId); - return generatedTokenId; - } - } - throw new Error("Unable to generate tokenIds") -} - -export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand) => { - if (!titleEscrowParameter) { - const wallet = new Wallet(privateKey); - titleEscrowParameter = { - address: deployTokenRegistry(privateKey), - beneficiary: wallet.address, - holder: wallet.address, - tokenId: generateTokenId(), - ...defaults, - } - } - - const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); - const results: ShellString = shell.exec(command); - const tokenRegistrySuccessFormat = "✔ success Token with hash "; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - if (!checkSuccess) throw new Error("Unable to mint token"); - const splitResults = results.trim().split("\n"); - const titleEscrowAddressLine = splitResults[splitResults.length - 2] - const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42) - if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); -} - From fce3481875a7d9b2a499d40c496bc06f3171a946 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Tue, 6 Dec 2022 18:39:28 +0800 Subject: [PATCH 03/20] feat: most e2e working except surrender --- package.json | 2 +- src/__tests__/fixture/e2e/commands.ts | 50 +++++++-- src/__tests__/fixture/e2e/constants.ts | 3 + src/__tests__/fixture/e2e/shell.ts | 102 +++++++++--------- src/__tests__/fixture/e2e/utils.ts | 61 +++++++++-- .../token-registry/change-holder.e2e.test.ts | 58 ++++++++++ .../endorse-change-owner.e2e.test.ts | 71 ++++++++++++ .../endorse-transfer.e2e.test.ts | 60 +++++++++++ src/__tests__/token-registry/mint.e2e.test.ts | 15 ++- .../token-registry/nominate.e2e.test.ts | 59 ++++++++++ .../token-registry/surrender.e2e.test.ts | 56 ++++++++++ .../title-escrow/surrender-document.ts | 1 + 12 files changed, 467 insertions(+), 71 deletions(-) create mode 100644 src/__tests__/token-registry/change-holder.e2e.test.ts create mode 100644 src/__tests__/token-registry/endorse-change-owner.e2e.test.ts create mode 100644 src/__tests__/token-registry/endorse-transfer.e2e.test.ts create mode 100644 src/__tests__/token-registry/nominate.e2e.test.ts create mode 100644 src/__tests__/token-registry/surrender.e2e.test.ts diff --git a/package.json b/package.json index ec970697..5f36ab88 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", - "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli --miner.blockTime 1", + "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli --miner.blockTime 12", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", diff --git a/src/__tests__/fixture/e2e/commands.ts b/src/__tests__/fixture/e2e/commands.ts index 150875e7..0910cee4 100644 --- a/src/__tests__/fixture/e2e/commands.ts +++ b/src/__tests__/fixture/e2e/commands.ts @@ -1,23 +1,55 @@ import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { TitleEscrowEndorseTransferOfOwnersCommand, TitleEscrowNominateBeneficiaryCommand, TitleEscrowtransferOwnerCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; export const generateDeployTokenRegistryCommand = ( - tokenRegistryParameter: DeployTokenRegistryCommand, - privateKey: string + tokenRegistryParameter: DeployTokenRegistryCommand, + privateKey: string ): string => { - return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}`; + return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}`; }; export const generateDeployDocumentStoreCommand = ( - documentStoreParameter: DeployDocumentStoreCommand, - privateKey: string + documentStoreParameter: DeployDocumentStoreCommand, + privateKey: string ): string => { - return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}`; + return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}`; }; export const generateMintTitleEscrowCommand = ( - titleEscrowParameter: TokenRegistryIssueCommand, - privateKey: string + titleEscrowParameter: TokenRegistryIssueCommand, + privateKey: string ): string => { - return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} --n ${titleEscrowParameter.network}`; + return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} -n ${titleEscrowParameter.network}`; }; + +export const generateChangeHolderCommand = ( + changeHolderParameter: TitleEscrowtransferOwnerCommand, + privateKey: string +): string => { + return `npm run dev -- title-escrow change-holder --token-registry ${changeHolderParameter.tokenRegistry} --tokenId ${changeHolderParameter.tokenId} --newHolder ${changeHolderParameter.newHolder} -k ${privateKey} --network ${changeHolderParameter.network}`; +}; +export const generateTransferOwnersCommand = ( + transferOwners: TitleEscrowEndorseTransferOfOwnersCommand, + privateKey: string +): string => { + return `npm run dev -- title-escrow endorse-change-owner --token-registry ${transferOwners.tokenRegistry} --tokenId ${transferOwners.tokenId} --newOwner ${transferOwners.newOwner} --newHolder ${transferOwners.newHolder} -k ${privateKey} --network ${transferOwners.network}`; +} + +export const generateEndorseTransferOwnerCommand = ( + transferOwner: TitleEscrowNominateBeneficiaryCommand, + privateKey: string +): string => { +// const command = `npm run dev -- title-escrow endorse-transfer-owner --token-registry ${transferHolder.tokenRegistry} --tokenId ${transferHolder.tokenId} --newBeneficiary ${transferHolder.newBeneficiary} -k ${owner.privateKey} --network ${transferHolder.network}`; + return `npm run dev -- title-escrow endorse-transfer-owner --token-registry ${transferOwner.tokenRegistry} --tokenId ${transferOwner.tokenId} --newBeneficiary ${transferOwner.newBeneficiary} -k ${privateKey} --network ${transferOwner.network}`; +} + + +export const generateNominateCommand = ( + nominateParameter: TitleEscrowNominateBeneficiaryCommand, + privateKey: string +): string => { +// const command = `npm run dev -- title-escrow nominate-change-owner --token-registry ${transferHolder.tokenRegistry} --tokenId ${transferHolder.tokenId} --newOwner ${transferHolder.newBeneficiary} -k ${owner.privateKey} --network ${transferHolder.network}`; + return `npm run dev -- title-escrow nominate-change-owner --token-registry ${nominateParameter.tokenRegistry} --tokenId ${nominateParameter.tokenId} --newOwner ${nominateParameter.newBeneficiary} -k ${privateKey} --network ${nominateParameter.network}`; +} + diff --git a/src/__tests__/fixture/e2e/constants.ts b/src/__tests__/fixture/e2e/constants.ts index 64ebd81b..5c3d3fef 100644 --- a/src/__tests__/fixture/e2e/constants.ts +++ b/src/__tests__/fixture/e2e/constants.ts @@ -30,3 +30,6 @@ export const emoji = { tick: "✔", cross: "✖", }; + +export const silent = true; +export const verbose = true; diff --git a/src/__tests__/fixture/e2e/shell.ts b/src/__tests__/fixture/e2e/shell.ts index f04badd9..15c0e80f 100644 --- a/src/__tests__/fixture/e2e/shell.ts +++ b/src/__tests__/fixture/e2e/shell.ts @@ -1,72 +1,74 @@ import shell, { ShellString } from "shelljs"; import { log } from "signale"; -import { emoji } from "./constants"; +import { emoji, verbose } from "./constants"; shell.config.silent = true; -interface LineInfo { - lineNumber: number; - lineContent: string; +export interface LineInfo { + lineNumber: number; + lineContent: string; } -const run = (command: string, silent = false): string => { - const rawResults: ShellString = shell.exec(command); - const results = stripAnsi(rawResults.trim()); - if (!silent) { - const success = `${emoji.tick} success`; - const failure = `${emoji.cross} error`; - let successLines = extractLine(results, success); - if (!successLines) successLines = []; - let failureLines = extractLine(results, failure); - if (!failureLines) failureLines = []; - const statusLines: LineInfo[] = [...successLines, ...failureLines]; - log(command); - printLines(statusLines); - } - return results; +export const run = (command: string, silent = false): string => { + shell.config.silent = !(!silent && verbose); + const rawResults: ShellString = shell.exec(command); + const results = stripAnsi(rawResults.trim()); + if (!silent) { + const success = `${emoji.tick} success`; + const failure = `${emoji.cross} error`; + let successLines = extractLine(results, success); + if (!successLines) successLines = []; + let failureLines = extractLine(results, failure); + if (!failureLines) failureLines = []; + const statusLines: LineInfo[] = [...successLines, ...failureLines]; + log(command); + printLines(statusLines); + if(verbose && failureLines.length > 0) log(rawResults) + } + return results; }; -const printLines = (lines: LineInfo[]): void => { - lines.sort((a: LineInfo, b: LineInfo): number => { - return a.lineNumber - b.lineNumber; - }); - for (const line of lines) { - log(`${line.lineNumber}: ${line.lineContent}`); - } +export const printLines = (lines: LineInfo[]): void => { + lines.sort((a: LineInfo, b: LineInfo): number => { + return a.lineNumber - b.lineNumber; + }); + for (const line of lines) { + log(`${line.lineNumber}: ${line.lineContent}`); + } }; -const extractLine = (result: string, query: string): LineInfo[] | void => { - const splitResults = result.trim().split("\n"); - const matchedLines = []; - for (let count = 0; count < splitResults.length; count++) { - const line = splitResults[count].trim(); - const containsQueryString = line.includes(query); - if (containsQueryString) { - matchedLines.push({ - lineNumber: count, - lineContent: line, - }); +export const extractLine = (result: string, query: string): LineInfo[] | void => { + const splitResults = result.trim().split("\n"); + const matchedLines = []; + for (let count = 0; count < splitResults.length; count++) { + const line = splitResults[count].trim(); + const containsQueryString = line.includes(query); + if (containsQueryString) { + matchedLines.push({ + lineNumber: count, + lineContent: line, + }); + } } - } - if (matchedLines.length > 0) return matchedLines; - else return; + if (matchedLines.length > 0) return matchedLines; + else return; }; // https://github.com/chalk/strip-ansi/blob/main/index.js export function stripAnsi(ansiString: string): string { - if (typeof ansiString !== "string") { - throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); - } - return ansiString.replace(ansiRegex(), ""); + if (typeof ansiString !== "string") { + throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); + } + return ansiString.replace(ansiRegex(), ""); } // https://github.com/chalk/ansi-regex/blob/main/index.js export function ansiRegex({ onlyFirst = false } = {}): RegExp { - const pattern = [ - "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", - "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", - ].join("|"); + const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", + ].join("|"); - return new RegExp(pattern, onlyFirst ? undefined : "g"); + return new RegExp(pattern, onlyFirst ? undefined : "g"); } -export { shell, ShellString, run }; +export { shell, ShellString }; diff --git a/src/__tests__/fixture/e2e/utils.ts b/src/__tests__/fixture/e2e/utils.ts index 7123663f..95885205 100644 --- a/src/__tests__/fixture/e2e/utils.ts +++ b/src/__tests__/fixture/e2e/utils.ts @@ -1,19 +1,18 @@ import { isAddress } from "web3-utils"; import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; import { creators, emoji, network } from "./constants"; -import { run } from "./shell"; +import { extractLine, LineInfo, run } from "./shell"; import { Wallet } from "ethers"; import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand, generateMintTitleEscrowCommand, + generateNominateCommand, } from "./commands"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; const defaults = { - factoryAddress: creators.titleEscrowFactory, - tokenImplementationAddress: creators.tokenImplementation, - deployerAddress: creators.deployer, network: network, dryRun: false, }; @@ -27,14 +26,16 @@ export const deployTokenRegistry = ( tokenRegistryParameters?: DeployTokenRegistryCommand ): string => { if (!tokenRegistryParameters) { - const index = numberGenerator(2); + const index = numberGenerator(100); tokenRegistryParameters = { registryName: `Test Tokenx ${index}`, registrySymbol: `TKNx${index}`, + factoryAddress: creators.titleEscrowFactory, + tokenImplementationAddress: creators.tokenImplementation, + deployerAddress: creators.deployer, ...defaults, }; } - const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); const results = run(command, true); const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; @@ -52,7 +53,7 @@ export const deployDocumentStore = ( documentStoreParameters?: DeployDocumentStoreCommand ): string => { if (!documentStoreParameters) { - const index = numberGenerator(2); + const index = numberGenerator(100); documentStoreParameters = { storeName: `Test Document Store ${index}`, ...defaults, @@ -83,7 +84,13 @@ export const generateTokenId = (): string => { throw new Error("Unable to generate tokenIds"); }; -export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand): string => { +export interface TokenInfo { + tokenRegistry: string, + tokenId: string, + titleEscrowAddress?: string, +} + +export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand): TokenInfo => { if (!titleEscrowParameter) { const wallet = new Wallet(privateKey); titleEscrowParameter = { @@ -104,5 +111,41 @@ export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegist const titleEscrowAddressLine = splitResults[splitResults.length - 2]; const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); - return titleEscrowAddress; + return { + tokenRegistry: titleEscrowParameter.address, + tokenId: titleEscrowParameter.tokenId, + titleEscrowAddress + }; +}; + +export const mintNominatedToken = (privateKey: string, nominee: string) => { + const { tokenRegistry, tokenId } = mintToken(privateKey); + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newBeneficiary: nominee, + ...defaults + } + nominateToken(privateKey, nominateParameter); + return { tokenRegistry, tokenId }; +} + +export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand) => { + const command = generateNominateCommand(nominateParameter, privateKey); + const results = run(command, true); + const frontFormat = `${emoji.tick} success Transferable record with hash `; + const middleFormat = `'s holder has been successfully nominated to new owner with address ` + const queryResults = extractLine(results, frontFormat); + if (!queryResults) throw new Error("Unable to nominate token"); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(frontFormat); + const checkContext = filteredLine.includes(middleFormat); + expect(checkSuccess && checkContext).toBe(true); + if (!checkSuccess || !checkContext) throw new Error("Unexpected nominate token format"); + const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); + expect(resultTokenId).toBe(nominateParameter.tokenId); + if (resultTokenId !== nominateParameter.tokenId) throw new Error("Unexpected nominate tokenid"); + const destination = filteredLine.trim().substring(filteredLine.length - 42); + if (destination !== nominateParameter.newBeneficiary) throw new Error("Unexpected nominee"); }; + diff --git a/src/__tests__/token-registry/change-holder.e2e.test.ts b/src/__tests__/token-registry/change-holder.e2e.test.ts new file mode 100644 index 00000000..22790378 --- /dev/null +++ b/src/__tests__/token-registry/change-holder.e2e.test.ts @@ -0,0 +1,58 @@ +import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { generateChangeHolderCommand } from "../fixture/e2e/commands"; + +describe("transfer holder title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + + const defaultTransferHolder = { + newHolder: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to transfer holder title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrow: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + ...defaultTitleEscrow, + } + mintToken(owner.privateKey, titleEscrow); + const transferHolder: TitleEscrowTransferHolderCommand = { + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + ...defaultTransferHolder, + } + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + const frontFormat = `${emoji.tick} success Transferable record with hash `; + const middleFormat = `'s holder has been successfully changed to holder with address: ` + const queryResults = extractLine(results, frontFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(frontFormat); + const checkContext = filteredLine.includes(middleFormat); + expect(checkSuccess && checkContext).toBe(true); + const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); + expect(resultTokenId).toBe(transferHolder.tokenId); + const destination = filteredLine.trim().substring(filteredLine.length - 42); + expect(destination).toBe(transferHolder.newHolder); + }); +}); diff --git a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts new file mode 100644 index 00000000..2a16fe13 --- /dev/null +++ b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts @@ -0,0 +1,71 @@ + +import { deployTokenRegistry, generateTokenId, mintToken, nominateToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { TitleEscrowEndorseTransferOfOwnersCommand, TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { generateTransferOwnersCommand } from "../fixture/e2e/commands"; + +describe("nominate title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + + const defaultTransferOwners = { + newOwner: receiver.ethAddress, + newHolder: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to nominate title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrow: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + ...defaultTitleEscrow, + } + mintToken(owner.privateKey, titleEscrow); + const nominateOnwer: TitleEscrowNominateBeneficiaryCommand = { + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + newBeneficiary: defaultTransferOwners.newOwner, + ...defaultTransferOwners, + } + const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + ...defaultTransferOwners, + } + nominateToken(owner.privateKey, nominateOnwer) + const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); + const results = run(command); + const frontFormat = `${emoji.tick} success Transferable record with hash `; + const middleFormat = `'s holder has been successfully endorsed to new owner with address ` + const rearFormat = ` and new holder with address: ` + const queryResults = extractLine(results, frontFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(frontFormat); + const checkOwner = filteredLine.includes(middleFormat); + const checkHolder = filteredLine.includes(rearFormat); + expect(checkSuccess && checkOwner && checkHolder).toBe(true); + const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); + expect(resultTokenId).toBe(transferOwners.tokenId); + const resultOwner = filteredLine.trim().substring(frontFormat.length + 66 + middleFormat.length, frontFormat.length + 66 + middleFormat.length + 42); + expect(resultOwner).toBe(transferOwners.newOwner); + const destination = filteredLine.trim().substring(filteredLine.length - 42); + expect(destination).toBe(transferOwners.newHolder); + }); +}); diff --git a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts new file mode 100644 index 00000000..a68d7bec --- /dev/null +++ b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts @@ -0,0 +1,60 @@ +import { deployTokenRegistry, generateTokenId, mintToken, nominateToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { generateEndorseTransferOwnerCommand } from "../fixture/e2e/commands"; + +describe("nominate title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + + const defaultTransferHolder = { + newBeneficiary: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to nominate title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrow: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + ...defaultTitleEscrow, + } + mintToken(owner.privateKey, titleEscrow); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + ...defaultTransferHolder, + } + nominateToken(owner.privateKey, transferHolder) + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey) + const results = run(command); + const frontFormat = `${emoji.tick} success Transferable record with hash `; + const middleFormat = `'s holder has been successfully endorsed to approved beneficiary at ` + const queryResults = extractLine(results, frontFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(frontFormat); + const checkContext = filteredLine.includes(middleFormat); + expect(checkSuccess && checkContext).toBe(true); + const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); + expect(resultTokenId).toBe(transferHolder.tokenId); + const destination = filteredLine.trim().substring(filteredLine.length - 42); + expect(destination).toBe(transferHolder.newBeneficiary); + }); +}); diff --git a/src/__tests__/token-registry/mint.e2e.test.ts b/src/__tests__/token-registry/mint.e2e.test.ts index a6414fa8..ff3d494f 100644 --- a/src/__tests__/token-registry/mint.e2e.test.ts +++ b/src/__tests__/token-registry/mint.e2e.test.ts @@ -1,7 +1,9 @@ import { deployTokenRegistry, generateTokenId } from "../fixture/e2e/utils"; import { run } from "../fixture/e2e/shell"; -import { emoji, network, owner } from "../fixture/e2e/constants"; +import { emoji, network, owner, silent } from "../fixture/e2e/constants"; import { isAddress } from "web3-utils"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { generateMintTitleEscrowCommand } from "../fixture/e2e/commands"; describe("deploy token-registry", () => { jest.setTimeout(90000); @@ -13,7 +15,16 @@ describe("deploy token-registry", () => { it("should be able to mint title-escrow on token-registry", async () => { const tokenId = generateTokenId(); - const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + } + // const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); diff --git a/src/__tests__/token-registry/nominate.e2e.test.ts b/src/__tests__/token-registry/nominate.e2e.test.ts new file mode 100644 index 00000000..9e9d3de1 --- /dev/null +++ b/src/__tests__/token-registry/nominate.e2e.test.ts @@ -0,0 +1,59 @@ +// title-escrow nominate-change-owner --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 --newOwner 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 + +import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { generateNominateCommand } from "../fixture/e2e/commands"; + +describe("nominate title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + const defaultTransferHolder = { + newBeneficiary: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to nominate title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrow: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + ...defaultTitleEscrow, + } + mintToken(owner.privateKey, titleEscrow); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + ...defaultTransferHolder, + } + const command = generateNominateCommand(transferHolder, owner.privateKey); + const results = run(command); + const frontFormat = `${emoji.tick} success Transferable record with hash `; + const middleFormat = `'s holder has been successfully nominated to new owner with address ` + const queryResults = extractLine(results, frontFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(frontFormat); + const checkContext = filteredLine.includes(middleFormat); + expect(checkSuccess && checkContext).toBe(true); + const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); + expect(resultTokenId).toBe(transferHolder.tokenId); + const destination = filteredLine.trim().substring(filteredLine.length - 42); + expect(destination).toBe(transferHolder.newBeneficiary); + }); +}); diff --git a/src/__tests__/token-registry/surrender.e2e.test.ts b/src/__tests__/token-registry/surrender.e2e.test.ts new file mode 100644 index 00000000..142d14ba --- /dev/null +++ b/src/__tests__/token-registry/surrender.e2e.test.ts @@ -0,0 +1,56 @@ +import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; +import { run } from "../fixture/e2e/shell"; +import { emoji, network, owner } from "../fixture/e2e/constants"; +import { isAddress } from "web3-utils"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; +import { defaultSeverityColor } from "snyk/dist/lib/snyk-test/common"; + +describe("surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + it.todo("surrender is not working ????"); + it("should be able to surrender title-escrow on token-registry", async () => { + expect(true).toBe(false); + // const tokenId = generateTokenId(); + // const surrenderTitleEscrow: TokenRegistryIssueCommand = { + // address: tokenRegistryAddress, + // tokenId: tokenId, + // ...defaultTitleEscrow, + // } + // const titleEscrowAddress = mintToken(owner.privateKey, surrenderTitleEscrow); + // console.log(titleEscrowAddress); + // const command = `npm run dev -- title-escrow surrender --token-registry ${surrenderTitleEscrow.address} --tokenId ${surrenderTitleEscrow.tokenId} -k ${owner.privateKey} --network ${surrenderTitleEscrow.network}`; + + // const results = run(command); + // const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; + // const checkSuccess = results.includes(tokenRegistrySuccessFormat); + // expect(checkSuccess).toBe(true); + // const splitResults = results.trim().split("\n"); + // const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; + // const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); + // expect(isAddress(tokenRegistryAddress)).toBe(true); + + // const tokenId = generateTokenId(); + // const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; + // const results = run(command); + // const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; + // const checkSuccess = results.includes(tokenRegistrySuccessFormat); + // expect(checkSuccess).toBe(true); + // const splitResults = results.trim().split("\n"); + // const titleEscrowAddressLine = splitResults[splitResults.length - 2]; + // const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); + // expect(isAddress(titleEscrowAddress)).toBe(true); + }); +}); diff --git a/src/commands/title-escrow/surrender-document.ts b/src/commands/title-escrow/surrender-document.ts index e418f635..275408ec 100644 --- a/src/commands/title-escrow/surrender-document.ts +++ b/src/commands/title-escrow/surrender-document.ts @@ -38,6 +38,7 @@ export const handler = async (args: TitleEscrowSurrenderDocumentCommand): Promis success(`Transferable record with hash ${args.tokenId} has been surrendered.`); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transaction.transactionHash}`); } catch (e) { + console.log(e) error(getErrorMessage(e)); } }; From a8d573a0441ef32d3c6ba701e43979041cbe64ec Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 7 Dec 2022 17:11:31 +0800 Subject: [PATCH 04/20] feat: surrender complete --- src/__tests__/fixture/e2e/commands.ts | 76 ++++++++------ src/__tests__/fixture/e2e/shell.ts | 92 ++++++++--------- src/__tests__/fixture/e2e/utils.ts | 99 +++++++++++++------ .../accept-surrender.e2e.test.ts | 39 ++++++++ .../token-registry/change-holder.e2e.test.ts | 34 +++---- .../endorse-change-owner.e2e.test.ts | 54 +++++----- .../endorse-transfer.e2e.test.ts | 45 ++++----- src/__tests__/token-registry/mint.e2e.test.ts | 5 +- .../token-registry/nominate.e2e.test.ts | 20 ++-- .../reject-surrender.e2e.test.ts | 39 ++++++++ .../token-registry/surrender.e2e.test.ts | 56 +++++------ .../title-escrow/surrender-document.ts | 1 - 12 files changed, 329 insertions(+), 231 deletions(-) create mode 100644 src/__tests__/token-registry/accept-surrender.e2e.test.ts create mode 100644 src/__tests__/token-registry/reject-surrender.e2e.test.ts diff --git a/src/__tests__/fixture/e2e/commands.ts b/src/__tests__/fixture/e2e/commands.ts index 0910cee4..c7f4c273 100644 --- a/src/__tests__/fixture/e2e/commands.ts +++ b/src/__tests__/fixture/e2e/commands.ts @@ -1,55 +1,75 @@ import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; -import { TitleEscrowEndorseTransferOfOwnersCommand, TitleEscrowNominateBeneficiaryCommand, TitleEscrowtransferOwnerCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { + BaseTitleEscrowCommand, + TitleEscrowEndorseTransferOfOwnersCommand, + TitleEscrowNominateBeneficiaryCommand, + TitleEscrowTransferHolderCommand, +} from "../../../commands/title-escrow/title-escrow-command.type"; import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; +const command = `npm run dev --`; + export const generateDeployTokenRegistryCommand = ( - tokenRegistryParameter: DeployTokenRegistryCommand, - privateKey: string + tokenRegistryParameter: DeployTokenRegistryCommand, + privateKey: string ): string => { - return `npm run dev -- deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} -n ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}`; + return `${command} deploy token-registry "${tokenRegistryParameter.registryName}" ${tokenRegistryParameter.registrySymbol} --network ${tokenRegistryParameter.network} -k ${privateKey} --factory-address ${tokenRegistryParameter.factoryAddress} --token-implementation-address ${tokenRegistryParameter.tokenImplementationAddress} --deployer-address ${tokenRegistryParameter.deployerAddress}`; }; export const generateDeployDocumentStoreCommand = ( - documentStoreParameter: DeployDocumentStoreCommand, - privateKey: string + documentStoreParameter: DeployDocumentStoreCommand, + privateKey: string ): string => { - return `npm run dev -- deploy document-store "${documentStoreParameter.storeName}" -n ${documentStoreParameter.network} -k ${privateKey}`; + return `${command} deploy document-store "${documentStoreParameter.storeName}" --network ${documentStoreParameter.network} -k ${privateKey}`; }; export const generateMintTitleEscrowCommand = ( - titleEscrowParameter: TokenRegistryIssueCommand, - privateKey: string + titleEscrowParameter: TokenRegistryIssueCommand, + privateKey: string ): string => { - return `npm run dev -- token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} -n ${titleEscrowParameter.network}`; + return `${command} token-registry mint --address ${titleEscrowParameter.address} --tokenId ${titleEscrowParameter.tokenId} --beneficiary ${titleEscrowParameter.beneficiary} --holder ${titleEscrowParameter.holder} -k ${privateKey} --network ${titleEscrowParameter.network}`; }; export const generateChangeHolderCommand = ( - changeHolderParameter: TitleEscrowtransferOwnerCommand, - privateKey: string + changeHolderParameter: TitleEscrowTransferHolderCommand, + privateKey: string ): string => { - return `npm run dev -- title-escrow change-holder --token-registry ${changeHolderParameter.tokenRegistry} --tokenId ${changeHolderParameter.tokenId} --newHolder ${changeHolderParameter.newHolder} -k ${privateKey} --network ${changeHolderParameter.network}`; + return `${command} title-escrow change-holder --token-registry ${changeHolderParameter.tokenRegistry} --tokenId ${changeHolderParameter.tokenId} --newHolder ${changeHolderParameter.newHolder} -k ${privateKey} --network ${changeHolderParameter.network}`; }; export const generateTransferOwnersCommand = ( - transferOwners: TitleEscrowEndorseTransferOfOwnersCommand, - privateKey: string + transferOwners: TitleEscrowEndorseTransferOfOwnersCommand, + privateKey: string ): string => { - return `npm run dev -- title-escrow endorse-change-owner --token-registry ${transferOwners.tokenRegistry} --tokenId ${transferOwners.tokenId} --newOwner ${transferOwners.newOwner} --newHolder ${transferOwners.newHolder} -k ${privateKey} --network ${transferOwners.network}`; -} + return `${command} title-escrow endorse-change-owner --token-registry ${transferOwners.tokenRegistry} --tokenId ${transferOwners.tokenId} --newOwner ${transferOwners.newOwner} --newHolder ${transferOwners.newHolder} -k ${privateKey} --network ${transferOwners.network}`; +}; export const generateEndorseTransferOwnerCommand = ( - transferOwner: TitleEscrowNominateBeneficiaryCommand, - privateKey: string + transferOwner: TitleEscrowNominateBeneficiaryCommand, + privateKey: string ): string => { -// const command = `npm run dev -- title-escrow endorse-transfer-owner --token-registry ${transferHolder.tokenRegistry} --tokenId ${transferHolder.tokenId} --newBeneficiary ${transferHolder.newBeneficiary} -k ${owner.privateKey} --network ${transferHolder.network}`; - return `npm run dev -- title-escrow endorse-transfer-owner --token-registry ${transferOwner.tokenRegistry} --tokenId ${transferOwner.tokenId} --newBeneficiary ${transferOwner.newBeneficiary} -k ${privateKey} --network ${transferOwner.network}`; -} - + return `${command} title-escrow endorse-transfer-owner --token-registry ${transferOwner.tokenRegistry} --tokenId ${transferOwner.tokenId} --newBeneficiary ${transferOwner.newBeneficiary} -k ${privateKey} --network ${transferOwner.network}`; +}; export const generateNominateCommand = ( - nominateParameter: TitleEscrowNominateBeneficiaryCommand, - privateKey: string + nominateParameter: TitleEscrowNominateBeneficiaryCommand, + privateKey: string ): string => { -// const command = `npm run dev -- title-escrow nominate-change-owner --token-registry ${transferHolder.tokenRegistry} --tokenId ${transferHolder.tokenId} --newOwner ${transferHolder.newBeneficiary} -k ${owner.privateKey} --network ${transferHolder.network}`; - return `npm run dev -- title-escrow nominate-change-owner --token-registry ${nominateParameter.tokenRegistry} --tokenId ${nominateParameter.tokenId} --newOwner ${nominateParameter.newBeneficiary} -k ${privateKey} --network ${nominateParameter.network}`; -} + return `${command} title-escrow nominate-change-owner --token-registry ${nominateParameter.tokenRegistry} --tokenId ${nominateParameter.tokenId} --newOwner ${nominateParameter.newBeneficiary} -k ${privateKey} --network ${nominateParameter.network}`; +}; +export const generateSurrenderCommand = (surrenderParameter: BaseTitleEscrowCommand, privateKey: string): string => { + return `${command} title-escrow surrender --token-registry ${surrenderParameter.tokenRegistry} --tokenId ${surrenderParameter.tokenId} -k ${privateKey} --network ${surrenderParameter.network}`; +}; + +export const generateRejectSurrenderCommand = ( + surrenderParameter: BaseTitleEscrowCommand, + privateKey: string +): string => { + return `${command} title-escrow reject-surrendered --token-registry ${surrenderParameter.tokenRegistry} --tokenId ${surrenderParameter.tokenId} --network ${surrenderParameter.network} -k ${privateKey}`; +}; +export const generateAcceptSurrenderCommand = ( + surrenderParameter: BaseTitleEscrowCommand, + privateKey: string +): string => { + return `${command} title-escrow accept-surrendered --token-registry ${surrenderParameter.tokenRegistry} --tokenId ${surrenderParameter.tokenId} --network ${surrenderParameter.network} -k ${privateKey}`; +}; diff --git a/src/__tests__/fixture/e2e/shell.ts b/src/__tests__/fixture/e2e/shell.ts index 15c0e80f..e33a3fb3 100644 --- a/src/__tests__/fixture/e2e/shell.ts +++ b/src/__tests__/fixture/e2e/shell.ts @@ -4,71 +4,71 @@ import { emoji, verbose } from "./constants"; shell.config.silent = true; export interface LineInfo { - lineNumber: number; - lineContent: string; + lineNumber: number; + lineContent: string; } export const run = (command: string, silent = false): string => { - shell.config.silent = !(!silent && verbose); - const rawResults: ShellString = shell.exec(command); - const results = stripAnsi(rawResults.trim()); - if (!silent) { - const success = `${emoji.tick} success`; - const failure = `${emoji.cross} error`; - let successLines = extractLine(results, success); - if (!successLines) successLines = []; - let failureLines = extractLine(results, failure); - if (!failureLines) failureLines = []; - const statusLines: LineInfo[] = [...successLines, ...failureLines]; - log(command); - printLines(statusLines); - if(verbose && failureLines.length > 0) log(rawResults) - } - return results; + shell.config.silent = !(!silent && verbose); + const rawResults: ShellString = shell.exec(command); + const results = stripAnsi(rawResults.trim()); + if (!silent) { + const success = `${emoji.tick} success`; + const failure = `${emoji.cross} error`; + let successLines = extractLine(results, success); + if (!successLines) successLines = []; + let failureLines = extractLine(results, failure); + if (!failureLines) failureLines = []; + const statusLines: LineInfo[] = [...successLines, ...failureLines]; + log(command); + printLines(statusLines); + if (verbose && failureLines.length > 0) log(rawResults); + } + return results; }; export const printLines = (lines: LineInfo[]): void => { - lines.sort((a: LineInfo, b: LineInfo): number => { - return a.lineNumber - b.lineNumber; - }); - for (const line of lines) { - log(`${line.lineNumber}: ${line.lineContent}`); - } + lines.sort((a: LineInfo, b: LineInfo): number => { + return a.lineNumber - b.lineNumber; + }); + for (const line of lines) { + log(`${line.lineNumber}: ${line.lineContent}`); + } }; export const extractLine = (result: string, query: string): LineInfo[] | void => { - const splitResults = result.trim().split("\n"); - const matchedLines = []; - for (let count = 0; count < splitResults.length; count++) { - const line = splitResults[count].trim(); - const containsQueryString = line.includes(query); - if (containsQueryString) { - matchedLines.push({ - lineNumber: count, - lineContent: line, - }); - } + const splitResults = result.trim().split("\n"); + const matchedLines = []; + for (let count = 0; count < splitResults.length; count++) { + const line = splitResults[count].trim(); + const containsQueryString = line.includes(query); + if (containsQueryString) { + matchedLines.push({ + lineNumber: count, + lineContent: line, + }); } - if (matchedLines.length > 0) return matchedLines; - else return; + } + if (matchedLines.length > 0) return matchedLines; + else return; }; // https://github.com/chalk/strip-ansi/blob/main/index.js export function stripAnsi(ansiString: string): string { - if (typeof ansiString !== "string") { - throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); - } - return ansiString.replace(ansiRegex(), ""); + if (typeof ansiString !== "string") { + throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); + } + return ansiString.replace(ansiRegex(), ""); } // https://github.com/chalk/ansi-regex/blob/main/index.js export function ansiRegex({ onlyFirst = false } = {}): RegExp { - const pattern = [ - "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", - "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", - ].join("|"); + const pattern = [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", + ].join("|"); - return new RegExp(pattern, onlyFirst ? undefined : "g"); + return new RegExp(pattern, onlyFirst ? undefined : "g"); } export { shell, ShellString }; diff --git a/src/__tests__/fixture/e2e/utils.ts b/src/__tests__/fixture/e2e/utils.ts index 95885205..b4b94268 100644 --- a/src/__tests__/fixture/e2e/utils.ts +++ b/src/__tests__/fixture/e2e/utils.ts @@ -9,8 +9,17 @@ import { generateDeployTokenRegistryCommand, generateMintTitleEscrowCommand, generateNominateCommand, + generateSurrenderCommand, } from "./commands"; -import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { + BaseTitleEscrowCommand, + TitleEscrowNominateBeneficiaryCommand, +} from "../../../commands/title-escrow/title-escrow-command.type"; +export interface TokenInfo { + tokenRegistry: string; + tokenId: string; + titleEscrowAddress?: string; +} const defaults = { network: network, @@ -28,8 +37,8 @@ export const deployTokenRegistry = ( if (!tokenRegistryParameters) { const index = numberGenerator(100); tokenRegistryParameters = { - registryName: `Test Tokenx ${index}`, - registrySymbol: `TKNx${index}`, + registryName: `Test Token ${index}`, + registrySymbol: `TKN${index}`, factoryAddress: creators.titleEscrowFactory, tokenImplementationAddress: creators.tokenImplementation, deployerAddress: creators.deployer, @@ -71,9 +80,10 @@ export const deployDocumentStore = ( return documentStoreAddress; }; +const retries = 10; const usedTokenIds = new Set(); export const generateTokenId = (): string => { - for (let count = 0; count < 10; count = count + 1) { + for (let count = 0; count < retries; count = count + 1) { const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join("")}`; const unique = !usedTokenIds.has(generatedTokenId); if (unique) { @@ -84,24 +94,24 @@ export const generateTokenId = (): string => { throw new Error("Unable to generate tokenIds"); }; -export interface TokenInfo { - tokenRegistry: string, - tokenId: string, - titleEscrowAddress?: string, -} - -export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegistryIssueCommand): TokenInfo => { - if (!titleEscrowParameter) { - const wallet = new Wallet(privateKey); - titleEscrowParameter = { - address: deployTokenRegistry(privateKey), - beneficiary: wallet.address, - holder: wallet.address, - tokenId: generateTokenId(), - ...defaults, - }; +export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { + if (!tokenRegistryAddress) { + tokenRegistryAddress = deployTokenRegistry(privateKey); } + if (!isAddress(tokenRegistryAddress)) throw new Error("Invalid Token Registry Address"); + const wallet = new Wallet(privateKey); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + beneficiary: wallet.address, + holder: wallet.address, + tokenId: generateTokenId(), + ...defaults, + }; + return mintToken(privateKey, titleEscrowParameter); +}; + +export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); const results = run(command, true); const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; @@ -111,41 +121,68 @@ export const mintToken = (privateKey: string, titleEscrowParameter?: TokenRegist const titleEscrowAddressLine = splitResults[splitResults.length - 2]; const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); + if (titleEscrowAddress !== titleEscrowParameter.tokenId) throw new Error("tokenId mismatch"); return { tokenRegistry: titleEscrowParameter.address, tokenId: titleEscrowParameter.tokenId, - titleEscrowAddress + titleEscrowAddress, }; }; -export const mintNominatedToken = (privateKey: string, nominee: string) => { - const { tokenRegistry, tokenId } = mintToken(privateKey); +export const mintNominatedToken = (privateKey: string, nominee: string, tokenRegistryAddress?: string): TokenInfo => { + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = tokenDetails; const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { tokenId: tokenId, tokenRegistry: tokenRegistry, newBeneficiary: nominee, - ...defaults - } + ...defaults, + }; nominateToken(privateKey, nominateParameter); - return { tokenRegistry, tokenId }; -} + return tokenDetails; +}; -export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand) => { +export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { const command = generateNominateCommand(nominateParameter, privateKey); const results = run(command, true); const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully nominated to new owner with address ` + const middleFormat = `'s holder has been successfully nominated to new owner with address `; const queryResults = extractLine(results, frontFormat); if (!queryResults) throw new Error("Unable to nominate token"); const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); const checkSuccess = filteredLine.includes(frontFormat); const checkContext = filteredLine.includes(middleFormat); - expect(checkSuccess && checkContext).toBe(true); if (!checkSuccess || !checkContext) throw new Error("Unexpected nominate token format"); const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - expect(resultTokenId).toBe(nominateParameter.tokenId); if (resultTokenId !== nominateParameter.tokenId) throw new Error("Unexpected nominate tokenid"); const destination = filteredLine.trim().substring(filteredLine.length - 42); if (destination !== nominateParameter.newBeneficiary) throw new Error("Unexpected nominee"); }; +export const mintSurrenderToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = tokenDetails; + const surrenderParameter: BaseTitleEscrowCommand = { + tokenRegistry: tokenRegistry, + tokenId: tokenId, + ...defaults, + }; + surrenderToken(privateKey, surrenderParameter); + return tokenDetails; +}; + +export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { + const command = generateSurrenderCommand(surrenderParameter, privateKey); + const results = run(command); + + const tokenRegistrySuccessFormat = `${emoji.tick} success Transferable record with hash `; + const queryResults = extractLine(results, tokenRegistrySuccessFormat); + if (!queryResults) throw new Error("Unable to surrender token"); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); + if (!checkSuccess) throw new Error("Unexpected surrender token format"); + const resultTokenId = filteredLine + .trim() + .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); + if (resultTokenId !== surrenderParameter.tokenId) throw new Error("Unexpected surrendered token"); +}; diff --git a/src/__tests__/token-registry/accept-surrender.e2e.test.ts b/src/__tests__/token-registry/accept-surrender.e2e.test.ts new file mode 100644 index 00000000..19ef40f7 --- /dev/null +++ b/src/__tests__/token-registry/accept-surrender.e2e.test.ts @@ -0,0 +1,39 @@ +import { deployTokenRegistry, mintSurrenderToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner } from "../fixture/e2e/constants"; +import { generateAcceptSurrenderCommand } from "../fixture/e2e/commands"; + +describe("surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to surrender title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + + const tokenRegistrySuccessFormat = `${emoji.tick} success Surrendered transferable record with hash `; + const queryResults = extractLine(results, tokenRegistrySuccessFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const resultTokenId = filteredLine + .trim() + .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); + expect(resultTokenId).toBe(tokenId); + }); + + it.todo("should be fail when permission is denied"); +}); diff --git a/src/__tests__/token-registry/change-holder.e2e.test.ts b/src/__tests__/token-registry/change-holder.e2e.test.ts index 22790378..c64538fd 100644 --- a/src/__tests__/token-registry/change-holder.e2e.test.ts +++ b/src/__tests__/token-registry/change-holder.e2e.test.ts @@ -1,7 +1,6 @@ -import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; +import { deployTokenRegistry, mintTokenRegistry } from "../fixture/e2e/utils"; import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateChangeHolderCommand } from "../fixture/e2e/commands"; @@ -13,13 +12,12 @@ describe("transfer holder title-escrow", () => { tokenRegistryAddress = deployTokenRegistry(owner.privateKey); }); - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - + // const defaultTitleEscrow = { + // beneficiary: owner.ethAddress, + // holder: owner.ethAddress, + // network: network, + // dryRun: false, + // }; const defaultTransferHolder = { newHolder: receiver.ethAddress, @@ -28,22 +26,16 @@ describe("transfer holder title-escrow", () => { }; it("should be able to transfer holder title-escrow on token-registry", async () => { - const tokenId = generateTokenId(); - const titleEscrow: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - ...defaultTitleEscrow, - } - mintToken(owner.privateKey, titleEscrow); + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - ...defaultTransferHolder, - } + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferHolder, + }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully changed to holder with address: ` + const middleFormat = `'s holder has been successfully changed to holder with address: `; const queryResults = extractLine(results, frontFormat); expect(queryResults).toBeTruthy(); const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); diff --git a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts index 2a16fe13..c6209848 100644 --- a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts +++ b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts @@ -1,9 +1,7 @@ - -import { deployTokenRegistry, generateTokenId, mintToken, nominateToken } from "../fixture/e2e/utils"; +import { deployTokenRegistry, mintNominatedToken } from "../fixture/e2e/utils"; import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { TitleEscrowEndorseTransferOfOwnersCommand, TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateTransferOwnersCommand } from "../fixture/e2e/commands"; describe("nominate title-escrow", () => { @@ -14,13 +12,12 @@ describe("nominate title-escrow", () => { tokenRegistryAddress = deployTokenRegistry(owner.privateKey); }); - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - + // const defaultTitleEscrow = { + // beneficiary: owner.ethAddress, + // holder: owner.ethAddress, + // network: network, + // dryRun: false, + // }; const defaultTransferOwners = { newOwner: receiver.ethAddress, @@ -30,30 +27,21 @@ describe("nominate title-escrow", () => { }; it("should be able to nominate title-escrow on token-registry", async () => { - const tokenId = generateTokenId(); - const titleEscrow: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - ...defaultTitleEscrow, - } - mintToken(owner.privateKey, titleEscrow); - const nominateOnwer: TitleEscrowNominateBeneficiaryCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - newBeneficiary: defaultTransferOwners.newOwner, - ...defaultTransferOwners, - } + const { tokenRegistry, tokenId } = mintNominatedToken( + owner.privateKey, + defaultTransferOwners.newOwner, + tokenRegistryAddress + ); const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - ...defaultTransferOwners, - } - nominateToken(owner.privateKey, nominateOnwer) + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferOwners, + }; const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully endorsed to new owner with address ` - const rearFormat = ` and new holder with address: ` + const middleFormat = `'s holder has been successfully endorsed to new owner with address `; + const rearFormat = ` and new holder with address: `; const queryResults = extractLine(results, frontFormat); expect(queryResults).toBeTruthy(); const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); @@ -63,7 +51,9 @@ describe("nominate title-escrow", () => { expect(checkSuccess && checkOwner && checkHolder).toBe(true); const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); expect(resultTokenId).toBe(transferOwners.tokenId); - const resultOwner = filteredLine.trim().substring(frontFormat.length + 66 + middleFormat.length, frontFormat.length + 66 + middleFormat.length + 42); + const resultOwner = filteredLine + .trim() + .substring(frontFormat.length + 66 + middleFormat.length, frontFormat.length + 66 + middleFormat.length + 42); expect(resultOwner).toBe(transferOwners.newOwner); const destination = filteredLine.trim().substring(filteredLine.length - 42); expect(destination).toBe(transferOwners.newHolder); diff --git a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts index a68d7bec..d188ef58 100644 --- a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts +++ b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts @@ -1,8 +1,7 @@ -import { deployTokenRegistry, generateTokenId, mintToken, nominateToken } from "../fixture/e2e/utils"; +import { deployTokenRegistry, mintNominatedToken } from "../fixture/e2e/utils"; import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateEndorseTransferOwnerCommand } from "../fixture/e2e/commands"; describe("nominate title-escrow", () => { @@ -13,13 +12,12 @@ describe("nominate title-escrow", () => { tokenRegistryAddress = deployTokenRegistry(owner.privateKey); }); - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - + // const defaultTitleEscrow = { + // beneficiary: owner.ethAddress, + // holder: owner.ethAddress, + // network: network, + // dryRun: false, + // }; const defaultTransferHolder = { newBeneficiary: receiver.ethAddress, @@ -28,24 +26,21 @@ describe("nominate title-escrow", () => { }; it("should be able to nominate title-escrow on token-registry", async () => { - const tokenId = generateTokenId(); - const titleEscrow: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - ...defaultTitleEscrow, - } - mintToken(owner.privateKey, titleEscrow); + const { tokenRegistry, tokenId } = mintNominatedToken( + owner.privateKey, + defaultTransferHolder.newBeneficiary, + tokenRegistryAddress + ); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - ...defaultTransferHolder, - } - nominateToken(owner.privateKey, transferHolder) - - const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey) + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferHolder, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully endorsed to approved beneficiary at ` + const middleFormat = `'s holder has been successfully endorsed to approved beneficiary at `; const queryResults = extractLine(results, frontFormat); expect(queryResults).toBeTruthy(); const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); diff --git a/src/__tests__/token-registry/mint.e2e.test.ts b/src/__tests__/token-registry/mint.e2e.test.ts index ff3d494f..98ed5348 100644 --- a/src/__tests__/token-registry/mint.e2e.test.ts +++ b/src/__tests__/token-registry/mint.e2e.test.ts @@ -1,6 +1,6 @@ import { deployTokenRegistry, generateTokenId } from "../fixture/e2e/utils"; import { run } from "../fixture/e2e/shell"; -import { emoji, network, owner, silent } from "../fixture/e2e/constants"; +import { emoji, network, owner } from "../fixture/e2e/constants"; import { isAddress } from "web3-utils"; import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { generateMintTitleEscrowCommand } from "../fixture/e2e/commands"; @@ -22,8 +22,7 @@ describe("deploy token-registry", () => { holder: owner.ethAddress, network: network, dryRun: false, - } - // const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; + }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; diff --git a/src/__tests__/token-registry/nominate.e2e.test.ts b/src/__tests__/token-registry/nominate.e2e.test.ts index 9e9d3de1..7fda263c 100644 --- a/src/__tests__/token-registry/nominate.e2e.test.ts +++ b/src/__tests__/token-registry/nominate.e2e.test.ts @@ -4,7 +4,7 @@ import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/ import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateNominateCommand } from "../fixture/e2e/commands"; describe("nominate title-escrow", () => { @@ -31,20 +31,20 @@ describe("nominate title-escrow", () => { it("should be able to nominate title-escrow on token-registry", async () => { const tokenId = generateTokenId(); const titleEscrow: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - ...defaultTitleEscrow, - } + address: tokenRegistryAddress, + tokenId: tokenId, + ...defaultTitleEscrow, + }; mintToken(owner.privateKey, titleEscrow); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - ...defaultTransferHolder, - } + tokenId: titleEscrow.tokenId, + tokenRegistry: titleEscrow.address, + ...defaultTransferHolder, + }; const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully nominated to new owner with address ` + const middleFormat = `'s holder has been successfully nominated to new owner with address `; const queryResults = extractLine(results, frontFormat); expect(queryResults).toBeTruthy(); const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); diff --git a/src/__tests__/token-registry/reject-surrender.e2e.test.ts b/src/__tests__/token-registry/reject-surrender.e2e.test.ts new file mode 100644 index 00000000..551225a2 --- /dev/null +++ b/src/__tests__/token-registry/reject-surrender.e2e.test.ts @@ -0,0 +1,39 @@ +import { deployTokenRegistry, mintSurrenderToken } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; +import { emoji, network, owner } from "../fixture/e2e/constants"; +import { generateRejectSurrenderCommand } from "../fixture/e2e/commands"; + +describe("surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to surrender title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + + const tokenRegistrySuccessFormat = `${emoji.tick} success Surrendered transferable record with hash `; + const queryResults = extractLine(results, tokenRegistrySuccessFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const resultTokenId = filteredLine + .trim() + .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); + expect(resultTokenId).toBe(tokenId); + }); + + it.todo("should be fail when user without permission attempts reject surrender"); +}); diff --git a/src/__tests__/token-registry/surrender.e2e.test.ts b/src/__tests__/token-registry/surrender.e2e.test.ts index 142d14ba..9cc8d7ea 100644 --- a/src/__tests__/token-registry/surrender.e2e.test.ts +++ b/src/__tests__/token-registry/surrender.e2e.test.ts @@ -1,9 +1,8 @@ -import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; -import { run } from "../fixture/e2e/shell"; +import { deployTokenRegistry, mintTokenRegistry } from "../fixture/e2e/utils"; +import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner } from "../fixture/e2e/constants"; -import { isAddress } from "web3-utils"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { defaultSeverityColor } from "snyk/dist/lib/snyk-test/common"; +import { BaseTitleEscrowCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { generateSurrenderCommand } from "../fixture/e2e/commands"; describe("surrender title-escrow", () => { jest.setTimeout(90000); @@ -20,37 +19,26 @@ describe("surrender title-escrow", () => { dryRun: false, }; - it.todo("surrender is not working ????"); it("should be able to surrender title-escrow on token-registry", async () => { - expect(true).toBe(false); - // const tokenId = generateTokenId(); - // const surrenderTitleEscrow: TokenRegistryIssueCommand = { - // address: tokenRegistryAddress, - // tokenId: tokenId, - // ...defaultTitleEscrow, - // } - // const titleEscrowAddress = mintToken(owner.privateKey, surrenderTitleEscrow); - // console.log(titleEscrowAddress); - // const command = `npm run dev -- title-escrow surrender --token-registry ${surrenderTitleEscrow.address} --tokenId ${surrenderTitleEscrow.tokenId} -k ${owner.privateKey} --network ${surrenderTitleEscrow.network}`; + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + tokenRegistry, + tokenId, + ...defaultTitleEscrow, + }; - // const results = run(command); - // const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; - // const checkSuccess = results.includes(tokenRegistrySuccessFormat); - // expect(checkSuccess).toBe(true); - // const splitResults = results.trim().split("\n"); - // const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; - // const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); - // expect(isAddress(tokenRegistryAddress)).toBe(true); + const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); + const results = run(command); - // const tokenId = generateTokenId(); - // const command = `npm run dev -- token-registry mint --address ${tokenRegistryAddress} --tokenId ${tokenId} --beneficiary ${owner.ethAddress} --holder ${owner.ethAddress} -k ${owner.privateKey} --network ${network}`; - // const results = run(command); - // const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; - // const checkSuccess = results.includes(tokenRegistrySuccessFormat); - // expect(checkSuccess).toBe(true); - // const splitResults = results.trim().split("\n"); - // const titleEscrowAddressLine = splitResults[splitResults.length - 2]; - // const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); - // expect(isAddress(titleEscrowAddress)).toBe(true); + const tokenRegistrySuccessFormat = `${emoji.tick} success Transferable record with hash `; + const queryResults = extractLine(results, tokenRegistrySuccessFormat); + expect(queryResults).toBeTruthy(); + const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); + const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); + expect(checkSuccess).toBe(true); + const resultTokenId = filteredLine + .trim() + .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); + expect(resultTokenId).toBe(surrenderTitleEscrow.tokenId); }); }); diff --git a/src/commands/title-escrow/surrender-document.ts b/src/commands/title-escrow/surrender-document.ts index 275408ec..e418f635 100644 --- a/src/commands/title-escrow/surrender-document.ts +++ b/src/commands/title-escrow/surrender-document.ts @@ -38,7 +38,6 @@ export const handler = async (args: TitleEscrowSurrenderDocumentCommand): Promis success(`Transferable record with hash ${args.tokenId} has been surrendered.`); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transaction.transactionHash}`); } catch (e) { - console.log(e) error(getErrorMessage(e)); } }; From d4995eda85cb5e7ec6e175f7f1b4820ee4109ece Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 7 Dec 2022 18:01:28 +0800 Subject: [PATCH 05/20] fix: rename --- package.json | 2 +- src/__tests__/fixture/e2e/constants.ts | 2 +- src/__tests__/fixture/e2e/utils.ts | 1 - src/__tests__/token-registry/accept-surrender.e2e.test.ts | 4 ++-- src/__tests__/token-registry/endorse-change-owner.e2e.test.ts | 4 ++-- src/__tests__/token-registry/endorse-transfer.e2e.test.ts | 4 ++-- src/__tests__/token-registry/reject-surrender.e2e.test.ts | 4 ++-- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 7a4e31b2..fc7342b1 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", - "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli --miner.blockTime 12", + "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", diff --git a/src/__tests__/fixture/e2e/constants.ts b/src/__tests__/fixture/e2e/constants.ts index 5c3d3fef..c0f94982 100644 --- a/src/__tests__/fixture/e2e/constants.ts +++ b/src/__tests__/fixture/e2e/constants.ts @@ -32,4 +32,4 @@ export const emoji = { }; export const silent = true; -export const verbose = true; +export const verbose = false; diff --git a/src/__tests__/fixture/e2e/utils.ts b/src/__tests__/fixture/e2e/utils.ts index b4b94268..e8fdd73f 100644 --- a/src/__tests__/fixture/e2e/utils.ts +++ b/src/__tests__/fixture/e2e/utils.ts @@ -121,7 +121,6 @@ export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistr const titleEscrowAddressLine = splitResults[splitResults.length - 2]; const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); - if (titleEscrowAddress !== titleEscrowParameter.tokenId) throw new Error("tokenId mismatch"); return { tokenRegistry: titleEscrowParameter.address, tokenId: titleEscrowParameter.tokenId, diff --git a/src/__tests__/token-registry/accept-surrender.e2e.test.ts b/src/__tests__/token-registry/accept-surrender.e2e.test.ts index 19ef40f7..f5afce54 100644 --- a/src/__tests__/token-registry/accept-surrender.e2e.test.ts +++ b/src/__tests__/token-registry/accept-surrender.e2e.test.ts @@ -3,7 +3,7 @@ import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner } from "../fixture/e2e/constants"; import { generateAcceptSurrenderCommand } from "../fixture/e2e/commands"; -describe("surrender title-escrow", () => { +describe("accept-surrender title-escrow", () => { jest.setTimeout(90000); let tokenRegistryAddress = ""; @@ -18,7 +18,7 @@ describe("surrender title-escrow", () => { dryRun: false, }; - it("should be able to surrender title-escrow on token-registry", async () => { + it("should be able to accept-surrender title-escrow on token-registry", async () => { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); diff --git a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts index c6209848..9fce049e 100644 --- a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts +++ b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts @@ -4,7 +4,7 @@ import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateTransferOwnersCommand } from "../fixture/e2e/commands"; -describe("nominate title-escrow", () => { +describe("endorse change owner title-escrow", () => { jest.setTimeout(90000); let tokenRegistryAddress = ""; @@ -26,7 +26,7 @@ describe("nominate title-escrow", () => { dryRun: false, }; - it("should be able to nominate title-escrow on token-registry", async () => { + it("should be able to endorse change owner title-escrow on token-registry", async () => { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, defaultTransferOwners.newOwner, diff --git a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts index d188ef58..bf189829 100644 --- a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts +++ b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts @@ -4,7 +4,7 @@ import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { generateEndorseTransferOwnerCommand } from "../fixture/e2e/commands"; -describe("nominate title-escrow", () => { +describe("endorse transfer title-escrow", () => { jest.setTimeout(90000); let tokenRegistryAddress = ""; @@ -25,7 +25,7 @@ describe("nominate title-escrow", () => { dryRun: false, }; - it("should be able to nominate title-escrow on token-registry", async () => { + it("should be able to endorse transfer title-escrow on token-registry", async () => { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, defaultTransferHolder.newBeneficiary, diff --git a/src/__tests__/token-registry/reject-surrender.e2e.test.ts b/src/__tests__/token-registry/reject-surrender.e2e.test.ts index 551225a2..44671292 100644 --- a/src/__tests__/token-registry/reject-surrender.e2e.test.ts +++ b/src/__tests__/token-registry/reject-surrender.e2e.test.ts @@ -3,7 +3,7 @@ import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; import { emoji, network, owner } from "../fixture/e2e/constants"; import { generateRejectSurrenderCommand } from "../fixture/e2e/commands"; -describe("surrender title-escrow", () => { +describe("reject surrender title-escrow", () => { jest.setTimeout(90000); let tokenRegistryAddress = ""; @@ -18,7 +18,7 @@ describe("surrender title-escrow", () => { dryRun: false, }; - it("should be able to surrender title-escrow on token-registry", async () => { + it("should be able to reject surrender title-escrow on token-registry", async () => { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); From 4799fea13a690762cd16d2eb354a58bd627d6479 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:11:15 +0800 Subject: [PATCH 06/20] feat: tests --- package-lock.json | 464 +++++++++++++++++- package.json | 2 + src/__tests__/fixture/e2e/utils.ts | 187 ------- .../accept-surrender.e2e.test.ts | 39 -- .../token-registry/change-holder.e2e.test.ts | 50 -- .../endorse-change-owner.e2e.test.ts | 61 --- .../endorse-transfer.e2e.test.ts | 55 --- src/__tests__/token-registry/mint.e2e.test.ts | 36 -- .../token-registry/nominate.e2e.test.ts | 59 --- .../reject-surrender.e2e.test.ts | 39 -- .../token-registry/surrender.e2e.test.ts | 44 -- .../title-escrow/accept-surrendered.ts | 1 + src/e2e/accept-surrender.test.ts | 60 +++ src/e2e/change-holder.test.ts | 103 ++++ .../deploy.e2e.test.ts => e2e/deploy.test.ts} | 41 +- src/e2e/endorse-change-owner.test.ts | 55 +++ src/e2e/endorse-transfer.test.ts | 56 +++ src/e2e/mint.test.ts | 158 ++++++ src/e2e/nominate.test.ts | 109 ++++ src/e2e/reject-surrender.test.ts | 63 +++ src/e2e/surrender.test.ts | 100 ++++ src/e2e/utils/bootstrap/accept-surrender.ts | 20 + src/e2e/utils/bootstrap/change-holder.ts | 45 ++ src/e2e/utils/bootstrap/common.ts | 9 + src/e2e/utils/bootstrap/deploy.ts | 47 ++ src/e2e/utils/bootstrap/endorse-owners.ts | 28 ++ src/e2e/utils/bootstrap/endorse-transfer.ts | 23 + src/e2e/utils/bootstrap/index.ts | 11 + src/e2e/utils/bootstrap/mint.ts | 85 ++++ src/e2e/utils/bootstrap/nominate.ts | 54 ++ src/e2e/utils/bootstrap/reject-surrender.ts | 20 + src/e2e/utils/bootstrap/surrender.ts | 48 ++ .../fixture/e2e => e2e/utils}/commands.ts | 6 +- .../fixture/e2e => e2e/utils}/constants.ts | 36 +- src/e2e/utils/contract-checks.ts | 170 +++++++ .../fixture/e2e => e2e/utils}/shell.ts | 25 +- src/e2e/utils/token-management.ts | 22 + 37 files changed, 1816 insertions(+), 615 deletions(-) delete mode 100644 src/__tests__/fixture/e2e/utils.ts delete mode 100644 src/__tests__/token-registry/accept-surrender.e2e.test.ts delete mode 100644 src/__tests__/token-registry/change-holder.e2e.test.ts delete mode 100644 src/__tests__/token-registry/endorse-change-owner.e2e.test.ts delete mode 100644 src/__tests__/token-registry/endorse-transfer.e2e.test.ts delete mode 100644 src/__tests__/token-registry/mint.e2e.test.ts delete mode 100644 src/__tests__/token-registry/nominate.e2e.test.ts delete mode 100644 src/__tests__/token-registry/reject-surrender.e2e.test.ts delete mode 100644 src/__tests__/token-registry/surrender.e2e.test.ts create mode 100644 src/e2e/accept-surrender.test.ts create mode 100644 src/e2e/change-holder.test.ts rename src/{__tests__/token-registry/deploy.e2e.test.ts => e2e/deploy.test.ts} (54%) create mode 100644 src/e2e/endorse-change-owner.test.ts create mode 100644 src/e2e/endorse-transfer.test.ts create mode 100644 src/e2e/mint.test.ts create mode 100644 src/e2e/nominate.test.ts create mode 100644 src/e2e/reject-surrender.test.ts create mode 100644 src/e2e/surrender.test.ts create mode 100644 src/e2e/utils/bootstrap/accept-surrender.ts create mode 100644 src/e2e/utils/bootstrap/change-holder.ts create mode 100644 src/e2e/utils/bootstrap/common.ts create mode 100644 src/e2e/utils/bootstrap/deploy.ts create mode 100644 src/e2e/utils/bootstrap/endorse-owners.ts create mode 100644 src/e2e/utils/bootstrap/endorse-transfer.ts create mode 100644 src/e2e/utils/bootstrap/index.ts create mode 100644 src/e2e/utils/bootstrap/mint.ts create mode 100644 src/e2e/utils/bootstrap/nominate.ts create mode 100644 src/e2e/utils/bootstrap/reject-surrender.ts create mode 100644 src/e2e/utils/bootstrap/surrender.ts rename src/{__tests__/fixture/e2e => e2e/utils}/commands.ts (94%) rename src/{__tests__/fixture/e2e => e2e/utils}/constants.ts (58%) create mode 100644 src/e2e/utils/contract-checks.ts rename src/{__tests__/fixture/e2e => e2e/utils}/shell.ts (71%) create mode 100644 src/e2e/utils/token-management.ts diff --git a/package-lock.json b/package-lock.json index a5fe303e..2cbcf9f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1638,6 +1638,464 @@ "@ethersproject/transactions": "^5.4.0" } }, + "@ethersproject/experimental": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/experimental/-/experimental-5.7.0.tgz", + "integrity": "sha512-DWvhuw7Dg8JPyhMbh/CNYOwsTLjXRx/HGkacIL5rBocG8jJC0kmixwoK/J3YblO4vtcyBLMa+sV74RJZK2iyHg==", + "dev": true, + "requires": { + "@ethersproject/web": "^5.7.0", + "ethers": "^5.7.0", + "scrypt-js": "3.0.1" + }, + "dependencies": { + "@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true + }, + "@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "requires": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "requires": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + } + } + }, "@ethersproject/hash": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz", @@ -5276,9 +5734,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001243", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001243.tgz", - "integrity": "sha512-vNxw9mkTBtkmLFnJRv/2rhs1yufpDfCkBZexG3Y0xdOH2Z/eE/85E4Dl5j1YUN34nZVsSp6vVRFQRrez9wJMRA==", + "version": "1.0.30001439", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", + "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", "dev": true }, "canonicalize": { diff --git a/package.json b/package.json index fc7342b1..10e83e70 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli", + "test:e2e": "jest --ci src/e2e/", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", @@ -39,6 +40,7 @@ "@commitlint/cli": "^12.1.4", "@commitlint/config-conventional": "^12.1.4", "@commitlint/prompt": "^12.1.4", + "@ethersproject/experimental": "^5.7.0", "@ls-age/commitlint-circle": "1.0.0", "@semantic-release/exec": "^5.0.0", "@types/debug": "4.1.5", diff --git a/src/__tests__/fixture/e2e/utils.ts b/src/__tests__/fixture/e2e/utils.ts deleted file mode 100644 index e8fdd73f..00000000 --- a/src/__tests__/fixture/e2e/utils.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { isAddress } from "web3-utils"; -import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; -import { creators, emoji, network } from "./constants"; -import { extractLine, LineInfo, run } from "./shell"; -import { Wallet } from "ethers"; -import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; -import { - generateDeployDocumentStoreCommand, - generateDeployTokenRegistryCommand, - generateMintTitleEscrowCommand, - generateNominateCommand, - generateSurrenderCommand, -} from "./commands"; -import { - BaseTitleEscrowCommand, - TitleEscrowNominateBeneficiaryCommand, -} from "../../../commands/title-escrow/title-escrow-command.type"; -export interface TokenInfo { - tokenRegistry: string; - tokenId: string; - titleEscrowAddress?: string; -} - -const defaults = { - network: network, - dryRun: false, -}; - -const numberGenerator = (range: number): number => { - return Math.floor(Math.random() * range); -}; - -export const deployTokenRegistry = ( - privateKey: string, - tokenRegistryParameters?: DeployTokenRegistryCommand -): string => { - if (!tokenRegistryParameters) { - const index = numberGenerator(100); - tokenRegistryParameters = { - registryName: `Test Token ${index}`, - registrySymbol: `TKN${index}`, - factoryAddress: creators.titleEscrowFactory, - tokenImplementationAddress: creators.tokenImplementation, - deployerAddress: creators.deployer, - ...defaults, - }; - } - const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); - const results = run(command, true); - const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - if (!checkSuccess) throw new Error("Unable to deploy the token registry"); - const splitResults = results.trim().split("\n"); - const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; - const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); - if (!isAddress(tokenRegistryAddress)) throw new Error("Unable to find token registry address"); - return tokenRegistryAddress; -}; - -export const deployDocumentStore = ( - privateKey: string, - documentStoreParameters?: DeployDocumentStoreCommand -): string => { - if (!documentStoreParameters) { - const index = numberGenerator(100); - documentStoreParameters = { - storeName: `Test Document Store ${index}`, - ...defaults, - }; - } - const command = generateDeployDocumentStoreCommand(documentStoreParameters, privateKey); - const results = run(command, true); - const documentStoreSuccessFormat = `${emoji.tick} success Document store Test Document Store deployed at `; - const checkSuccess = results.includes(documentStoreSuccessFormat); - if (!checkSuccess) throw new Error("Unable to deploy document store"); - const splitResults = results.trim().split("\n"); - const documentStoreAddressLine = splitResults[splitResults.length - 2]; - const documentStoreAddress = documentStoreAddressLine.trim().substring(documentStoreSuccessFormat.length); - if (!isAddress(documentStoreAddress)) throw new Error("Unable to find document store address"); - return documentStoreAddress; -}; - -const retries = 10; -const usedTokenIds = new Set(); -export const generateTokenId = (): string => { - for (let count = 0; count < retries; count = count + 1) { - const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join("")}`; - const unique = !usedTokenIds.has(generatedTokenId); - if (unique) { - usedTokenIds.add(generatedTokenId); - return generatedTokenId; - } - } - throw new Error("Unable to generate tokenIds"); -}; - -export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { - if (!tokenRegistryAddress) { - tokenRegistryAddress = deployTokenRegistry(privateKey); - } - if (!isAddress(tokenRegistryAddress)) throw new Error("Invalid Token Registry Address"); - const wallet = new Wallet(privateKey); - const titleEscrowParameter: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - beneficiary: wallet.address, - holder: wallet.address, - tokenId: generateTokenId(), - ...defaults, - }; - - return mintToken(privateKey, titleEscrowParameter); -}; - -export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { - const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); - const results = run(command, true); - const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - if (!checkSuccess) throw new Error("Unable to mint token"); - const splitResults = results.trim().split("\n"); - const titleEscrowAddressLine = splitResults[splitResults.length - 2]; - const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); - if (!isAddress(titleEscrowAddress)) throw new Error("Unable to find token"); - return { - tokenRegistry: titleEscrowParameter.address, - tokenId: titleEscrowParameter.tokenId, - titleEscrowAddress, - }; -}; - -export const mintNominatedToken = (privateKey: string, nominee: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); - const { tokenRegistry, tokenId } = tokenDetails; - const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - tokenId: tokenId, - tokenRegistry: tokenRegistry, - newBeneficiary: nominee, - ...defaults, - }; - nominateToken(privateKey, nominateParameter); - return tokenDetails; -}; - -export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { - const command = generateNominateCommand(nominateParameter, privateKey); - const results = run(command, true); - const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully nominated to new owner with address `; - const queryResults = extractLine(results, frontFormat); - if (!queryResults) throw new Error("Unable to nominate token"); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(frontFormat); - const checkContext = filteredLine.includes(middleFormat); - if (!checkSuccess || !checkContext) throw new Error("Unexpected nominate token format"); - const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - if (resultTokenId !== nominateParameter.tokenId) throw new Error("Unexpected nominate tokenid"); - const destination = filteredLine.trim().substring(filteredLine.length - 42); - if (destination !== nominateParameter.newBeneficiary) throw new Error("Unexpected nominee"); -}; - -export const mintSurrenderToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); - const { tokenRegistry, tokenId } = tokenDetails; - const surrenderParameter: BaseTitleEscrowCommand = { - tokenRegistry: tokenRegistry, - tokenId: tokenId, - ...defaults, - }; - surrenderToken(privateKey, surrenderParameter); - return tokenDetails; -}; - -export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { - const command = generateSurrenderCommand(surrenderParameter, privateKey); - const results = run(command); - - const tokenRegistrySuccessFormat = `${emoji.tick} success Transferable record with hash `; - const queryResults = extractLine(results, tokenRegistrySuccessFormat); - if (!queryResults) throw new Error("Unable to surrender token"); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); - if (!checkSuccess) throw new Error("Unexpected surrender token format"); - const resultTokenId = filteredLine - .trim() - .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); - if (resultTokenId !== surrenderParameter.tokenId) throw new Error("Unexpected surrendered token"); -}; diff --git a/src/__tests__/token-registry/accept-surrender.e2e.test.ts b/src/__tests__/token-registry/accept-surrender.e2e.test.ts deleted file mode 100644 index f5afce54..00000000 --- a/src/__tests__/token-registry/accept-surrender.e2e.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { deployTokenRegistry, mintSurrenderToken } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner } from "../fixture/e2e/constants"; -import { generateAcceptSurrenderCommand } from "../fixture/e2e/commands"; - -describe("accept-surrender title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to accept-surrender title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); - const results = run(command); - - const tokenRegistrySuccessFormat = `${emoji.tick} success Surrendered transferable record with hash `; - const queryResults = extractLine(results, tokenRegistrySuccessFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); - const resultTokenId = filteredLine - .trim() - .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); - expect(resultTokenId).toBe(tokenId); - }); - - it.todo("should be fail when permission is denied"); -}); diff --git a/src/__tests__/token-registry/change-holder.e2e.test.ts b/src/__tests__/token-registry/change-holder.e2e.test.ts deleted file mode 100644 index c64538fd..00000000 --- a/src/__tests__/token-registry/change-holder.e2e.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { deployTokenRegistry, mintTokenRegistry } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { generateChangeHolderCommand } from "../fixture/e2e/commands"; - -describe("transfer holder title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - // const defaultTitleEscrow = { - // beneficiary: owner.ethAddress, - // holder: owner.ethAddress, - // network: network, - // dryRun: false, - // }; - - const defaultTransferHolder = { - newHolder: receiver.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to transfer holder title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const transferHolder: TitleEscrowTransferHolderCommand = { - tokenId: tokenId, - tokenRegistry: tokenRegistry, - ...defaultTransferHolder, - }; - const command = generateChangeHolderCommand(transferHolder, owner.privateKey); - const results = run(command); - const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully changed to holder with address: `; - const queryResults = extractLine(results, frontFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(frontFormat); - const checkContext = filteredLine.includes(middleFormat); - expect(checkSuccess && checkContext).toBe(true); - const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - expect(resultTokenId).toBe(transferHolder.tokenId); - const destination = filteredLine.trim().substring(filteredLine.length - 42); - expect(destination).toBe(transferHolder.newHolder); - }); -}); diff --git a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts b/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts deleted file mode 100644 index 9fce049e..00000000 --- a/src/__tests__/token-registry/endorse-change-owner.e2e.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { deployTokenRegistry, mintNominatedToken } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { generateTransferOwnersCommand } from "../fixture/e2e/commands"; - -describe("endorse change owner title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - // const defaultTitleEscrow = { - // beneficiary: owner.ethAddress, - // holder: owner.ethAddress, - // network: network, - // dryRun: false, - // }; - - const defaultTransferOwners = { - newOwner: receiver.ethAddress, - newHolder: receiver.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to endorse change owner title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintNominatedToken( - owner.privateKey, - defaultTransferOwners.newOwner, - tokenRegistryAddress - ); - const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { - tokenId: tokenId, - tokenRegistry: tokenRegistry, - ...defaultTransferOwners, - }; - const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); - const results = run(command); - const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully endorsed to new owner with address `; - const rearFormat = ` and new holder with address: `; - const queryResults = extractLine(results, frontFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(frontFormat); - const checkOwner = filteredLine.includes(middleFormat); - const checkHolder = filteredLine.includes(rearFormat); - expect(checkSuccess && checkOwner && checkHolder).toBe(true); - const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - expect(resultTokenId).toBe(transferOwners.tokenId); - const resultOwner = filteredLine - .trim() - .substring(frontFormat.length + 66 + middleFormat.length, frontFormat.length + 66 + middleFormat.length + 42); - expect(resultOwner).toBe(transferOwners.newOwner); - const destination = filteredLine.trim().substring(filteredLine.length - 42); - expect(destination).toBe(transferOwners.newHolder); - }); -}); diff --git a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts b/src/__tests__/token-registry/endorse-transfer.e2e.test.ts deleted file mode 100644 index bf189829..00000000 --- a/src/__tests__/token-registry/endorse-transfer.e2e.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { deployTokenRegistry, mintNominatedToken } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { generateEndorseTransferOwnerCommand } from "../fixture/e2e/commands"; - -describe("endorse transfer title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - // const defaultTitleEscrow = { - // beneficiary: owner.ethAddress, - // holder: owner.ethAddress, - // network: network, - // dryRun: false, - // }; - - const defaultTransferHolder = { - newBeneficiary: receiver.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to endorse transfer title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintNominatedToken( - owner.privateKey, - defaultTransferHolder.newBeneficiary, - tokenRegistryAddress - ); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - tokenId: tokenId, - tokenRegistry: tokenRegistry, - ...defaultTransferHolder, - }; - - const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); - const results = run(command); - const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully endorsed to approved beneficiary at `; - const queryResults = extractLine(results, frontFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(frontFormat); - const checkContext = filteredLine.includes(middleFormat); - expect(checkSuccess && checkContext).toBe(true); - const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - expect(resultTokenId).toBe(transferHolder.tokenId); - const destination = filteredLine.trim().substring(filteredLine.length - 42); - expect(destination).toBe(transferHolder.newBeneficiary); - }); -}); diff --git a/src/__tests__/token-registry/mint.e2e.test.ts b/src/__tests__/token-registry/mint.e2e.test.ts deleted file mode 100644 index 98ed5348..00000000 --- a/src/__tests__/token-registry/mint.e2e.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { deployTokenRegistry, generateTokenId } from "../fixture/e2e/utils"; -import { run } from "../fixture/e2e/shell"; -import { emoji, network, owner } from "../fixture/e2e/constants"; -import { isAddress } from "web3-utils"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { generateMintTitleEscrowCommand } from "../fixture/e2e/commands"; - -describe("deploy token-registry", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - it("should be able to mint title-escrow on token-registry", async () => { - const tokenId = generateTokenId(); - const titleEscrowParameter: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); - const results = run(command); - const tokenRegistrySuccessFormat = `${emoji.tick} success Token with hash `; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); - const splitResults = results.trim().split("\n"); - const titleEscrowAddressLine = splitResults[splitResults.length - 2]; - const titleEscrowAddress = titleEscrowAddressLine.trim().substring(115, 115 + 42); - expect(isAddress(titleEscrowAddress)).toBe(true); - }); -}); diff --git a/src/__tests__/token-registry/nominate.e2e.test.ts b/src/__tests__/token-registry/nominate.e2e.test.ts deleted file mode 100644 index 7fda263c..00000000 --- a/src/__tests__/token-registry/nominate.e2e.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -// title-escrow nominate-change-owner --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 --newOwner 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 - -import { deployTokenRegistry, generateTokenId, mintToken } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner, receiver } from "../fixture/e2e/constants"; -import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; -import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { generateNominateCommand } from "../fixture/e2e/commands"; - -describe("nominate title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - - const defaultTransferHolder = { - newBeneficiary: receiver.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to nominate title-escrow on token-registry", async () => { - const tokenId = generateTokenId(); - const titleEscrow: TokenRegistryIssueCommand = { - address: tokenRegistryAddress, - tokenId: tokenId, - ...defaultTitleEscrow, - }; - mintToken(owner.privateKey, titleEscrow); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - tokenId: titleEscrow.tokenId, - tokenRegistry: titleEscrow.address, - ...defaultTransferHolder, - }; - const command = generateNominateCommand(transferHolder, owner.privateKey); - const results = run(command); - const frontFormat = `${emoji.tick} success Transferable record with hash `; - const middleFormat = `'s holder has been successfully nominated to new owner with address `; - const queryResults = extractLine(results, frontFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(frontFormat); - const checkContext = filteredLine.includes(middleFormat); - expect(checkSuccess && checkContext).toBe(true); - const resultTokenId = filteredLine.trim().substring(frontFormat.length, frontFormat.length + 66); - expect(resultTokenId).toBe(transferHolder.tokenId); - const destination = filteredLine.trim().substring(filteredLine.length - 42); - expect(destination).toBe(transferHolder.newBeneficiary); - }); -}); diff --git a/src/__tests__/token-registry/reject-surrender.e2e.test.ts b/src/__tests__/token-registry/reject-surrender.e2e.test.ts deleted file mode 100644 index 44671292..00000000 --- a/src/__tests__/token-registry/reject-surrender.e2e.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { deployTokenRegistry, mintSurrenderToken } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner } from "../fixture/e2e/constants"; -import { generateRejectSurrenderCommand } from "../fixture/e2e/commands"; - -describe("reject surrender title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to reject surrender title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); - const results = run(command); - - const tokenRegistrySuccessFormat = `${emoji.tick} success Surrendered transferable record with hash `; - const queryResults = extractLine(results, tokenRegistrySuccessFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); - const resultTokenId = filteredLine - .trim() - .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); - expect(resultTokenId).toBe(tokenId); - }); - - it.todo("should be fail when user without permission attempts reject surrender"); -}); diff --git a/src/__tests__/token-registry/surrender.e2e.test.ts b/src/__tests__/token-registry/surrender.e2e.test.ts deleted file mode 100644 index 9cc8d7ea..00000000 --- a/src/__tests__/token-registry/surrender.e2e.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { deployTokenRegistry, mintTokenRegistry } from "../fixture/e2e/utils"; -import { extractLine, LineInfo, run } from "../fixture/e2e/shell"; -import { emoji, network, owner } from "../fixture/e2e/constants"; -import { BaseTitleEscrowCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { generateSurrenderCommand } from "../fixture/e2e/commands"; - -describe("surrender title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - const defaultTitleEscrow = { - beneficiary: owner.ethAddress, - holder: owner.ethAddress, - network: network, - dryRun: false, - }; - - it("should be able to surrender title-escrow on token-registry", async () => { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const surrenderTitleEscrow: BaseTitleEscrowCommand = { - tokenRegistry, - tokenId, - ...defaultTitleEscrow, - }; - - const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); - const results = run(command); - - const tokenRegistrySuccessFormat = `${emoji.tick} success Transferable record with hash `; - const queryResults = extractLine(results, tokenRegistrySuccessFormat); - expect(queryResults).toBeTruthy(); - const filteredLine = (queryResults as LineInfo[])[0].lineContent.trim(); - const checkSuccess = filteredLine.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); - const resultTokenId = filteredLine - .trim() - .substring(tokenRegistrySuccessFormat.length, tokenRegistrySuccessFormat.length + 66); - expect(resultTokenId).toBe(surrenderTitleEscrow.tokenId); - }); -}); diff --git a/src/commands/title-escrow/accept-surrendered.ts b/src/commands/title-escrow/accept-surrendered.ts index 2c8a426e..64af790e 100644 --- a/src/commands/title-escrow/accept-surrendered.ts +++ b/src/commands/title-escrow/accept-surrendered.ts @@ -35,6 +35,7 @@ export const handler = async (args: TitleEscrowSurrenderDocumentCommand): Promis try { info(`Accepting surrendered document`); const transaction = await acceptSurrendered(args); + console.log(transaction); success(`Surrendered transferable record with hash ${args.tokenId} has been accepted.`); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transaction.transactionHash}`); } catch (e) { diff --git a/src/e2e/accept-surrender.test.ts b/src/e2e/accept-surrender.test.ts new file mode 100644 index 00000000..f8b86e12 --- /dev/null +++ b/src/e2e/accept-surrender.test.ts @@ -0,0 +1,60 @@ +import { extractLine, LineInfo, run } from "./utils/shell"; +import { BurnAddress, EmptyTokenID, EndStatus, network, owner } from "./utils/constants"; +import { generateAcceptSurrenderCommand } from "./utils/commands"; +import { checkFailure, checkSurrenderAcceptSuccess, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; +import { getSigner, retrieveTitleEscrow, retrieveTitleEscrowInfo, retrieveTitleEscrowOwner } from "./utils/contract-checks"; + +describe("accept-surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to accept-surrender title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId, titleEscrowAddress } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); + let titleEscrowOwner: string = await retrieveTitleEscrowOwner( + signer, + tokenRegistry, + tokenId + ); + expect(titleEscrowOwner).toBe(tokenRegistry); + const results = run(command); + titleEscrowOwner = await retrieveTitleEscrowOwner( + signer, + tokenRegistry, + tokenId + ); + expect(titleEscrowOwner).toBe("0x000000000000000000000000000000000000dEaD"); + checkSurrenderAcceptSuccess(results); + }); + + it("should not be able to accept surrender invalid title-escrow on token-registry", async () => { + const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + + it("should not be able to accept surrender title-escrow on invalid token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); + + it.todo("should be fail when permission is denied"); +}); + +// npm run dev -- title-escrow accept-surrendered --token-registry 0x0000000000000000000000000000000000000000 --tokenId 0xf32907ec66ac258f058eae34d76b160b0ca520b5c613ca93ae6bb22a8f9c451f --network local -k 0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7 \ No newline at end of file diff --git a/src/e2e/change-holder.test.ts b/src/e2e/change-holder.test.ts new file mode 100644 index 00000000..12eb4060 --- /dev/null +++ b/src/e2e/change-holder.test.ts @@ -0,0 +1,103 @@ +import { extractLine, LineInfo, run } from "./utils/shell"; +import { BurnAddress, EmptyTokenID, EndStatus, network, owner, receiver } from "./utils/constants"; +import { TitleEscrowTransferHolderCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { generateChangeHolderCommand } from "./utils/commands"; +import { checkFailure, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; +import { changeHolderToken, checkChangeHolderSuccess, defaultTransferHolder } from "./utils/bootstrap"; + +describe("transfer holder title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + it("should be able to transfer holder title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowTransferHolderCommand = { + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferHolder, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + checkChangeHolderSuccess(results); + }); + + + it("holder should be able to transfer holder of title-escrow", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + // Transfer Holder to Receiver + const initialHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: receiver.ethAddress, + }; + changeHolderToken(owner.privateKey, initialHolder); + // Transfer Holder to Receiver + // Holder attempts to transfer Holder with permission + const transferHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: owner.ethAddress, + }; + const command = generateChangeHolderCommand(transferHolder, receiver.privateKey); + // Holder attempts to transfer Holder with permission + const results = run(command); + checkChangeHolderSuccess(results); + }); + + it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowTransferHolderCommand = { + tokenId: EmptyTokenID, + tokenRegistry: tokenRegistry, + ...defaultTransferHolder, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + + it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowTransferHolderCommand = { + tokenId: tokenId, + tokenRegistry: BurnAddress, + ...defaultTransferHolder, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); + + it("beneficiary should not be able to transfer holder of title-escrow", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + // Transfer Holder to Receiver + const initialHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: receiver.ethAddress, + }; + changeHolderToken(owner.privateKey, initialHolder); + // Transfer Holder to Receiver + // Beneficiary attempts to transfer Holder without permission + const transferHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: owner.ethAddress, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + // Beneficiary attempts to transfer Holder without permission + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + +}); diff --git a/src/__tests__/token-registry/deploy.e2e.test.ts b/src/e2e/deploy.test.ts similarity index 54% rename from src/__tests__/token-registry/deploy.e2e.test.ts rename to src/e2e/deploy.test.ts index 33f42ba0..48f489ab 100644 --- a/src/__tests__/token-registry/deploy.e2e.test.ts +++ b/src/e2e/deploy.test.ts @@ -1,16 +1,10 @@ -import { run } from "../fixture/e2e/shell"; -import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; +import { run } from "./utils/shell"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../commands/deploy/deploy.types"; import { isAddress } from "web3-utils"; -import { creators, emoji, network, owner } from "../fixture/e2e/constants"; -import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "../fixture/e2e/commands"; - -const defaults = { - factoryAddress: creators.titleEscrowFactory, - tokenImplementationAddress: creators.tokenImplementation, - deployerAddress: creators.deployer, - network: network, - dryRun: false, -}; +import { defaultRunParameters, EndStatus, owner } from "./utils/constants"; +import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "./utils/commands"; +import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; +import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/bootstrap"; describe("deploy token-registry", () => { jest.setTimeout(90000); @@ -19,18 +13,21 @@ describe("deploy token-registry", () => { const tokenRegistryParameter: DeployTokenRegistryCommand = { registryName: "Test Token", registrySymbol: "TKN", - ...defaults, + ...defaultTokenRegistry, }; const command = generateDeployTokenRegistryCommand(tokenRegistryParameter, owner.privateKey); const results = run(command); - const tokenRegistrySuccessFormat = `${emoji.tick} success Token registry deployed at `; - const checkSuccess = results.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); - const splitResults = results.trim().split("\n"); - const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; - const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); - expect(isAddress(tokenRegistryAddress)).toBe(true); + const tokenRegistryAddress = checkTokenRegistrySuccess(results); + const signer = getSigner(defaultRunParameters.network, owner.privateKey); + const tokenInfo = await retrieveTokenInfo(signer, tokenRegistryAddress); + expect(tokenInfo.name).toBe(tokenRegistryParameter.registryName); + expect(tokenInfo.symbol).toBe(tokenRegistryParameter.registrySymbol); + const rolesInfo = await rolesCheck(signer, tokenRegistryAddress); + expect(rolesInfo.accepterRole).toBe(true); + expect(rolesInfo.defaultRole).toBe(true); + expect(rolesInfo.minterRole).toBe(true); + expect(rolesInfo.restorerRole).toBe(true); }); }); @@ -40,12 +37,12 @@ describe("deploy document-store", () => { it("should be able to deploy document-store", async () => { const documentStoreParameters: DeployDocumentStoreCommand = { storeName: "Test Document Store", - ...defaults, + ...defaultRunParameters, }; const command = generateDeployDocumentStoreCommand(documentStoreParameters, owner.privateKey); const results = run(command); - const tokenRegistrySuccessFormat = `${emoji.tick} success Document store Test Document Store deployed at `; + const tokenRegistrySuccessFormat = `${EndStatus.success} Document store Test Document Store deployed at `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); expect(checkSuccess).toBe(true); const splitResults = results.trim().split("\n"); diff --git a/src/e2e/endorse-change-owner.test.ts b/src/e2e/endorse-change-owner.test.ts new file mode 100644 index 00000000..5cba8185 --- /dev/null +++ b/src/e2e/endorse-change-owner.test.ts @@ -0,0 +1,55 @@ +import { extractLine, LineInfo, run } from "./utils/shell"; +import { EndStatus, network, owner, receiver } from "./utils/constants"; +import { TitleEscrowEndorseTransferOfOwnersCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { generateTransferOwnersCommand } from "./utils/commands"; +import { changeHolderToken, checkEndorseOwner, checkFailure, deployTokenRegistry, mintNominatedToken, mintTokenRegistry } from "./utils/bootstrap"; + +describe("endorse change owner title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTransferOwners = { + newOwner: receiver.ethAddress, + newHolder: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to endorse change owner title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintNominatedToken( + owner.privateKey, + defaultTransferOwners.newOwner, + tokenRegistryAddress + ); + const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferOwners, + }; + const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); + const results = run(command); + const { beneficiary, holder, tokenId: tokenIdResult } = checkEndorseOwner(results); + expect(beneficiary).toBe(transferOwners.newOwner); + expect(holder).toBe(transferOwners.newHolder); + expect(tokenIdResult).toBe(transferOwners.tokenId); + }); + + it("should not be able to endorse change owner on un-nominated title-escrow", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry( + owner.privateKey, + tokenRegistryAddress + ); + const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { + tokenId: tokenId, + tokenRegistry: tokenRegistry, + ...defaultTransferOwners, + }; + const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); + const results = run(command); + checkFailure(results, "") + }); +}); diff --git a/src/e2e/endorse-transfer.test.ts b/src/e2e/endorse-transfer.test.ts new file mode 100644 index 00000000..3ad49afe --- /dev/null +++ b/src/e2e/endorse-transfer.test.ts @@ -0,0 +1,56 @@ +import { extractLine, LineInfo, run } from "./utils/shell"; +import { BurnAddress, EndStatus, network, owner, receiver } from "./utils/constants"; +import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { generateEndorseTransferOwnerCommand } from "./utils/commands"; +import { checkEndorseTransfer, checkFailure, deployTokenRegistry, mintNominatedToken, mintToken, mintTokenRegistry } from "./utils/bootstrap"; + +describe("endorse transfer title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTransferHolder = { + newBeneficiary: receiver.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to endorse transfer title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintNominatedToken( + owner.privateKey, + defaultTransferHolder.newBeneficiary, + tokenRegistryAddress + ); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferHolder, + tokenId, + tokenRegistry, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); + const results = run(command); + checkEndorseTransfer(results); + }); + + + it("should not be able to endorse un-nominated title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry( + owner.privateKey, + tokenRegistryAddress + ); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferHolder, + tokenId, + tokenRegistry, + newBeneficiary: BurnAddress + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, ""); + }); + +}); diff --git a/src/e2e/mint.test.ts b/src/e2e/mint.test.ts new file mode 100644 index 00000000..19f6c5f5 --- /dev/null +++ b/src/e2e/mint.test.ts @@ -0,0 +1,158 @@ +import { generateTokenId, isTokenId } from "./utils/token-management"; +import { extractStatus, run } from "./utils/shell"; +import { + AddressLength, + BurnAddress, + defaultRunParameters, + EndStatus, + network, + owner, + receiver, + TokenIdLength, + TokenInfo, +} from "./utils/constants"; +import { isAddress } from "web3-utils"; +import { TokenRegistryIssueCommand } from "../commands/token-registry/token-registry-command.type"; +import { generateMintTitleEscrowCommand } from "./utils/commands"; +import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; +import { BigNumber } from "ethers"; +import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/bootstrap"; + +describe("deploy token-registry", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + it("should be able to mint title-escrow on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + const mintResults = checkMintSuccess(results); + validateMintData(titleEscrowParameter as MintData, mintResults); + + const signer = await getSigner(titleEscrowParameter.network, owner.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + titleEscrowParameter.address, + titleEscrowParameter.tokenId + ); + expect(titleEscrowInfo.active).toBe(true); + expect(titleEscrowInfo.beneficiary).toBe(titleEscrowParameter.beneficiary); + expect(titleEscrowInfo.holder).toBe(titleEscrowParameter.holder); + expect(titleEscrowInfo.isHoldingToken).toBe(true); + expect(titleEscrowInfo.nominee).toBe(BurnAddress); + expect(titleEscrowInfo.registry).toBe(titleEscrowParameter.address); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); + expect(correctTokenID).toBe(true); + }); + + it("should be able to mint title-escrow for a different wallet on token-registry", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: receiver.ethAddress, + holder: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + const mintResults = checkMintSuccess(results); + validateMintData(titleEscrowParameter as MintData, mintResults); + + const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + titleEscrowParameter.address, + titleEscrowParameter.tokenId + ); + expect(titleEscrowInfo.active).toBe(true); + expect(titleEscrowInfo.beneficiary).toBe(titleEscrowParameter.beneficiary); + expect(titleEscrowInfo.holder).toBe(titleEscrowParameter.holder); + expect(titleEscrowInfo.isHoldingToken).toBe(true); + expect(titleEscrowInfo.nominee).toBe(BurnAddress); + expect(titleEscrowInfo.registry).toBe(titleEscrowParameter.address); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); + expect(correctTokenID).toBe(true); + }); + + it("should fail with invalid token id", async () => { + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: "0xZ", + beneficiary: receiver.ethAddress, + holder: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + checkFailure(results, "invalid BigNumber string"); + }); + + it("should fail with invalid token registry", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: BurnAddress, + tokenId: tokenId, + beneficiary: receiver.ethAddress, + holder: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); + + it("should fail with invalid beneficiary", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: BurnAddress, + holder: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + it("should fail with invalid holder", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: receiver.ethAddress, + holder: BurnAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + it("should fail with no funds", async () => { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + ...defaultRunParameters, + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: receiver.ethAddress, + holder: receiver.ethAddress, + network: "mainnet", + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); +}); diff --git a/src/e2e/nominate.test.ts b/src/e2e/nominate.test.ts new file mode 100644 index 00000000..664a2ec4 --- /dev/null +++ b/src/e2e/nominate.test.ts @@ -0,0 +1,109 @@ +import { run } from "./utils/shell"; +import { + BurnAddress, + defaultRunParameters, + owner, + receiver, +} from "./utils/constants"; +import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { generateNominateCommand } from "./utils/commands"; +import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; +import { checkFailure, checkNominateSuccess, defaultNominateBeneficiary, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; + +describe("nominate title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + it("should be able to nominate title-escrow on token-registry", async () => { + const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + tokenId, + tokenRegistry, + newBeneficiary: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateNominateCommand(transferHolder, owner.privateKey); + const results = run(command); + const nominateInfo = checkNominateSuccess(results); + expect(nominateInfo.tokenId).toBe(transferHolder.tokenId); + expect(nominateInfo.nominee).toBe(transferHolder.newBeneficiary); + const signer = await getSigner(transferHolder.network, owner.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); + expect(titleEscrowInfo.nominee).toBe(transferHolder.newBeneficiary); + }); + + it("should be able to cancel nomination of title-escrow on token-registry", async () => { + const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId, + tokenRegistry, + }; + let results = run(generateNominateCommand(transferHolder, owner.privateKey)); + transferHolder.newBeneficiary = BurnAddress; + const command = generateNominateCommand(transferHolder, owner.privateKey); + results = run(command); + const nominateInfo = checkNominateSuccess(results); + expect(nominateInfo.tokenId).toBe(transferHolder.tokenId); + expect(nominateInfo.nominee).toBe(transferHolder.newBeneficiary); + + const signer = await getSigner(transferHolder.network, owner.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); + expect(titleEscrowInfo.nominee).toBe(BurnAddress); + }); + + it("should not be able to nominate self", async () => { + const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId, + tokenRegistry, + newBeneficiary: owner.ethAddress, + }; + const command = generateNominateCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "new beneficiary address is the same as the current beneficiary address"); + }); + + it("should not be able to nominate unowned token", async () => { + const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId, + tokenRegistry, + newBeneficiary: receiver.ethAddress, + }; + const command = generateNominateCommand(transferHolder, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + it("should not be able to nominate non-existent token", async () => { + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", + tokenRegistry: tokenRegistryAddress, + newBeneficiary: receiver.ethAddress, + }; + const command = generateNominateCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + it("should not be able to nominate non-existent token registry", async () => { + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", + tokenRegistry: "0x0000000000000000000000000000000000000000", + newBeneficiary: receiver.ethAddress, + }; + const command = generateNominateCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); +}); diff --git a/src/e2e/reject-surrender.test.ts b/src/e2e/reject-surrender.test.ts new file mode 100644 index 00000000..07eeff8a --- /dev/null +++ b/src/e2e/reject-surrender.test.ts @@ -0,0 +1,63 @@ +// import { deployTokenRegistry, mintSurrenderToken } from "./utils/utils"; +import { extractLine, LineInfo, run } from "./utils/shell"; +import { BurnAddress, EmptyTokenID, EndStatus, network, owner } from "./utils/constants"; +import { generateRejectSurrenderCommand } from "./utils/commands"; +import { checkFailure, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; +import { checkSurrenderRejectSuccess } from "./utils/bootstrap"; +import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; +import { isAddress } from "web3-utils"; + +describe("reject surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + const defaultTitleEscrow = { + beneficiary: owner.ethAddress, + holder: owner.ethAddress, + network: network, + dryRun: false, + }; + + it("should be able to reject surrender title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); + const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); + + let titleEscrowOwner: string = await retrieveTitleEscrowOwner( + signer, + tokenRegistry, + tokenId + ); + expect(titleEscrowOwner).toBe(tokenRegistry); + const results = run(command); + checkSurrenderRejectSuccess(results); + titleEscrowOwner = await retrieveTitleEscrowOwner( + signer, + tokenRegistry, + tokenId + ); + expect(isAddress(titleEscrowOwner)).toBe(true); + expect(titleEscrowOwner).not.toBe(tokenRegistry); + }); + + it("should not be able to reject surrender invalid title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + + it("should not be able to reject surrender title-escrow on invalid token-registry", async () => { + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); + + it.todo("should be fail when user without permission attempts reject surrender"); +}); diff --git a/src/e2e/surrender.test.ts b/src/e2e/surrender.test.ts new file mode 100644 index 00000000..5ea47dfe --- /dev/null +++ b/src/e2e/surrender.test.ts @@ -0,0 +1,100 @@ +import { isTokenId } from "./utils/token-management"; +import { extractLine, extractStatus, LineInfo, run } from "./utils/shell"; +import { BurnAddress, defaultRunParameters, EndStatus, network, owner, receiver, TokenIdLength, TokenInfo } from "./utils/constants"; +import { BaseTitleEscrowCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { generateSurrenderCommand } from "./utils/commands"; +import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; +import { isAddress } from "web3-utils"; +import { deployTokenRegistry, mintTokenRegistry, checkSurrenderSuccess, checkFailure } from "./utils/bootstrap"; + + +const defaultTitleEscrow = { + ...defaultRunParameters, + beneficiary: owner.ethAddress, + holder: owner.ethAddress, +}; + + +describe("surrender title-escrow", () => { + jest.setTimeout(90000); + + let tokenRegistryAddress = ""; + beforeAll(() => { + tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + }); + + it("should be able to surrender title-escrow on token-registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry, + tokenId, + }; + const signer = await getSigner(surrenderTitleEscrow.network, owner.privateKey); + let titleEscrowOwner: string = await retrieveTitleEscrowOwner( + signer, + surrenderTitleEscrow.tokenRegistry, + surrenderTitleEscrow.tokenId + ); + expect(isAddress(titleEscrowOwner)).toBe(true); + expect(titleEscrowOwner).not.toBe(surrenderTitleEscrow.tokenRegistry); + const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); + const results = run(command); + const surrenderResults = checkSurrenderSuccess(results); + expect(surrenderResults.tokenId).toBe(surrenderTitleEscrow.tokenId); + titleEscrowOwner = await retrieveTitleEscrowOwner( + signer, + surrenderTitleEscrow.tokenRegistry, + surrenderTitleEscrow.tokenId + ); + expect(titleEscrowOwner).toBe(surrenderTitleEscrow.tokenRegistry); + }); + + it("should not surrender unowned title escrow", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry, + tokenId, + }; + const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + const signer = await getSigner(surrenderTitleEscrow.network, owner.privateKey); + const titleEscrowOwner: string = await retrieveTitleEscrowOwner( + signer, + surrenderTitleEscrow.tokenRegistry, + surrenderTitleEscrow.tokenId + ); + expect(isAddress(titleEscrowOwner)).toBe(true); + expect(titleEscrowOwner).not.toBe(surrenderTitleEscrow.tokenRegistry); + }); + + it("should not surrender invalid title escrow", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry, + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", + }; + const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + }); + + it("should not surrender invalid title escrow with invalid token registry", async () => { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry: BurnAddress, + tokenId, + }; + const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); + const results = run(command); + checkFailure(results, "null"); + }); +}); diff --git a/src/e2e/utils/bootstrap/accept-surrender.ts b/src/e2e/utils/bootstrap/accept-surrender.ts new file mode 100644 index 00000000..4db06ade --- /dev/null +++ b/src/e2e/utils/bootstrap/accept-surrender.ts @@ -0,0 +1,20 @@ + +import { EndStatus, TokenIdLength } from "../constants"; +import { extractStatus } from "../shell"; +import { isTokenId } from "../token-management"; + +export const checkSurrenderAcceptSuccess = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, + }; + }; + \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/change-holder.ts b/src/e2e/utils/bootstrap/change-holder.ts new file mode 100644 index 00000000..849ee09a --- /dev/null +++ b/src/e2e/utils/bootstrap/change-holder.ts @@ -0,0 +1,45 @@ + +import { isAddress } from "web3-utils"; +import { mintTokenRegistry } from "."; +import { TitleEscrowTransferHolderCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { generateChangeHolderCommand } from "../commands"; +import { AddressLength, BurnAddress, defaultRunParameters, EndStatus, network, receiver, TokenIdLength } from "../constants"; +import { extractStatus, run } from "../shell"; +import { isTokenId } from "../token-management"; + +// ✔ success Transferable record with hash 0x9837ba0954300cf74dea2d7ee9f294a3d1ca0ce2fb8025a3de505440971a7baf's holder has been successfully changed to holder with address: 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 + + +export const defaultTransferHolder = { + newHolder: receiver.ethAddress, + network: network, + dryRun: false, + }; + +export const checkChangeHolderSuccess = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Change Holder failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const holder = titleEscrowAddressLine.substring(173, 173 + AddressLength); + + const holderIsAddress = isAddress(holder); + const isValidTokenId = isTokenId(tokenId); + + if (!holderIsAddress) throw new Error("Invalid holder address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + newHolder: holder, + tokenId, + }; + }; + + export const changeHolderToken = (privateKey: string, transferHolder: TitleEscrowTransferHolderCommand) => { + const command = generateChangeHolderCommand(transferHolder, privateKey); + const results = run(command); + checkChangeHolderSuccess(results); + } + + + \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/common.ts b/src/e2e/utils/bootstrap/common.ts new file mode 100644 index 00000000..2ff1b25e --- /dev/null +++ b/src/e2e/utils/bootstrap/common.ts @@ -0,0 +1,9 @@ +import { EndStatus } from "../constants"; +import { extractStatus } from "../shell"; + +export const checkFailure = (results: string, expectedErrorMessage: string) => { + const statusMessage = extractStatus(results, EndStatus.error); + expect(statusMessage.length).toBeGreaterThan(0); + const errorMessage = statusMessage[0].lineContent.substring(13); + expect(errorMessage).toContain(expectedErrorMessage); +}; \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/deploy.ts b/src/e2e/utils/bootstrap/deploy.ts new file mode 100644 index 00000000..d213b537 --- /dev/null +++ b/src/e2e/utils/bootstrap/deploy.ts @@ -0,0 +1,47 @@ +import { isAddress } from "web3-utils"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "../commands"; +import { defaultRunParameters, creators, EndStatus, AddressLength } from "../constants"; +import { extractStatus, run } from "../shell"; + +export const defaultTokenRegistry = { + ...defaultRunParameters, + factoryAddress: creators.titleEscrowFactory, + tokenImplementationAddress: creators.tokenImplementation, + deployerAddress: creators.deployer, +}; + +const numberGenerator = (range: number): number => { + return Math.floor(Math.random() * range); +}; + + +export const checkTokenRegistrySuccess = (results: string): string => { + const statusLine = extractStatus(results, EndStatus.success, "Token registry deployed at "); + if (statusLine.length <= 0) throw new Error("Deployment failed"); + const tokenRegistryDeploymentLine = statusLine[0].lineContent; + const tokenRegistry = tokenRegistryDeploymentLine.substring(40, 40 + AddressLength); + + const tokenRegistryIsAddress = isAddress(tokenRegistry); + if (!tokenRegistryIsAddress) throw new Error("Invalid token registry address"); + return tokenRegistry; +}; + + +export const deployTokenRegistry = ( + privateKey: string, + tokenRegistryParameters?: DeployTokenRegistryCommand +): string => { + if (!tokenRegistryParameters) { + const index = numberGenerator(100); + tokenRegistryParameters = { + ...defaultTokenRegistry, + registryName: `Test Token ${index}`, + registrySymbol: `TKN${index}`, + }; + } + const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); + const results = run(command); + const tokenRegistryAddress = checkTokenRegistrySuccess(results); + return tokenRegistryAddress; +}; diff --git a/src/e2e/utils/bootstrap/endorse-owners.ts b/src/e2e/utils/bootstrap/endorse-owners.ts new file mode 100644 index 00000000..ecd0c883 --- /dev/null +++ b/src/e2e/utils/bootstrap/endorse-owners.ts @@ -0,0 +1,28 @@ +import { isAddress } from "web3-utils"; +import { AddressLength, EndStatus, TokenIdLength } from "../constants"; +import { extractStatus } from "../shell"; +import { isTokenId } from "../token-management"; + + export const checkEndorseOwner = (results: string) => { + // ✔ success Transferable record with hash 0xffafc068018521a42a40ba1a8b9bf574c99c69763e42e155fae498278edb8074's holder has been successfully endorsed to new owner with address 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 and new holder with address: 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const beneficiary = titleEscrowAddressLine.substring(176, 176 + AddressLength); + const holder = titleEscrowAddressLine.substring(248, 248 + AddressLength); + + const beneficiaryIsAddress = isAddress(beneficiary); + const holderIsAddress = isAddress(holder); + const isValidTokenId = isTokenId(tokenId); + + if (!beneficiaryIsAddress) throw new Error("Invalid beneficiary address"); + if (!holderIsAddress) throw new Error("Invalid holder address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + beneficiary, + holder, + tokenId, + }; + } \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/endorse-transfer.ts b/src/e2e/utils/bootstrap/endorse-transfer.ts new file mode 100644 index 00000000..090628f0 --- /dev/null +++ b/src/e2e/utils/bootstrap/endorse-transfer.ts @@ -0,0 +1,23 @@ +import { isAddress } from "web3-utils"; +import { AddressLength, EndStatus, TokenIdLength } from "../constants"; +import { extractStatus } from "../shell"; +import { isTokenId } from "../token-management"; + + export const checkEndorseTransfer = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const beneficiary = titleEscrowAddressLine.substring(177, 177 + AddressLength); + + const beneficiaryIsAddress = isAddress(beneficiary); + const isValidTokenId = isTokenId(tokenId); + + if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + beneficiary, + tokenId, + }; + } \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/index.ts b/src/e2e/utils/bootstrap/index.ts new file mode 100644 index 00000000..53aea86a --- /dev/null +++ b/src/e2e/utils/bootstrap/index.ts @@ -0,0 +1,11 @@ +export * from "./common"; +export * from "./deploy"; +export * from "./mint"; +export * from "./nominate"; +export * from "./endorse-transfer"; +export * from "./endorse-owners"; +export * from "./change-holder"; +export * from "./surrender"; +export * from "./reject-surrender"; +export * from "./accept-surrender"; + diff --git a/src/e2e/utils/bootstrap/mint.ts b/src/e2e/utils/bootstrap/mint.ts new file mode 100644 index 00000000..d4615fa1 --- /dev/null +++ b/src/e2e/utils/bootstrap/mint.ts @@ -0,0 +1,85 @@ +import { Wallet } from "ethers"; +import { isAddress } from "web3-utils"; +import { deployTokenRegistry } from "."; +import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; +import { generateMintTitleEscrowCommand } from "../commands"; +import { AddressLength, defaultRunParameters, EndStatus, TokenIdLength, TokenInfo } from "../constants"; +import { getSigner, retrieveTitleEscrowOwner } from "../contract-checks"; +import { extractStatus, run } from "../shell"; +import { generateTokenId, isTokenId } from "../token-management"; + +export interface MintData { + tokenId: string; + address: string; + beneficiary: string; + holder: string; +} + +export const validateMintData = (expectedValue: MintData, value: MintData) => { + expect(expectedValue.address).toBe(value.address); + expect(expectedValue.beneficiary).toBe(value.beneficiary); + expect(expectedValue.holder).toBe(value.holder); + expect(expectedValue.tokenId).toBe(value.tokenId); +}; + +export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { + if (!tokenRegistryAddress) { + tokenRegistryAddress = deployTokenRegistry(privateKey); + } + if (!isAddress(tokenRegistryAddress)) throw new Error("Invalid Token Registry Address"); + const wallet = new Wallet(privateKey); + const titleEscrowParameter: TokenRegistryIssueCommand = { + ...defaultRunParameters, + address: tokenRegistryAddress, + beneficiary: wallet.address, + holder: wallet.address, + tokenId: generateTokenId(), + }; + + return mintToken(privateKey, titleEscrowParameter); +}; + + +export const checkMintSuccess = (results: string): MintData => { + const statusLine = extractStatus(results, EndStatus.success, "Token with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(29, 29 + TokenIdLength); + const address = titleEscrowAddressLine.substring(115, 115 + AddressLength); + const beneficiary = titleEscrowAddressLine.substring(191, 191 + AddressLength); + const holder = titleEscrowAddressLine.substring(253, 253 + AddressLength); + + const tokenRegistryAddressIsAddress = isAddress(address); + const beneficiaryIsAddress = isAddress(beneficiary); + const holderIsAddress = isAddress(holder); + const isValidTokenId = isTokenId(tokenId); + + if (!tokenRegistryAddressIsAddress) throw new Error("Invalid token registry address"); + if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); + if (!holderIsAddress) throw new Error("Invalid holder address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + address, + beneficiary, + holder, + tokenId, + }; +}; + +export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { + const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); + const results = run(command); + const {address: tokenRegistry, tokenId, beneficiary, holder} = checkMintSuccess(results); + const signer = getSigner(titleEscrowParameter.network, privateKey); + // let titleEscrowAddress: string = retrieveTitleEscrowOwner( + // signer, + // tokenRegistry, + // tokenId, + // ); + return { + tokenRegistry, + tokenId, + // titleEscrowAddress, + }; +}; diff --git a/src/e2e/utils/bootstrap/nominate.ts b/src/e2e/utils/bootstrap/nominate.ts new file mode 100644 index 00000000..ec476111 --- /dev/null +++ b/src/e2e/utils/bootstrap/nominate.ts @@ -0,0 +1,54 @@ +import { isAddress } from "web3-utils"; +import { mintTokenRegistry } from "."; +import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { generateNominateCommand } from "../commands"; +import { AddressLength, defaultRunParameters, EndStatus, network, owner, receiver, TokenIdLength, TokenInfo } from "../constants"; +import { extractStatus, run } from "../shell"; +import { isTokenId } from "../token-management"; + +export const defaultNominateBeneficiary = { + ...defaultRunParameters, + newBeneficiary: receiver.ethAddress, +}; + +export const mintNominatedToken = (privateKey: string, nominee: string, tokenRegistryAddress?: string): TokenInfo => { + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = tokenDetails; + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newBeneficiary: nominee, + }; + nominateToken(privateKey, nominateParameter); + return tokenDetails; + }; + + + export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { + const command = generateNominateCommand(nominateParameter, privateKey); + const results = run(command); + checkNominateSuccess(results); + }; + + + +export const checkNominateSuccess = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Nomination failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const nominee = titleEscrowAddressLine.substring(177, 177 + AddressLength); + + const nomineeIsAddress = isAddress(nominee); + const isValidTokenId = isTokenId(tokenId); + + if (!nomineeIsAddress) throw new Error("Invalid nominee address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + nominee, + tokenId, + }; + }; + \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/reject-surrender.ts b/src/e2e/utils/bootstrap/reject-surrender.ts new file mode 100644 index 00000000..fef9f0ec --- /dev/null +++ b/src/e2e/utils/bootstrap/reject-surrender.ts @@ -0,0 +1,20 @@ + +import { EndStatus, TokenIdLength } from "../constants"; +import { extractStatus } from "../shell"; +import { isTokenId } from "../token-management"; + +export const checkSurrenderRejectSuccess = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, + }; + }; + \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/surrender.ts b/src/e2e/utils/bootstrap/surrender.ts new file mode 100644 index 00000000..4e215ad6 --- /dev/null +++ b/src/e2e/utils/bootstrap/surrender.ts @@ -0,0 +1,48 @@ +import { mintTokenRegistry } from "."; +import { BaseTitleEscrowCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { generateSurrenderCommand } from "../commands"; +import { defaultRunParameters, EndStatus, owner, TokenIdLength, TokenInfo } from "../constants"; +import { extractStatus, run } from "../shell"; +import { isTokenId } from "../token-management"; + +const defaultTitleEscrow = { + ...defaultRunParameters, + beneficiary: owner.ethAddress, + holder: owner.ethAddress, +}; + +export const mintSurrenderToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId, titleEscrowAddress } = tokenDetails; + const surrenderParameter: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry: tokenRegistry, + tokenId: tokenId, + }; + surrenderToken(privateKey, surrenderParameter); + return tokenDetails; + }; + + + export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { + const command = generateSurrenderCommand(surrenderParameter, privateKey); + const results = run(command); + checkSurrenderSuccess(results); + }; + + +export const checkSurrenderSuccess = (results: string) => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Nomination failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, + }; + }; + \ No newline at end of file diff --git a/src/__tests__/fixture/e2e/commands.ts b/src/e2e/utils/commands.ts similarity index 94% rename from src/__tests__/fixture/e2e/commands.ts rename to src/e2e/utils/commands.ts index c7f4c273..962d8d61 100644 --- a/src/__tests__/fixture/e2e/commands.ts +++ b/src/e2e/utils/commands.ts @@ -1,11 +1,11 @@ -import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../commands/deploy/deploy.types"; import { BaseTitleEscrowCommand, TitleEscrowEndorseTransferOfOwnersCommand, TitleEscrowNominateBeneficiaryCommand, TitleEscrowTransferHolderCommand, -} from "../../../commands/title-escrow/title-escrow-command.type"; -import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; +} from "../../commands/title-escrow/title-escrow-command.type"; +import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; const command = `npm run dev --`; diff --git a/src/__tests__/fixture/e2e/constants.ts b/src/e2e/utils/constants.ts similarity index 58% rename from src/__tests__/fixture/e2e/constants.ts rename to src/e2e/utils/constants.ts index c0f94982..7b0a1ca9 100644 --- a/src/__tests__/fixture/e2e/constants.ts +++ b/src/e2e/utils/constants.ts @@ -1,5 +1,6 @@ +// import { NonceManager } from "@ethersproject/experimental"; import { constants } from "@govtechsg/token-registry"; - +import { providers, Wallet } from "ethers"; export const { contractAddress } = constants; export const network = "local"; @@ -8,6 +9,9 @@ export const forkedNetwork = 5; export const mnemonic = "indicate swing place chair flight used hammer soon photo region volume shuffle"; +export const BurnAddress = "0x0000000000000000000000000000000000000000"; +export const EmptyTokenID = "0x0000000000000000000000000000000000000000000000000000000000000000"; + export const owner = { ethAddress: "0xe0A71284EF59483795053266CB796B65E48B5124", publicKey: "0x02de2454a05cdb55780b85c04128233e31ac9179235607e4d6fa0c6b38140fb51a", @@ -26,10 +30,30 @@ export const creators = { tokenImplementation: contractAddress.TokenImplementation[forkedNetwork], }; -export const emoji = { - tick: "✔", - cross: "✖", -}; +export const EndStatus = { + success: "✔ success", + error: "✖ error", +} as const; + +export const TokenIdLength = 66; +export const AddressLength = 42; -export const silent = true; +export type EndStatusType = typeof EndStatus[keyof typeof EndStatus]; + +export const silent = false; export const verbose = false; +export const defaultRunParameters = { + network: network, + dryRun: false, +}; + +export interface TokenInfo { + tokenRegistry: string; + tokenId: string; + titleEscrowAddress?: string; +} + +export interface Owners { + owner: string; + holder: string; +} diff --git a/src/e2e/utils/contract-checks.ts b/src/e2e/utils/contract-checks.ts new file mode 100644 index 00000000..f344eea9 --- /dev/null +++ b/src/e2e/utils/contract-checks.ts @@ -0,0 +1,170 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { constants } from "@govtechsg/token-registry"; +import { + TitleEscrow__factory, + TradeTrustToken, + TradeTrustToken__factory, +} from "@govtechsg/token-registry/dist/contracts"; +import { BigNumber, Signer, Wallet } from "ethers"; +import { getSupportedNetwork } from "../../commands/networks"; +import { addAddressPrefix } from "../../utils"; +import { TokenInfo } from "./constants"; + +type SignerOrProvider = Signer | Provider; + +export interface TokenRegistryInfo { + name: string; + symbol: string; +} + +export interface TitleEscrowInfo { + titleEscrow: string; + beneficiary: string; + holder: string; + nominee: string; + active: boolean; + tokenId: BigNumber; + registry: string; + isHoldingToken: boolean; +} + +export interface RolesInfo { + minterRole: boolean; + accepterRole: boolean; + restorerRole: boolean; + defaultRole: boolean; +} + +export const retrieveWalletEscrowDetails = async ( + signer: Signer, + tokenInfo: TokenInfo +): Promise => { + const escrowDetails = await retrieveEscrowDetails(signer, tokenInfo); + const titleEscrowDetails = await rolesCheck(signer, tokenInfo.tokenRegistry); + return { + ...escrowDetails, + ...titleEscrowDetails, + }; +}; + +export const retrieveEscrowDetails = async ( + signerOrProvider: SignerOrProvider, + tokenInfo: TokenInfo +): Promise => { + //TODO: check ownership + const { tokenRegistry, tokenId, titleEscrowAddress } = tokenInfo; + if(!titleEscrowAddress) throw new Error("Escrow Address unspecified"); + const tokenDetails = await retrieveTokenInfo(signerOrProvider, tokenRegistry); + let titleEscrowDetails: TitleEscrowInfo | undefined; + if (!titleEscrowAddress) { + titleEscrowDetails = await retrieveTitleEscrowInfo(signerOrProvider, titleEscrowAddress); + } else { + titleEscrowDetails = await retrieveTitleEscrow(signerOrProvider, tokenRegistry, tokenId); + } + return { + ...tokenDetails, + ...titleEscrowDetails, + }; +}; + +export const retrieveTokenInfo = async ( + signerOrProvider: SignerOrProvider, + tokenRegistry: string +): Promise => { + const token: TradeTrustToken = TradeTrustToken__factory.connect(tokenRegistry, signerOrProvider); + const namePromise = token.name(); + const symbolPromise = token.symbol(); + const [name, symbol] = await Promise.all([namePromise, symbolPromise]); + return { + name, + symbol, + }; +}; + +export const retrieveTitleEscrow = async ( + signerOrProvider: SignerOrProvider, + tokenRegistry: string, + tokenId: string +): Promise => { + const token: TradeTrustToken = TradeTrustToken__factory.connect(tokenRegistry, signerOrProvider); + const escrowAddress = await token.ownerOf(tokenId); + return retrieveTitleEscrowInfo(signerOrProvider, escrowAddress); +}; + +export const retrieveTitleEscrowOwner = async ( + signerOrProvider: SignerOrProvider, + tokenRegistry: string, + tokenId: string +): Promise => { + const token: TradeTrustToken = TradeTrustToken__factory.connect(tokenRegistry, signerOrProvider); + const escrowAddress = await token.ownerOf(tokenId); + return escrowAddress; +}; + +export const retrieveTitleEscrowInfo = async ( + signerOrProvider: SignerOrProvider, + titleEscrowAddress: string +): Promise => { + const titleEscrow = TitleEscrow__factory.connect(titleEscrowAddress, signerOrProvider); + const beneficiaryPromise: Promise = titleEscrow.beneficiary(); + const holderPromise: Promise = titleEscrow.holder(); + const nomineePromise: Promise = titleEscrow.nominee(); + const activePromise: Promise = titleEscrow.active(); + const tokenIdPromise: Promise = titleEscrow.tokenId(); + const registryPromise: Promise = titleEscrow.registry(); + const isHoldingTokenPromise: Promise = titleEscrow.isHoldingToken(); + + const [beneficiary, holder, nominee, active, tokenId, registry, isHoldingToken] = await Promise.all([ + beneficiaryPromise, + holderPromise, + nomineePromise, + activePromise, + tokenIdPromise, + registryPromise, + isHoldingTokenPromise, + ]); + return { + titleEscrow: titleEscrowAddress, + beneficiary, + holder, + nominee, + active, + tokenId, + registry, + isHoldingToken, + }; +}; + +export const rolesCheck = async (signerOrProvider: Signer, tokenRegistry: string): Promise => { + const token: TradeTrustToken = TradeTrustToken__factory.connect(tokenRegistry, signerOrProvider); + const walletAddress: string = await signerOrProvider.getAddress(); + const minterPromise = token.hasRole(constants.roleHash.MinterRole, walletAddress); + const accepterPromise = token.hasRole(constants.roleHash.AccepterRole, walletAddress); + const restorerPromise = token.hasRole(constants.roleHash.RestorerRole, walletAddress); + const defaultPromise = token.hasRole(constants.roleHash.DefaultAdmin, walletAddress); + const [minterRole, accepterRole, restorerRole, defaultRole] = await Promise.all([ + minterPromise, + accepterPromise, + restorerPromise, + defaultPromise, + ]); + return { + minterRole, + accepterRole, + restorerRole, + defaultRole, + }; +}; + +export const getSigner = (network: string, privateKey: string): Signer => { + return getSignerOrProvider(network, privateKey) as Signer; +}; + +export const getSignerOrProvider = (network: string, privateKey?: string): SignerOrProvider => { + const provider = getSupportedNetwork(network ?? "local").provider(); + if (privateKey) { + const hexlifiedPrivateKey = addAddressPrefix(privateKey); + return new Wallet(hexlifiedPrivateKey, provider); + } + return provider; +}; diff --git a/src/__tests__/fixture/e2e/shell.ts b/src/e2e/utils/shell.ts similarity index 71% rename from src/__tests__/fixture/e2e/shell.ts rename to src/e2e/utils/shell.ts index e33a3fb3..10aaa9c5 100644 --- a/src/__tests__/fixture/e2e/shell.ts +++ b/src/e2e/utils/shell.ts @@ -1,6 +1,6 @@ import shell, { ShellString } from "shelljs"; import { log } from "signale"; -import { emoji, verbose } from "./constants"; +import { EndStatus, verbose, silent as globalSilent, EndStatusType } from "./constants"; shell.config.silent = true; export interface LineInfo { @@ -8,25 +8,28 @@ export interface LineInfo { lineContent: string; } -export const run = (command: string, silent = false): string => { - shell.config.silent = !(!silent && verbose); +export const run = (command: string, silent = true): string => { + silent = silent && globalSilent; const rawResults: ShellString = shell.exec(command); const results = stripAnsi(rawResults.trim()); if (!silent) { - const success = `${emoji.tick} success`; - const failure = `${emoji.cross} error`; - let successLines = extractLine(results, success); - if (!successLines) successLines = []; - let failureLines = extractLine(results, failure); - if (!failureLines) failureLines = []; - const statusLines: LineInfo[] = [...successLines, ...failureLines]; + const successLines = extractStatus(results, EndStatus.success); + const failureLines = extractStatus(results, EndStatus.error); + const statusLines = [...successLines, ...failureLines]; log(command); printLines(statusLines); - if (verbose && failureLines.length > 0) log(rawResults); + if (verbose && failureLines.length > 0) log(results); } return results; }; +export const extractStatus = (results: string, expectedStatus: EndStatusType, expectedMessage = ""): LineInfo[] => { + const expectedString = `${expectedStatus} ${expectedMessage}`; + let lineInfo = extractLine(results, expectedString); + if (!lineInfo) lineInfo = []; + return lineInfo; +}; + export const printLines = (lines: LineInfo[]): void => { lines.sort((a: LineInfo, b: LineInfo): number => { return a.lineNumber - b.lineNumber; diff --git a/src/e2e/utils/token-management.ts b/src/e2e/utils/token-management.ts new file mode 100644 index 00000000..c5a43384 --- /dev/null +++ b/src/e2e/utils/token-management.ts @@ -0,0 +1,22 @@ +const retries = 10; +const usedTokenIds = new Set(); +export const generateTokenId = (): string => { + for (let count = 0; count < retries; count = count + 1) { + const generatedTokenId = `0x${[...Array(64)].map(() => Math.floor(Math.random() * 16).toString(16)).join("")}`; + const unique = !usedTokenIds.has(generatedTokenId); + if (unique) { + usedTokenIds.add(generatedTokenId); + return generatedTokenId; + } + } + throw new Error("Unable to generate tokenIds"); +}; + +export const isTokenId = (tokenId: string) => { + const hexRegex = /[0-9A-Fa-f]{6}/g; + tokenId = tokenId.trim(); + const containsHexPrefix = tokenId.substring(0, 2) === "0x"; + if (containsHexPrefix) tokenId = tokenId.substring(2); + if (tokenId.length <= 0) return false; + return tokenId.match(hexRegex); +}; From 1a2c73441005cb422a142f1dee6004fde9e9e32f Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Mon, 9 Jan 2023 17:41:37 +0800 Subject: [PATCH 07/20] feat: remove jest --- .circleci/config.yml | 3 + .gitignore | 3 +- package-lock.json | 54 +++++++++++++ package.json | 3 + ...render.test.ts => accept-surrender.e2e.ts} | 39 +++++----- src/e2e/all.ts | 22 ++++++ ...ge-holder.test.ts => change-holder.e2e.ts} | 39 +++++----- src/e2e/{deploy.test.ts => deploy.e2e.ts} | 42 +++++----- ...er.test.ts => endorse-change-owner.e2e.ts} | 29 +++---- ...ansfer.test.ts => endorse-transfer.e2e.ts} | 23 +++--- src/e2e/{mint.test.ts => mint.e2e.ts} | 76 ++++++++++--------- src/e2e/{nominate.test.ts => nominate.e2e.ts} | 55 ++++++++------ ...render.test.ts => reject-surrender.e2e.ts} | 36 ++++----- .../{surrender.test.ts => surrender.e2e.ts} | 39 +++++----- src/e2e/utils/bootstrap/common.ts | 6 +- src/e2e/utils/bootstrap/mint.ts | 8 +- src/e2e/utils/constants.ts | 2 +- 17 files changed, 289 insertions(+), 190 deletions(-) rename src/e2e/{accept-surrender.test.ts => accept-surrender.e2e.ts} (70%) create mode 100644 src/e2e/all.ts rename src/e2e/{change-holder.test.ts => change-holder.e2e.ts} (83%) rename src/e2e/{deploy.test.ts => deploy.e2e.ts} (57%) rename src/e2e/{endorse-change-owner.test.ts => endorse-change-owner.e2e.ts} (68%) rename src/e2e/{endorse-transfer.test.ts => endorse-transfer.e2e.ts} (78%) rename src/e2e/{mint.test.ts => mint.e2e.ts} (65%) rename src/e2e/{nominate.test.ts => nominate.e2e.ts} (72%) rename src/e2e/{reject-surrender.test.ts => reject-surrender.e2e.ts} (68%) rename src/e2e/{surrender.test.ts => surrender.e2e.ts} (76%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a55a07b..ff0ba4dc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,6 +25,9 @@ jobs: - run: name: test command: npm run test -- --runInBand + - run: + name: e2e + command: npm run e2e - run: name: benchmark wrap functionality command: npm run benchmark diff --git a/.gitignore b/.gitignore index ab1e9684..6485857d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ yarn.lock /tmp key /.vscode/ -/vc-test-suite \ No newline at end of file +/vc-test-suite +.DS_Store \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2cbcf9f7..5c59f67c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6374,6 +6374,48 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, "configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -6717,6 +6759,12 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "dev": true + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -16082,6 +16130,12 @@ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, "spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", diff --git a/package.json b/package.json index 10e83e70..331ecbe5 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "scripts": { "dev": "ts-node src/index.ts", + "e2e": "concurrently -k -s first \"npm:blockchain\" \"npm:e2e-test\"", "build": "npm run clean && npm run build:cjs && npm run build:type", "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", @@ -19,6 +20,7 @@ "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", + "e2e-test": "ts-node src/e2e/all.ts", "lint": "eslint . --ext .ts --max-warnings 0", "lint:fix": "eslint . --ext .ts --fix", "benchmark:make-certs": "./scripts/makeCerts.sh 20000", @@ -56,6 +58,7 @@ "@typescript-eslint/eslint-plugin": "^4.24.0", "@typescript-eslint/parser": "^4.24.0", "commitizen": "^4.2.4", + "concurrently": "^6.2.0", "eslint": "^7.26.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-jest": "^24.3.6", diff --git a/src/e2e/accept-surrender.test.ts b/src/e2e/accept-surrender.e2e.ts similarity index 70% rename from src/e2e/accept-surrender.test.ts rename to src/e2e/accept-surrender.e2e.ts index f8b86e12..3c4ce096 100644 --- a/src/e2e/accept-surrender.test.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -4,14 +4,11 @@ import { generateAcceptSurrenderCommand } from "./utils/commands"; import { checkFailure, checkSurrenderAcceptSuccess, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; import { getSigner, retrieveTitleEscrow, retrieveTitleEscrowInfo, retrieveTitleEscrowOwner } from "./utils/contract-checks"; -describe("accept-surrender title-escrow", () => { - jest.setTimeout(90000); - - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - +// describe("accept-surrender title-escrow", () => { +export const acceptSurrender = async () => { + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + // const errors: Error[] = []; const defaultTitleEscrow = { beneficiary: owner.ethAddress, holder: owner.ethAddress, @@ -19,7 +16,8 @@ describe("accept-surrender title-escrow", () => { dryRun: false, }; - it("should be able to accept-surrender title-escrow on token-registry", async () => { + // it("should be able to accept-surrender title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId, titleEscrowAddress } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); @@ -28,33 +26,36 @@ describe("accept-surrender title-escrow", () => { tokenRegistry, tokenId ); - expect(titleEscrowOwner).toBe(tokenRegistry); + if(!(titleEscrowOwner === tokenRegistry)) throw new Error(`!(titleEscrowOwner === tokenRegistry)`); + const results = run(command); titleEscrowOwner = await retrieveTitleEscrowOwner( signer, tokenRegistry, tokenId ); - expect(titleEscrowOwner).toBe("0x000000000000000000000000000000000000dEaD"); + if(!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")) throw new Error(`!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")`); checkSurrenderAcceptSuccess(results); - }); + } - it("should not be able to accept surrender invalid title-escrow on token-registry", async () => { + // it("should not be able to accept surrender invalid title-escrow on token-registry", async () => { + { const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should not be able to accept surrender title-escrow on invalid token-registry", async () => { + // it("should not be able to accept surrender title-escrow on invalid token-registry", async () => { + { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); - - it.todo("should be fail when permission is denied"); -}); + } + // return errors; + // it.todo("should be fail when permission is denied"); +}; // npm run dev -- title-escrow accept-surrendered --token-registry 0x0000000000000000000000000000000000000000 --tokenId 0xf32907ec66ac258f058eae34d76b160b0ca520b5c613ca93ae6bb22a8f9c451f --network local -k 0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7 \ No newline at end of file diff --git a/src/e2e/all.ts b/src/e2e/all.ts new file mode 100644 index 00000000..1311e709 --- /dev/null +++ b/src/e2e/all.ts @@ -0,0 +1,22 @@ +import { acceptSurrender } from './accept-surrender.e2e' +import { surrender } from './surrender.e2e' +import { changeHolder } from './change-holder.e2e' +import { deployDocumentStore, deployTokenRegistry } from './deploy.e2e'; +import { endorseTransfer } from './endorse-transfer.e2e'; +import { mint } from './mint.e2e'; +import { nominate } from './nominate.e2e'; +import { rejectSurrender } from './reject-surrender.e2e'; + +const awaitForDuration = async (runFunction: Function) => { + await runFunction(); +} + +awaitForDuration(surrender); +awaitForDuration(acceptSurrender); +awaitForDuration(deployDocumentStore); +awaitForDuration(deployTokenRegistry); +awaitForDuration(changeHolder); +awaitForDuration(endorseTransfer); +awaitForDuration(mint); +awaitForDuration(nominate); +awaitForDuration(rejectSurrender); \ No newline at end of file diff --git a/src/e2e/change-holder.test.ts b/src/e2e/change-holder.e2e.ts similarity index 83% rename from src/e2e/change-holder.test.ts rename to src/e2e/change-holder.e2e.ts index 12eb4060..2dd672e1 100644 --- a/src/e2e/change-holder.test.ts +++ b/src/e2e/change-holder.e2e.ts @@ -5,15 +5,14 @@ import { generateChangeHolderCommand } from "./utils/commands"; import { checkFailure, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; import { changeHolderToken, checkChangeHolderSuccess, defaultTransferHolder } from "./utils/bootstrap"; -describe("transfer holder title-escrow", () => { - jest.setTimeout(90000); +// describe("transfer holder title-escrow", () => { +export const changeHolder = async () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - it("should be able to transfer holder title-escrow on token-registry", async () => { + // it("should be able to transfer holder title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: tokenId, @@ -23,10 +22,11 @@ describe("transfer holder title-escrow", () => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); checkChangeHolderSuccess(results); - }); + } - it("holder should be able to transfer holder of title-escrow", async () => { + // it("holder should be able to transfer holder of title-escrow", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver const initialHolder: TitleEscrowTransferHolderCommand = { @@ -48,9 +48,10 @@ describe("transfer holder title-escrow", () => { // Holder attempts to transfer Holder with permission const results = run(command); checkChangeHolderSuccess(results); - }); + } - it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + // it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: EmptyTokenID, @@ -60,10 +61,11 @@ describe("transfer holder title-escrow", () => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + // it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: tokenId, @@ -73,9 +75,10 @@ describe("transfer holder title-escrow", () => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); + } - it("beneficiary should not be able to transfer holder of title-escrow", async () => { + // it("beneficiary should not be able to transfer holder of title-escrow", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver const initialHolder: TitleEscrowTransferHolderCommand = { @@ -97,7 +100,5 @@ describe("transfer holder title-escrow", () => { // Beneficiary attempts to transfer Holder without permission const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); - - -}); + } +}; diff --git a/src/e2e/deploy.test.ts b/src/e2e/deploy.e2e.ts similarity index 57% rename from src/e2e/deploy.test.ts rename to src/e2e/deploy.e2e.ts index 48f489ab..b783fab1 100644 --- a/src/e2e/deploy.test.ts +++ b/src/e2e/deploy.e2e.ts @@ -6,10 +6,12 @@ import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/bootstrap"; -describe("deploy token-registry", () => { - jest.setTimeout(90000); +export const deployTokenRegistry = async () => { +// describe("deploy token-registry", () => { + // jest.setTimeout(90000); - it("should be able to deploy token-registry", async () => { + // it("should be able to deploy token-registry", async () => { + { const tokenRegistryParameter: DeployTokenRegistryCommand = { registryName: "Test Token", registrySymbol: "TKN", @@ -21,20 +23,21 @@ describe("deploy token-registry", () => { const tokenRegistryAddress = checkTokenRegistrySuccess(results); const signer = getSigner(defaultRunParameters.network, owner.privateKey); const tokenInfo = await retrieveTokenInfo(signer, tokenRegistryAddress); - expect(tokenInfo.name).toBe(tokenRegistryParameter.registryName); - expect(tokenInfo.symbol).toBe(tokenRegistryParameter.registrySymbol); + if(!(tokenInfo.name === tokenRegistryParameter.registryName)){ throw new Error("tokenInfo.name === tokenRegistryParameter.registryName")}; + if(!(tokenInfo.symbol === tokenRegistryParameter.registrySymbol)){ throw new Error("tokenInfo.symbol === tokenRegistryParameter.registrySymbol")}; const rolesInfo = await rolesCheck(signer, tokenRegistryAddress); - expect(rolesInfo.accepterRole).toBe(true); - expect(rolesInfo.defaultRole).toBe(true); - expect(rolesInfo.minterRole).toBe(true); - expect(rolesInfo.restorerRole).toBe(true); - }); -}); + if(!(rolesInfo.accepterRole === true)){ throw new Error("rolesInfo.accepterRole === true")}; + if(!(rolesInfo.defaultRole === true)){ throw new Error("rolesInfo.defaultRole === true")}; + if(!(rolesInfo.minterRole === true)){ throw new Error("rolesInfo.minterRole === true")}; + if(!(rolesInfo.restorerRole === true)){ throw new Error("rolesInfo.restorerRole === true")}; + } +} -describe("deploy document-store", () => { - jest.setTimeout(90000); - - it("should be able to deploy document-store", async () => { +// describe("deploy document-store", () => { +export const deployDocumentStore = () => { + // jest.setTimeout(90000); + // it("should be able to deploy document-store", async () => + { const documentStoreParameters: DeployDocumentStoreCommand = { storeName: "Test Document Store", ...defaultRunParameters, @@ -44,10 +47,11 @@ describe("deploy document-store", () => { const results = run(command); const tokenRegistrySuccessFormat = `${EndStatus.success} Document store Test Document Store deployed at `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); - expect(checkSuccess).toBe(true); + if(!(checkSuccess === true)) throw new Error(`checkSuccess === true)`); const splitResults = results.trim().split("\n"); const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); - expect(isAddress(tokenRegistryAddress)).toBe(true); - }); -}); + if(!(isAddress(tokenRegistryAddress) === true)) throw new Error(`isAddress(tokenRegistryAddress) === true)`); + if(!(isAddress(tokenRegistryAddress) === true)){ throw new Error("isAddress(tokenRegistryAddress) === true")}; + } +} diff --git a/src/e2e/endorse-change-owner.test.ts b/src/e2e/endorse-change-owner.e2e.ts similarity index 68% rename from src/e2e/endorse-change-owner.test.ts rename to src/e2e/endorse-change-owner.e2e.ts index 5cba8185..2f30c756 100644 --- a/src/e2e/endorse-change-owner.test.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -4,13 +4,12 @@ import { TitleEscrowEndorseTransferOfOwnersCommand } from "../commands/title-esc import { generateTransferOwnersCommand } from "./utils/commands"; import { changeHolderToken, checkEndorseOwner, checkFailure, deployTokenRegistry, mintNominatedToken, mintTokenRegistry } from "./utils/bootstrap"; -describe("endorse change owner title-escrow", () => { - jest.setTimeout(90000); +// describe("endorse change owner title-escrow", () => { +export const endorseChangeOwner = () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTransferOwners = { newOwner: receiver.ethAddress, @@ -19,7 +18,8 @@ describe("endorse change owner title-escrow", () => { dryRun: false, }; - it("should be able to endorse change owner title-escrow on token-registry", async () => { + // it("should be able to endorse change owner title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, defaultTransferOwners.newOwner, @@ -33,12 +33,13 @@ describe("endorse change owner title-escrow", () => { const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); const { beneficiary, holder, tokenId: tokenIdResult } = checkEndorseOwner(results); - expect(beneficiary).toBe(transferOwners.newOwner); - expect(holder).toBe(transferOwners.newHolder); - expect(tokenIdResult).toBe(transferOwners.tokenId); - }); + if(!(beneficiary === transferOwners.newOwner)) throw new Error(`beneficiary === transferOwners.newOwner`); + if(!(holder === transferOwners.newHolder)) throw new Error(`holder === transferOwners.newHolder`); + if(!(tokenIdResult === transferOwners.tokenId)) throw new Error(`tokenIdResult === transferOwners.tokenId`); + } - it("should not be able to endorse change owner on un-nominated title-escrow", async () => { + // it("should not be able to endorse change owner on un-nominated title-escrow", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry( owner.privateKey, tokenRegistryAddress @@ -51,5 +52,5 @@ describe("endorse change owner title-escrow", () => { const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); checkFailure(results, "") - }); -}); + } +} diff --git a/src/e2e/endorse-transfer.test.ts b/src/e2e/endorse-transfer.e2e.ts similarity index 78% rename from src/e2e/endorse-transfer.test.ts rename to src/e2e/endorse-transfer.e2e.ts index 3ad49afe..01badae4 100644 --- a/src/e2e/endorse-transfer.test.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -4,13 +4,12 @@ import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/ import { generateEndorseTransferOwnerCommand } from "./utils/commands"; import { checkEndorseTransfer, checkFailure, deployTokenRegistry, mintNominatedToken, mintToken, mintTokenRegistry } from "./utils/bootstrap"; -describe("endorse transfer title-escrow", () => { - jest.setTimeout(90000); +// describe("endorse transfer title-escrow", () => { +export const endorseTransfer = async () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTransferHolder = { newBeneficiary: receiver.ethAddress, @@ -18,7 +17,8 @@ describe("endorse transfer title-escrow", () => { dryRun: false, }; - it("should be able to endorse transfer title-escrow on token-registry", async () => { + // it("should be able to endorse transfer title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, defaultTransferHolder.newBeneficiary, @@ -33,10 +33,11 @@ describe("endorse transfer title-escrow", () => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); checkEndorseTransfer(results); - }); + } - it("should not be able to endorse un-nominated title-escrow on token-registry", async () => { + // it("should not be able to endorse un-nominated title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry( owner.privateKey, tokenRegistryAddress @@ -51,6 +52,6 @@ describe("endorse transfer title-escrow", () => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, ""); - }); + } -}); +} diff --git a/src/e2e/mint.test.ts b/src/e2e/mint.e2e.ts similarity index 65% rename from src/e2e/mint.test.ts rename to src/e2e/mint.e2e.ts index 19f6c5f5..e0c5086e 100644 --- a/src/e2e/mint.test.ts +++ b/src/e2e/mint.e2e.ts @@ -18,15 +18,15 @@ import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/bootstrap"; -describe("deploy token-registry", () => { - jest.setTimeout(90000); +export const mint = async () => { +// describe("deploy token-registry", () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - it("should be able to mint title-escrow on token-registry", async () => { + // it("should be able to mint title-escrow on token-registry", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, @@ -46,17 +46,18 @@ describe("deploy token-registry", () => { titleEscrowParameter.address, titleEscrowParameter.tokenId ); - expect(titleEscrowInfo.active).toBe(true); - expect(titleEscrowInfo.beneficiary).toBe(titleEscrowParameter.beneficiary); - expect(titleEscrowInfo.holder).toBe(titleEscrowParameter.holder); - expect(titleEscrowInfo.isHoldingToken).toBe(true); - expect(titleEscrowInfo.nominee).toBe(BurnAddress); - expect(titleEscrowInfo.registry).toBe(titleEscrowParameter.address); + if(!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if(!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if(!(titleEscrowInfo.holder === titleEscrowParameter.holder)) throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if(!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if(!(titleEscrowInfo.registry === titleEscrowParameter.address)) throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); - expect(correctTokenID).toBe(true); - }); + if(!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } - it("should be able to mint title-escrow for a different wallet on token-registry", async () => { + // it("should be able to mint title-escrow for a different wallet on token-registry", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, @@ -76,17 +77,18 @@ describe("deploy token-registry", () => { titleEscrowParameter.address, titleEscrowParameter.tokenId ); - expect(titleEscrowInfo.active).toBe(true); - expect(titleEscrowInfo.beneficiary).toBe(titleEscrowParameter.beneficiary); - expect(titleEscrowInfo.holder).toBe(titleEscrowParameter.holder); - expect(titleEscrowInfo.isHoldingToken).toBe(true); - expect(titleEscrowInfo.nominee).toBe(BurnAddress); - expect(titleEscrowInfo.registry).toBe(titleEscrowParameter.address); + if(!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if(!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if(!(titleEscrowInfo.holder === titleEscrowParameter.holder)) throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if(!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if(!(titleEscrowInfo.registry === titleEscrowParameter.address)) throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); - expect(correctTokenID).toBe(true); - }); + if(!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } - it("should fail with invalid token id", async () => { + // it("should fail with invalid token id", async () => { + { const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, tokenId: "0xZ", @@ -97,9 +99,10 @@ describe("deploy token-registry", () => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); checkFailure(results, "invalid BigNumber string"); - }); + } - it("should fail with invalid token registry", async () => { + // it("should fail with invalid token registry", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { address: BurnAddress, @@ -111,9 +114,10 @@ describe("deploy token-registry", () => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); + } - it("should fail with invalid beneficiary", async () => { + // it("should fail with invalid beneficiary", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, @@ -125,9 +129,10 @@ describe("deploy token-registry", () => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should fail with invalid holder", async () => { + // it("should fail with invalid holder", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, @@ -139,9 +144,10 @@ describe("deploy token-registry", () => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should fail with no funds", async () => { + // it("should fail with no funds", async () => { + { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { ...defaultRunParameters, @@ -154,5 +160,5 @@ describe("deploy token-registry", () => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); -}); + } +} diff --git a/src/e2e/nominate.test.ts b/src/e2e/nominate.e2e.ts similarity index 72% rename from src/e2e/nominate.test.ts rename to src/e2e/nominate.e2e.ts index 664a2ec4..493ecc41 100644 --- a/src/e2e/nominate.test.ts +++ b/src/e2e/nominate.e2e.ts @@ -10,15 +10,15 @@ import { generateNominateCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { checkFailure, checkNominateSuccess, defaultNominateBeneficiary, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; -describe("nominate title-escrow", () => { - jest.setTimeout(90000); +export const nominate = async () => { +// describe("nominate title-escrow", () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - it("should be able to nominate title-escrow on token-registry", async () => { + // it("should be able to nominate title-escrow on token-registry", async () => { + { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { tokenId, @@ -29,14 +29,15 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); const nominateInfo = checkNominateSuccess(results); - expect(nominateInfo.tokenId).toBe(transferHolder.tokenId); - expect(nominateInfo.nominee).toBe(transferHolder.newBeneficiary); + if(!(nominateInfo.tokenId === transferHolder.tokenId)) throw new Error (`nominateInfo.tokenId === transferHolder.tokenId`); + if(!(nominateInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`nominateInfo.nominee === transferHolder.newBeneficiary`); const signer = await getSigner(transferHolder.network, owner.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); - expect(titleEscrowInfo.nominee).toBe(transferHolder.newBeneficiary); - }); + if(!(titleEscrowInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`titleEscrowInfo.nominee === transferHolder.newBeneficiary`); + } - it("should be able to cancel nomination of title-escrow on token-registry", async () => { + // it("should be able to cancel nomination of title-escrow on token-registry", async () => { + { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -48,15 +49,16 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, owner.privateKey); results = run(command); const nominateInfo = checkNominateSuccess(results); - expect(nominateInfo.tokenId).toBe(transferHolder.tokenId); - expect(nominateInfo.nominee).toBe(transferHolder.newBeneficiary); + if(!(nominateInfo.tokenId === transferHolder.tokenId)) throw new Error (`nominateInfo.tokenId === transferHolder.tokenId`); + if(!(nominateInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`nominateInfo.nominee === transferHolder.newBeneficiary`); const signer = await getSigner(transferHolder.network, owner.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); - expect(titleEscrowInfo.nominee).toBe(BurnAddress); - }); + if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error (`titleEscrowInfo.nominee === BurnAddress`); + } - it("should not be able to nominate self", async () => { + // it("should not be able to nominate self", async () => { + { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -67,9 +69,10 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, "new beneficiary address is the same as the current beneficiary address"); - }); + } - it("should not be able to nominate unowned token", async () => { + // it("should not be able to nominate unowned token", async () => { + { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -80,9 +83,10 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, receiver.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should not be able to nominate non-existent token", async () => { + // it("should not be able to nominate non-existent token", async () => { + { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -93,9 +97,10 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should not be able to nominate non-existent token registry", async () => { + // it("should not be able to nominate non-existent token registry", async () => { + { const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -105,5 +110,5 @@ describe("nominate title-escrow", () => { const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); -}); + } +} diff --git a/src/e2e/reject-surrender.test.ts b/src/e2e/reject-surrender.e2e.ts similarity index 68% rename from src/e2e/reject-surrender.test.ts rename to src/e2e/reject-surrender.e2e.ts index 07eeff8a..0c8b67e3 100644 --- a/src/e2e/reject-surrender.test.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -7,13 +7,12 @@ import { checkSurrenderRejectSuccess } from "./utils/bootstrap"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; -describe("reject surrender title-escrow", () => { - jest.setTimeout(90000); +export const rejectSurrender = async () => { +// describe("reject surrender title-escrow", () => { + // jest.setTimeout(90000); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); + + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTitleEscrow = { beneficiary: owner.ethAddress, @@ -22,7 +21,8 @@ describe("reject surrender title-escrow", () => { dryRun: false, }; - it("should be able to reject surrender title-escrow on token-registry", async () => { + // it("should be able to reject surrender title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); @@ -32,7 +32,7 @@ describe("reject surrender title-escrow", () => { tokenRegistry, tokenId ); - expect(titleEscrowOwner).toBe(tokenRegistry); + if(!(titleEscrowOwner === tokenRegistry)) throw new Error(`titleEscrowOwner === tokenRegistry`); const results = run(command); checkSurrenderRejectSuccess(results); titleEscrowOwner = await retrieveTitleEscrowOwner( @@ -40,24 +40,26 @@ describe("reject surrender title-escrow", () => { tokenRegistry, tokenId ); - expect(isAddress(titleEscrowOwner)).toBe(true); - expect(titleEscrowOwner).not.toBe(tokenRegistry); - }); + if(!(isAddress(titleEscrowOwner) === true)) throw new Error(`isAddress(titleEscrowOwner) === true`); + if(!(titleEscrowOwner !== tokenRegistry)) throw new Error(`titleEscrowOwner !== tokenRegistry`); + } - it("should not be able to reject surrender invalid title-escrow on token-registry", async () => { + // it("should not be able to reject surrender invalid title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + } - it("should not be able to reject surrender title-escrow on invalid token-registry", async () => { + // it("should not be able to reject surrender title-escrow on invalid token-registry", async () => { + { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); const results = run(command); checkFailure(results, "null"); - }); + } - it.todo("should be fail when user without permission attempts reject surrender"); -}); + // it.todo("should be fail when user without permission attempts reject surrender"); +} diff --git a/src/e2e/surrender.test.ts b/src/e2e/surrender.e2e.ts similarity index 76% rename from src/e2e/surrender.test.ts rename to src/e2e/surrender.e2e.ts index 5ea47dfe..469476cf 100644 --- a/src/e2e/surrender.test.ts +++ b/src/e2e/surrender.e2e.ts @@ -15,15 +15,10 @@ const defaultTitleEscrow = { }; -describe("surrender title-escrow", () => { - jest.setTimeout(90000); +export const surrender = async () => { + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - let tokenRegistryAddress = ""; - beforeAll(() => { - tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - }); - - it("should be able to surrender title-escrow on token-registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { @@ -37,21 +32,21 @@ describe("surrender title-escrow", () => { surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - expect(isAddress(titleEscrowOwner)).toBe(true); - expect(titleEscrowOwner).not.toBe(surrenderTitleEscrow.tokenRegistry); + if(isAddress(titleEscrowOwner) !== true){ throw new Error(`(isAddress(titleEscrowOwner) === true);`)}; + if(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`)}; const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); const results = run(command); const surrenderResults = checkSurrenderSuccess(results); - expect(surrenderResults.tokenId).toBe(surrenderTitleEscrow.tokenId); + if(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId){ throw new Error(`(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId);`)}; titleEscrowOwner = await retrieveTitleEscrowOwner( signer, surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - expect(titleEscrowOwner).toBe(surrenderTitleEscrow.tokenRegistry); - }); + if(titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry);`)}; + }; - it("should not surrender unowned title escrow", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { @@ -68,11 +63,11 @@ describe("surrender title-escrow", () => { surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - expect(isAddress(titleEscrowOwner)).toBe(true); - expect(titleEscrowOwner).not.toBe(surrenderTitleEscrow.tokenRegistry); - }); + if(isAddress(titleEscrowOwner) !== true){ throw new Error(`(isAddress(titleEscrowOwner) !== true);`)}; + if(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`)}; + }; - it("should not surrender invalid title escrow", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { @@ -83,9 +78,9 @@ describe("surrender title-escrow", () => { const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }); + }; - it("should not surrender invalid title escrow with invalid token registry", async () => { + { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { @@ -96,5 +91,5 @@ describe("surrender title-escrow", () => { const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); checkFailure(results, "null"); - }); -}); + }; +}; diff --git a/src/e2e/utils/bootstrap/common.ts b/src/e2e/utils/bootstrap/common.ts index 2ff1b25e..a8a2b505 100644 --- a/src/e2e/utils/bootstrap/common.ts +++ b/src/e2e/utils/bootstrap/common.ts @@ -1,9 +1,9 @@ import { EndStatus } from "../constants"; import { extractStatus } from "../shell"; -export const checkFailure = (results: string, expectedErrorMessage: string) => { +export const checkFailure = (results: string, expectedErrorMessage: string) => { //}: Error | undefined => { const statusMessage = extractStatus(results, EndStatus.error); - expect(statusMessage.length).toBeGreaterThan(0); + if(!(statusMessage.length > 0)) throw new Error(`!statusMessage.length > 0`); const errorMessage = statusMessage[0].lineContent.substring(13); - expect(errorMessage).toContain(expectedErrorMessage); + if(!(errorMessage.includes(expectedErrorMessage))){throw new Error(`!errorMessage.includes(expectedErrorMessage)`)}; }; \ No newline at end of file diff --git a/src/e2e/utils/bootstrap/mint.ts b/src/e2e/utils/bootstrap/mint.ts index d4615fa1..8a991baf 100644 --- a/src/e2e/utils/bootstrap/mint.ts +++ b/src/e2e/utils/bootstrap/mint.ts @@ -16,10 +16,10 @@ export interface MintData { } export const validateMintData = (expectedValue: MintData, value: MintData) => { - expect(expectedValue.address).toBe(value.address); - expect(expectedValue.beneficiary).toBe(value.beneficiary); - expect(expectedValue.holder).toBe(value.holder); - expect(expectedValue.tokenId).toBe(value.tokenId); + if(!(expectedValue.address === value.address)) throw new Error(`expectedValue.address === value.address`); + if(!(expectedValue.beneficiary === value.beneficiary)) throw new Error(`expectedValue.beneficiary === value.beneficiary`); + if(!(expectedValue.holder === value.holder)) throw new Error(`expectedValue.holder === value.holder`); + if(!(expectedValue.tokenId === value.tokenId)) throw new Error(`expectedValue.tokenId === value.tokenId`); }; export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { diff --git a/src/e2e/utils/constants.ts b/src/e2e/utils/constants.ts index 7b0a1ca9..fc772f8b 100644 --- a/src/e2e/utils/constants.ts +++ b/src/e2e/utils/constants.ts @@ -40,7 +40,7 @@ export const AddressLength = 42; export type EndStatusType = typeof EndStatus[keyof typeof EndStatus]; -export const silent = false; +export const silent = true; export const verbose = false; export const defaultRunParameters = { network: network, From 4fc67932b8f0785930db7570d9afbc2e81af031b Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:48:50 +0800 Subject: [PATCH 08/20] fix: linting --- package.json | 5 +- src/e2e/accept-surrender.e2e.ts | 58 ++++++++-------- src/e2e/all.ts | 24 +++---- src/e2e/change-holder.e2e.ts | 38 ++++++----- src/e2e/deploy.e2e.ts | 49 ++++++++------ src/e2e/endorse-change-owner.e2e.ts | 40 +++++------ src/e2e/endorse-transfer.e2e.ts | 33 +++++----- src/e2e/mint.e2e.ts | 73 +++++++++------------ src/e2e/nominate.e2e.ts | 52 ++++++++------- src/e2e/reject-surrender.e2e.ts | 55 +++++++--------- src/e2e/surrender.e2e.ts | 47 +++++++------ src/e2e/utils/bootstrap/accept-surrender.ts | 28 ++++---- src/e2e/utils/bootstrap/change-holder.ts | 60 ++++++++--------- src/e2e/utils/bootstrap/common.ts | 14 ++-- src/e2e/utils/bootstrap/deploy.ts | 14 ++-- src/e2e/utils/bootstrap/endorse-owners.ts | 45 +++++++------ src/e2e/utils/bootstrap/endorse-transfer.ts | 36 +++++----- src/e2e/utils/bootstrap/index.ts | 1 - src/e2e/utils/bootstrap/mint.ts | 22 ++----- src/e2e/utils/bootstrap/nominate.ts | 66 +++++++++---------- src/e2e/utils/bootstrap/reject-surrender.ts | 28 ++++---- src/e2e/utils/bootstrap/surrender.ts | 63 +++++++++--------- src/e2e/utils/constants.ts | 1 - src/e2e/utils/contract-checks.ts | 2 +- src/e2e/utils/token-management.ts | 7 +- 25 files changed, 413 insertions(+), 448 deletions(-) diff --git a/package.json b/package.json index 331ecbe5..965e4c1c 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,12 @@ }, "scripts": { "dev": "ts-node src/index.ts", - "e2e": "concurrently -k -s first \"npm:blockchain\" \"npm:e2e-test\"", + "e2e": "concurrently -P -k -s first \"npm:e2e-test\" \"npm:blockchain\"", "build": "npm run clean && npm run build:cjs && npm run build:type", "build:cjs": "tsc --module commonjs --outDir dist/cjs --project ./tsconfig.prod.json", "build:type": "tsc -d --emitDeclarationOnly --outDir dist/types", "clean": "rm -rf dist/", - "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli", - "test:e2e": "jest --ci src/e2e/", + "blockchain": "ganache --wallet.mnemonic \"indicate swing place chair flight used hammer soon photo region volume shuffle\" -i 1337 --fork.network goerli --logging.quiet=true", "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", diff --git a/src/e2e/accept-surrender.e2e.ts b/src/e2e/accept-surrender.e2e.ts index 3c4ce096..3ad4d17f 100644 --- a/src/e2e/accept-surrender.e2e.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -1,12 +1,10 @@ -import { extractLine, LineInfo, run } from "./utils/shell"; -import { BurnAddress, EmptyTokenID, EndStatus, network, owner } from "./utils/constants"; -import { generateAcceptSurrenderCommand } from "./utils/commands"; import { checkFailure, checkSurrenderAcceptSuccess, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; -import { getSigner, retrieveTitleEscrow, retrieveTitleEscrowInfo, retrieveTitleEscrowOwner } from "./utils/contract-checks"; +import { generateAcceptSurrenderCommand } from "./utils/commands"; +import { BurnAddress, EmptyTokenID, network, owner } from "./utils/constants"; +import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; +import { run } from "./utils/shell"; -// describe("accept-surrender title-escrow", () => { -export const acceptSurrender = async () => { - +export const acceptSurrender = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); // const errors: Error[] = []; const defaultTitleEscrow = { @@ -16,46 +14,42 @@ export const acceptSurrender = async () => { dryRun: false, }; - // it("should be able to accept-surrender title-escrow on token-registry", async () => { + //"should be able to accept-surrender title-escrow on token-registry" { - const { tokenRegistry, tokenId, titleEscrowAddress } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); - let titleEscrowOwner: string = await retrieveTitleEscrowOwner( - signer, - tokenRegistry, - tokenId - ); - if(!(titleEscrowOwner === tokenRegistry)) throw new Error(`!(titleEscrowOwner === tokenRegistry)`); - + let titleEscrowOwner: string = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); + if (!(titleEscrowOwner === tokenRegistry)) throw new Error(`!(titleEscrowOwner === tokenRegistry)`); + const results = run(command); - titleEscrowOwner = await retrieveTitleEscrowOwner( - signer, - tokenRegistry, - tokenId - ); - if(!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")) throw new Error(`!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")`); + titleEscrowOwner = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); + if (!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")) + throw new Error(`!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")`); checkSurrenderAcceptSuccess(results); } - - // it("should not be able to accept surrender invalid title-escrow on token-registry", async () => { + + //"should not be able to accept surrender invalid title-escrow on token-registry" { const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); + const command = generateAcceptSurrenderCommand( + { tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, + owner.privateKey + ); const results = run(command); checkFailure(results, "missing revert data in call exception"); } - - // it("should not be able to accept surrender title-escrow on invalid token-registry", async () => { + //"should not be able to accept surrender title-escrow on invalid token-registry" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateAcceptSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand( + { tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, + owner.privateKey + ); const results = run(command); checkFailure(results, "null"); } - // return errors; - // it.todo("should be fail when permission is denied"); }; -// npm run dev -- title-escrow accept-surrendered --token-registry 0x0000000000000000000000000000000000000000 --tokenId 0xf32907ec66ac258f058eae34d76b160b0ca520b5c613ca93ae6bb22a8f9c451f --network local -k 0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7 \ No newline at end of file +// npm run dev -- title-escrow accept-surrendered --token-registry 0x0000000000000000000000000000000000000000 --tokenId 0xf32907ec66ac258f058eae34d76b160b0ca520b5c613ca93ae6bb22a8f9c451f --network local -k 0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7 diff --git a/src/e2e/all.ts b/src/e2e/all.ts index 1311e709..8f87e861 100644 --- a/src/e2e/all.ts +++ b/src/e2e/all.ts @@ -1,15 +1,15 @@ -import { acceptSurrender } from './accept-surrender.e2e' -import { surrender } from './surrender.e2e' -import { changeHolder } from './change-holder.e2e' -import { deployDocumentStore, deployTokenRegistry } from './deploy.e2e'; -import { endorseTransfer } from './endorse-transfer.e2e'; -import { mint } from './mint.e2e'; -import { nominate } from './nominate.e2e'; -import { rejectSurrender } from './reject-surrender.e2e'; +import { acceptSurrender } from "./accept-surrender.e2e"; +import { surrender } from "./surrender.e2e"; +import { changeHolder } from "./change-holder.e2e"; +import { deployDocumentStore, deployTokenRegistry } from "./deploy.e2e"; +import { endorseTransfer } from "./endorse-transfer.e2e"; +import { mint } from "./mint.e2e"; +import { nominate } from "./nominate.e2e"; +import { rejectSurrender } from "./reject-surrender.e2e"; -const awaitForDuration = async (runFunction: Function) => { - await runFunction(); -} +const awaitForDuration = async (runFunction: () => void): Promise => { + await runFunction(); +}; awaitForDuration(surrender); awaitForDuration(acceptSurrender); @@ -19,4 +19,4 @@ awaitForDuration(changeHolder); awaitForDuration(endorseTransfer); awaitForDuration(mint); awaitForDuration(nominate); -awaitForDuration(rejectSurrender); \ No newline at end of file +awaitForDuration(rejectSurrender); diff --git a/src/e2e/change-holder.e2e.ts b/src/e2e/change-holder.e2e.ts index 2dd672e1..19891e11 100644 --- a/src/e2e/change-holder.e2e.ts +++ b/src/e2e/change-holder.e2e.ts @@ -1,17 +1,21 @@ -import { extractLine, LineInfo, run } from "./utils/shell"; -import { BurnAddress, EmptyTokenID, EndStatus, network, owner, receiver } from "./utils/constants"; import { TitleEscrowTransferHolderCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { + changeHolderToken, + checkChangeHolderSuccess, + checkFailure, + defaultTransferHolder, + deployTokenRegistry, + mintTokenRegistry, +} from "./utils/bootstrap"; import { generateChangeHolderCommand } from "./utils/commands"; -import { checkFailure, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; -import { changeHolderToken, checkChangeHolderSuccess, defaultTransferHolder } from "./utils/bootstrap"; - -// describe("transfer holder title-escrow", () => { -export const changeHolder = async () => { - // jest.setTimeout(90000); +import { BurnAddress, EmptyTokenID, owner, receiver } from "./utils/constants"; +import { run } from "./utils/shell"; +// "transfer holder title-escrow" +export const changeHolder = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - // it("should be able to transfer holder title-escrow on token-registry", async () => { + //should be able to transfer holder title-escrow on token-registry" { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { @@ -24,8 +28,7 @@ export const changeHolder = async () => { checkChangeHolderSuccess(results); } - - // it("holder should be able to transfer holder of title-escrow", async () => { + //holder should be able to transfer holder of title-escrow" { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver @@ -50,9 +53,9 @@ export const changeHolder = async () => { checkChangeHolderSuccess(results); } - // it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + //should not be able to transfer holder of invalid title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: EmptyTokenID, tokenRegistry: tokenRegistry, @@ -63,10 +66,9 @@ export const changeHolder = async () => { checkFailure(results, "missing revert data in call exception"); } - - // it("should not be able to transfer holder of invalid title-escrow on token-registry", async () => { + //should not be able to transfer holder of invalid title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: tokenId, tokenRegistry: BurnAddress, @@ -77,7 +79,7 @@ export const changeHolder = async () => { checkFailure(results, "null"); } - // it("beneficiary should not be able to transfer holder of title-escrow", async () => { + //beneficiary should not be able to transfer holder of title-escrow" { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver @@ -100,5 +102,5 @@ export const changeHolder = async () => { // Beneficiary attempts to transfer Holder without permission const results = run(command); checkFailure(results, "missing revert data in call exception"); - } + } }; diff --git a/src/e2e/deploy.e2e.ts b/src/e2e/deploy.e2e.ts index b783fab1..62660c7c 100644 --- a/src/e2e/deploy.e2e.ts +++ b/src/e2e/deploy.e2e.ts @@ -6,11 +6,8 @@ import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/bootstrap"; -export const deployTokenRegistry = async () => { -// describe("deploy token-registry", () => { - // jest.setTimeout(90000); - - // it("should be able to deploy token-registry", async () => { +export const deployTokenRegistry = async (): Promise => { + //should be able to deploy token-registry" { const tokenRegistryParameter: DeployTokenRegistryCommand = { registryName: "Test Token", @@ -23,20 +20,30 @@ export const deployTokenRegistry = async () => { const tokenRegistryAddress = checkTokenRegistrySuccess(results); const signer = getSigner(defaultRunParameters.network, owner.privateKey); const tokenInfo = await retrieveTokenInfo(signer, tokenRegistryAddress); - if(!(tokenInfo.name === tokenRegistryParameter.registryName)){ throw new Error("tokenInfo.name === tokenRegistryParameter.registryName")}; - if(!(tokenInfo.symbol === tokenRegistryParameter.registrySymbol)){ throw new Error("tokenInfo.symbol === tokenRegistryParameter.registrySymbol")}; + if (!(tokenInfo.name === tokenRegistryParameter.registryName)) { + throw new Error("tokenInfo.name === tokenRegistryParameter.registryName"); + } + if (!(tokenInfo.symbol === tokenRegistryParameter.registrySymbol)) { + throw new Error("tokenInfo.symbol === tokenRegistryParameter.registrySymbol"); + } const rolesInfo = await rolesCheck(signer, tokenRegistryAddress); - if(!(rolesInfo.accepterRole === true)){ throw new Error("rolesInfo.accepterRole === true")}; - if(!(rolesInfo.defaultRole === true)){ throw new Error("rolesInfo.defaultRole === true")}; - if(!(rolesInfo.minterRole === true)){ throw new Error("rolesInfo.minterRole === true")}; - if(!(rolesInfo.restorerRole === true)){ throw new Error("rolesInfo.restorerRole === true")}; + if (!(rolesInfo.accepterRole === true)) { + throw new Error("rolesInfo.accepterRole === true"); + } + if (!(rolesInfo.defaultRole === true)) { + throw new Error("rolesInfo.defaultRole === true"); + } + if (!(rolesInfo.minterRole === true)) { + throw new Error("rolesInfo.minterRole === true"); + } + if (!(rolesInfo.restorerRole === true)) { + throw new Error("rolesInfo.restorerRole === true"); + } } -} +}; -// describe("deploy document-store", () => { -export const deployDocumentStore = () => { - // jest.setTimeout(90000); - // it("should be able to deploy document-store", async () => +export const deployDocumentStore = async (): Promise => { + //should be able to deploy document-store { const documentStoreParameters: DeployDocumentStoreCommand = { storeName: "Test Document Store", @@ -47,11 +54,13 @@ export const deployDocumentStore = () => { const results = run(command); const tokenRegistrySuccessFormat = `${EndStatus.success} Document store Test Document Store deployed at `; const checkSuccess = results.includes(tokenRegistrySuccessFormat); - if(!(checkSuccess === true)) throw new Error(`checkSuccess === true)`); + if (!(checkSuccess === true)) throw new Error(`checkSuccess === true)`); const splitResults = results.trim().split("\n"); const tokenRegistryAddressLine = splitResults[splitResults.length - 2]; const tokenRegistryAddress = tokenRegistryAddressLine.trim().substring(tokenRegistrySuccessFormat.length); - if(!(isAddress(tokenRegistryAddress) === true)) throw new Error(`isAddress(tokenRegistryAddress) === true)`); - if(!(isAddress(tokenRegistryAddress) === true)){ throw new Error("isAddress(tokenRegistryAddress) === true")}; + if (!(isAddress(tokenRegistryAddress) === true)) throw new Error(`isAddress(tokenRegistryAddress) === true)`); + if (!(isAddress(tokenRegistryAddress) === true)) { + throw new Error("isAddress(tokenRegistryAddress) === true"); + } } -} +}; diff --git a/src/e2e/endorse-change-owner.e2e.ts b/src/e2e/endorse-change-owner.e2e.ts index 2f30c756..1147cdf7 100644 --- a/src/e2e/endorse-change-owner.e2e.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -1,14 +1,17 @@ -import { extractLine, LineInfo, run } from "./utils/shell"; -import { EndStatus, network, owner, receiver } from "./utils/constants"; +import { run } from "./utils/shell"; +import { network, owner, receiver } from "./utils/constants"; import { TitleEscrowEndorseTransferOfOwnersCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateTransferOwnersCommand } from "./utils/commands"; -import { changeHolderToken, checkEndorseOwner, checkFailure, deployTokenRegistry, mintNominatedToken, mintTokenRegistry } from "./utils/bootstrap"; +import { + checkEndorseOwner, + checkFailure, + deployTokenRegistry, + mintNominatedToken, + mintTokenRegistry, +} from "./utils/bootstrap"; -// describe("endorse change owner title-escrow", () => { -export const endorseChangeOwner = () => { - // jest.setTimeout(90000); - - +// "endorse change owner title-escrow" +export const endorseChangeOwner = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTransferOwners = { @@ -18,7 +21,7 @@ export const endorseChangeOwner = () => { dryRun: false, }; - // it("should be able to endorse change owner title-escrow on token-registry", async () => { + //should be able to endorse change owner title-escrow on token-registry" { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, @@ -33,17 +36,14 @@ export const endorseChangeOwner = () => { const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); const { beneficiary, holder, tokenId: tokenIdResult } = checkEndorseOwner(results); - if(!(beneficiary === transferOwners.newOwner)) throw new Error(`beneficiary === transferOwners.newOwner`); - if(!(holder === transferOwners.newHolder)) throw new Error(`holder === transferOwners.newHolder`); - if(!(tokenIdResult === transferOwners.tokenId)) throw new Error(`tokenIdResult === transferOwners.tokenId`); + if (!(beneficiary === transferOwners.newOwner)) throw new Error(`beneficiary === transferOwners.newOwner`); + if (!(holder === transferOwners.newHolder)) throw new Error(`holder === transferOwners.newHolder`); + if (!(tokenIdResult === transferOwners.tokenId)) throw new Error(`tokenIdResult === transferOwners.tokenId`); } - // it("should not be able to endorse change owner on un-nominated title-escrow", async () => { - { - const { tokenRegistry, tokenId } = mintTokenRegistry( - owner.privateKey, - tokenRegistryAddress - ); + //should not be able to endorse change owner on un-nominated title-escrow" + { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { tokenId: tokenId, tokenRegistry: tokenRegistry, @@ -51,6 +51,6 @@ export const endorseChangeOwner = () => { }; const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); - checkFailure(results, "") + checkFailure(results, ""); } -} +}; diff --git a/src/e2e/endorse-transfer.e2e.ts b/src/e2e/endorse-transfer.e2e.ts index 01badae4..70ee5762 100644 --- a/src/e2e/endorse-transfer.e2e.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -1,14 +1,16 @@ -import { extractLine, LineInfo, run } from "./utils/shell"; -import { BurnAddress, EndStatus, network, owner, receiver } from "./utils/constants"; import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; +import { + checkEndorseTransfer, + checkFailure, + deployTokenRegistry, + mintNominatedToken, + mintTokenRegistry, +} from "./utils/bootstrap"; import { generateEndorseTransferOwnerCommand } from "./utils/commands"; -import { checkEndorseTransfer, checkFailure, deployTokenRegistry, mintNominatedToken, mintToken, mintTokenRegistry } from "./utils/bootstrap"; +import { BurnAddress, network, owner, receiver } from "./utils/constants"; +import { run } from "./utils/shell"; -// describe("endorse transfer title-escrow", () => { -export const endorseTransfer = async () => { - // jest.setTimeout(90000); - - +export const endorseTransfer = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTransferHolder = { @@ -17,7 +19,7 @@ export const endorseTransfer = async () => { dryRun: false, }; - // it("should be able to endorse transfer title-escrow on token-registry", async () => { + //"should be able to endorse transfer title-escrow on token-registry" { const { tokenRegistry, tokenId } = mintNominatedToken( owner.privateKey, @@ -35,23 +37,18 @@ export const endorseTransfer = async () => { checkEndorseTransfer(results); } - - // it("should not be able to endorse un-nominated title-escrow on token-registry", async () => { + //"should not be able to endorse un-nominated title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintTokenRegistry( - owner.privateKey, - tokenRegistryAddress - ); + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultTransferHolder, tokenId, tokenRegistry, - newBeneficiary: BurnAddress + newBeneficiary: BurnAddress, }; const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); checkFailure(results, ""); } - -} +}; diff --git a/src/e2e/mint.e2e.ts b/src/e2e/mint.e2e.ts index e0c5086e..7304e493 100644 --- a/src/e2e/mint.e2e.ts +++ b/src/e2e/mint.e2e.ts @@ -1,31 +1,16 @@ -import { generateTokenId, isTokenId } from "./utils/token-management"; -import { extractStatus, run } from "./utils/shell"; -import { - AddressLength, - BurnAddress, - defaultRunParameters, - EndStatus, - network, - owner, - receiver, - TokenIdLength, - TokenInfo, -} from "./utils/constants"; -import { isAddress } from "web3-utils"; +import { generateTokenId } from "./utils/token-management"; +import { run } from "./utils/shell"; +import { BurnAddress, defaultRunParameters, owner, receiver } from "./utils/constants"; import { TokenRegistryIssueCommand } from "../commands/token-registry/token-registry-command.type"; import { generateMintTitleEscrowCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/bootstrap"; -export const mint = async () => { -// describe("deploy token-registry", () => { - // jest.setTimeout(90000); - - +export const mint = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - // it("should be able to mint title-escrow on token-registry", async () => { + //"should be able to mint title-escrow on token-registry" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -46,17 +31,20 @@ export const mint = async () => { titleEscrowParameter.address, titleEscrowParameter.tokenId ); - if(!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); - if(!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); - if(!(titleEscrowInfo.holder === titleEscrowParameter.holder)) throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); - if(!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); - if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); - if(!(titleEscrowInfo.registry === titleEscrowParameter.address)) throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) + throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if (!(titleEscrowInfo.holder === titleEscrowParameter.holder)) + throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === titleEscrowParameter.address)) + throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); - if(!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); } - // it("should be able to mint title-escrow for a different wallet on token-registry", async () => { + //"should be able to mint title-escrow for a different wallet on token-registry" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -77,17 +65,20 @@ export const mint = async () => { titleEscrowParameter.address, titleEscrowParameter.tokenId ); - if(!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); - if(!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); - if(!(titleEscrowInfo.holder === titleEscrowParameter.holder)) throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); - if(!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); - if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); - if(!(titleEscrowInfo.registry === titleEscrowParameter.address)) throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) + throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if (!(titleEscrowInfo.holder === titleEscrowParameter.holder)) + throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === titleEscrowParameter.address)) + throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); - if(!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); } - // it("should fail with invalid token id", async () => { + //"should fail with invalid token id" { const titleEscrowParameter: TokenRegistryIssueCommand = { address: tokenRegistryAddress, @@ -101,7 +92,7 @@ export const mint = async () => { checkFailure(results, "invalid BigNumber string"); } - // it("should fail with invalid token registry", async () => { + //"should fail with invalid token registry" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -116,7 +107,7 @@ export const mint = async () => { checkFailure(results, "null"); } - // it("should fail with invalid beneficiary", async () => { + //"should fail with invalid beneficiary" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -131,7 +122,7 @@ export const mint = async () => { checkFailure(results, "missing revert data in call exception"); } - // it("should fail with invalid holder", async () => { + //"should fail with invalid holder" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -146,7 +137,7 @@ export const mint = async () => { checkFailure(results, "missing revert data in call exception"); } - // it("should fail with no funds", async () => { + //"should fail with no funds" { const tokenId = generateTokenId(); const titleEscrowParameter: TokenRegistryIssueCommand = { @@ -161,4 +152,4 @@ export const mint = async () => { const results = run(command); checkFailure(results, "null"); } -} +}; diff --git a/src/e2e/nominate.e2e.ts b/src/e2e/nominate.e2e.ts index 493ecc41..fc35daf5 100644 --- a/src/e2e/nominate.e2e.ts +++ b/src/e2e/nominate.e2e.ts @@ -1,23 +1,20 @@ import { run } from "./utils/shell"; -import { - BurnAddress, - defaultRunParameters, - owner, - receiver, -} from "./utils/constants"; +import { BurnAddress, defaultRunParameters, owner, receiver } from "./utils/constants"; import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateNominateCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; -import { checkFailure, checkNominateSuccess, defaultNominateBeneficiary, deployTokenRegistry, mintTokenRegistry } from "./utils/bootstrap"; - -export const nominate = async () => { -// describe("nominate title-escrow", () => { - // jest.setTimeout(90000); +import { + checkFailure, + checkNominateSuccess, + defaultNominateBeneficiary, + deployTokenRegistry, + mintTokenRegistry, +} from "./utils/bootstrap"; - +export const nominate = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - // it("should be able to nominate title-escrow on token-registry", async () => { + //"should be able to nominate title-escrow on token-registry" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { @@ -29,14 +26,17 @@ export const nominate = async () => { const command = generateNominateCommand(transferHolder, owner.privateKey); const results = run(command); const nominateInfo = checkNominateSuccess(results); - if(!(nominateInfo.tokenId === transferHolder.tokenId)) throw new Error (`nominateInfo.tokenId === transferHolder.tokenId`); - if(!(nominateInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`nominateInfo.nominee === transferHolder.newBeneficiary`); + if (!(nominateInfo.tokenId === transferHolder.tokenId)) + throw new Error(`nominateInfo.tokenId === transferHolder.tokenId`); + if (!(nominateInfo.nominee === transferHolder.newBeneficiary)) + throw new Error(`nominateInfo.nominee === transferHolder.newBeneficiary`); const signer = await getSigner(transferHolder.network, owner.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); - if(!(titleEscrowInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`titleEscrowInfo.nominee === transferHolder.newBeneficiary`); + if (!(titleEscrowInfo.nominee === transferHolder.newBeneficiary)) + throw new Error(`titleEscrowInfo.nominee === transferHolder.newBeneficiary`); } - // it("should be able to cancel nomination of title-escrow on token-registry", async () => { + //"should be able to cancel nomination of title-escrow on token-registry" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { @@ -49,15 +49,17 @@ export const nominate = async () => { const command = generateNominateCommand(transferHolder, owner.privateKey); results = run(command); const nominateInfo = checkNominateSuccess(results); - if(!(nominateInfo.tokenId === transferHolder.tokenId)) throw new Error (`nominateInfo.tokenId === transferHolder.tokenId`); - if(!(nominateInfo.nominee === transferHolder.newBeneficiary)) throw new Error (`nominateInfo.nominee === transferHolder.newBeneficiary`); + if (!(nominateInfo.tokenId === transferHolder.tokenId)) + throw new Error(`nominateInfo.tokenId === transferHolder.tokenId`); + if (!(nominateInfo.nominee === transferHolder.newBeneficiary)) + throw new Error(`nominateInfo.nominee === transferHolder.newBeneficiary`); const signer = await getSigner(transferHolder.network, owner.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); - if(!(titleEscrowInfo.nominee === BurnAddress)) throw new Error (`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); } - // it("should not be able to nominate self", async () => { + //"should not be able to nominate self" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { @@ -71,7 +73,7 @@ export const nominate = async () => { checkFailure(results, "new beneficiary address is the same as the current beneficiary address"); } - // it("should not be able to nominate unowned token", async () => { + //"should not be able to nominate unowned token" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { @@ -85,7 +87,7 @@ export const nominate = async () => { checkFailure(results, "missing revert data in call exception"); } - // it("should not be able to nominate non-existent token", async () => { + //"should not be able to nominate non-existent token" { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { @@ -99,7 +101,7 @@ export const nominate = async () => { checkFailure(results, "missing revert data in call exception"); } - // it("should not be able to nominate non-existent token registry", async () => { + //"should not be able to nominate non-existent token registry" { const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -111,4 +113,4 @@ export const nominate = async () => { const results = run(command); checkFailure(results, "null"); } -} +}; diff --git a/src/e2e/reject-surrender.e2e.ts b/src/e2e/reject-surrender.e2e.ts index 0c8b67e3..a1de39d8 100644 --- a/src/e2e/reject-surrender.e2e.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -1,17 +1,13 @@ // import { deployTokenRegistry, mintSurrenderToken } from "./utils/utils"; -import { extractLine, LineInfo, run } from "./utils/shell"; -import { BurnAddress, EmptyTokenID, EndStatus, network, owner } from "./utils/constants"; +import { run } from "./utils/shell"; +import { BurnAddress, EmptyTokenID, network, owner } from "./utils/constants"; import { generateRejectSurrenderCommand } from "./utils/commands"; import { checkFailure, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; import { checkSurrenderRejectSuccess } from "./utils/bootstrap"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; -export const rejectSurrender = async () => { -// describe("reject surrender title-escrow", () => { - // jest.setTimeout(90000); - - +export const rejectSurrender = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTitleEscrow = { @@ -21,45 +17,40 @@ export const rejectSurrender = async () => { dryRun: false, }; - // it("should be able to reject surrender title-escrow on token-registry", async () => { + //"should be able to reject surrender title-escrow on token-registry" { const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); - - let titleEscrowOwner: string = await retrieveTitleEscrowOwner( - signer, - tokenRegistry, - tokenId - ); - if(!(titleEscrowOwner === tokenRegistry)) throw new Error(`titleEscrowOwner === tokenRegistry`); + + let titleEscrowOwner: string = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); + if (!(titleEscrowOwner === tokenRegistry)) throw new Error(`titleEscrowOwner === tokenRegistry`); const results = run(command); checkSurrenderRejectSuccess(results); - titleEscrowOwner = await retrieveTitleEscrowOwner( - signer, - tokenRegistry, - tokenId - ); - if(!(isAddress(titleEscrowOwner) === true)) throw new Error(`isAddress(titleEscrowOwner) === true`); - if(!(titleEscrowOwner !== tokenRegistry)) throw new Error(`titleEscrowOwner !== tokenRegistry`); + titleEscrowOwner = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); + if (!(isAddress(titleEscrowOwner) === true)) throw new Error(`isAddress(titleEscrowOwner) === true`); + if (!(titleEscrowOwner !== tokenRegistry)) throw new Error(`titleEscrowOwner !== tokenRegistry`); } - // it("should not be able to reject surrender invalid title-escrow on token-registry", async () => { + //"should not be able to reject surrender invalid title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey); + const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand( + { tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, + owner.privateKey + ); const results = run(command); checkFailure(results, "missing revert data in call exception"); } - - // it("should not be able to reject surrender title-escrow on invalid token-registry", async () => { + //"should not be able to reject surrender title-escrow on invalid token-registry" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); - const command = generateRejectSurrenderCommand({ tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey); + const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand( + { tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, + owner.privateKey + ); const results = run(command); checkFailure(results, "null"); } - - // it.todo("should be fail when user without permission attempts reject surrender"); -} +}; diff --git a/src/e2e/surrender.e2e.ts b/src/e2e/surrender.e2e.ts index 469476cf..8f8fd6c4 100644 --- a/src/e2e/surrender.e2e.ts +++ b/src/e2e/surrender.e2e.ts @@ -1,22 +1,19 @@ -import { isTokenId } from "./utils/token-management"; -import { extractLine, extractStatus, LineInfo, run } from "./utils/shell"; -import { BurnAddress, defaultRunParameters, EndStatus, network, owner, receiver, TokenIdLength, TokenInfo } from "./utils/constants"; +import { run } from "./utils/shell"; +import { BurnAddress, defaultRunParameters, owner, receiver } from "./utils/constants"; import { BaseTitleEscrowCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateSurrenderCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; import { deployTokenRegistry, mintTokenRegistry, checkSurrenderSuccess, checkFailure } from "./utils/bootstrap"; - const defaultTitleEscrow = { ...defaultRunParameters, beneficiary: owner.ethAddress, holder: owner.ethAddress, }; - -export const surrender = async () => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); +export const surrender = async (): Promise => { + const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); @@ -32,19 +29,27 @@ export const surrender = async () => { surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - if(isAddress(titleEscrowOwner) !== true){ throw new Error(`(isAddress(titleEscrowOwner) === true);`)}; - if(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`)}; + if (isAddress(titleEscrowOwner) !== true) { + throw new Error(`(isAddress(titleEscrowOwner) === true);`); + } + if (titleEscrowOwner === surrenderTitleEscrow.tokenRegistry) { + throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`); + } const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); const results = run(command); const surrenderResults = checkSurrenderSuccess(results); - if(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId){ throw new Error(`(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId);`)}; + if (surrenderResults.tokenId !== surrenderTitleEscrow.tokenId) { + throw new Error(`(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId);`); + } titleEscrowOwner = await retrieveTitleEscrowOwner( signer, surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - if(titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry);`)}; - }; + if (titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry) { + throw new Error(`(titleEscrowOwner !== surrenderTitleEscrow.tokenRegistry);`); + } + } { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); @@ -63,12 +68,16 @@ export const surrender = async () => { surrenderTitleEscrow.tokenRegistry, surrenderTitleEscrow.tokenId ); - if(isAddress(titleEscrowOwner) !== true){ throw new Error(`(isAddress(titleEscrowOwner) !== true);`)}; - if(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry){ throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`)}; - }; + if (isAddress(titleEscrowOwner) !== true) { + throw new Error(`(isAddress(titleEscrowOwner) !== true);`); + } + if (titleEscrowOwner === surrenderTitleEscrow.tokenRegistry) { + throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`); + } + } { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -78,10 +87,10 @@ export const surrender = async () => { const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - }; + } { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -91,5 +100,5 @@ export const surrender = async () => { const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); checkFailure(results, "null"); - }; + } }; diff --git a/src/e2e/utils/bootstrap/accept-surrender.ts b/src/e2e/utils/bootstrap/accept-surrender.ts index 4db06ade..cf2043ef 100644 --- a/src/e2e/utils/bootstrap/accept-surrender.ts +++ b/src/e2e/utils/bootstrap/accept-surrender.ts @@ -1,20 +1,18 @@ - import { EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; -export const checkSurrenderAcceptSuccess = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); - - const isValidTokenId = isTokenId(tokenId); - - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - tokenId, - }; +export const checkSurrenderAcceptSuccess = (results: string): { tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, }; - \ No newline at end of file +}; diff --git a/src/e2e/utils/bootstrap/change-holder.ts b/src/e2e/utils/bootstrap/change-holder.ts index 849ee09a..0c8f4498 100644 --- a/src/e2e/utils/bootstrap/change-holder.ts +++ b/src/e2e/utils/bootstrap/change-holder.ts @@ -1,45 +1,37 @@ - import { isAddress } from "web3-utils"; -import { mintTokenRegistry } from "."; import { TitleEscrowTransferHolderCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { generateChangeHolderCommand } from "../commands"; -import { AddressLength, BurnAddress, defaultRunParameters, EndStatus, network, receiver, TokenIdLength } from "../constants"; +import { AddressLength, EndStatus, network, receiver, TokenIdLength } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -// ✔ success Transferable record with hash 0x9837ba0954300cf74dea2d7ee9f294a3d1ca0ce2fb8025a3de505440971a7baf's holder has been successfully changed to holder with address: 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 +export const defaultTransferHolder = { + newHolder: receiver.ethAddress, + network: network, + dryRun: false, +}; +export const checkChangeHolderSuccess = (results: string): { newHolder: string; tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Change Holder failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const holder = titleEscrowAddressLine.substring(173, 173 + AddressLength); -export const defaultTransferHolder = { - newHolder: receiver.ethAddress, - network: network, - dryRun: false, - }; - -export const checkChangeHolderSuccess = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Change Holder failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - const holder = titleEscrowAddressLine.substring(173, 173 + AddressLength); - - const holderIsAddress = isAddress(holder); - const isValidTokenId = isTokenId(tokenId); - - if (!holderIsAddress) throw new Error("Invalid holder address"); - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - newHolder: holder, - tokenId, - }; - }; + const holderIsAddress = isAddress(holder); + const isValidTokenId = isTokenId(tokenId); - export const changeHolderToken = (privateKey: string, transferHolder: TitleEscrowTransferHolderCommand) => { - const command = generateChangeHolderCommand(transferHolder, privateKey); - const results = run(command); - checkChangeHolderSuccess(results); - } + if (!holderIsAddress) throw new Error("Invalid holder address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + return { + newHolder: holder, + tokenId, + }; +}; - \ No newline at end of file +export const changeHolderToken = (privateKey: string, transferHolder: TitleEscrowTransferHolderCommand): void => { + const command = generateChangeHolderCommand(transferHolder, privateKey); + const results = run(command); + checkChangeHolderSuccess(results); +}; diff --git a/src/e2e/utils/bootstrap/common.ts b/src/e2e/utils/bootstrap/common.ts index a8a2b505..a727d91f 100644 --- a/src/e2e/utils/bootstrap/common.ts +++ b/src/e2e/utils/bootstrap/common.ts @@ -1,9 +1,11 @@ import { EndStatus } from "../constants"; import { extractStatus } from "../shell"; -export const checkFailure = (results: string, expectedErrorMessage: string) => { //}: Error | undefined => { - const statusMessage = extractStatus(results, EndStatus.error); - if(!(statusMessage.length > 0)) throw new Error(`!statusMessage.length > 0`); - const errorMessage = statusMessage[0].lineContent.substring(13); - if(!(errorMessage.includes(expectedErrorMessage))){throw new Error(`!errorMessage.includes(expectedErrorMessage)`)}; -}; \ No newline at end of file +export const checkFailure = (results: string, expectedErrorMessage: string): void => { + const statusMessage = extractStatus(results, EndStatus.error); + if (!(statusMessage.length > 0)) throw new Error(`!statusMessage.length > 0`); + const errorMessage = statusMessage[0].lineContent.substring(13); + if (!errorMessage.includes(expectedErrorMessage)) { + throw new Error(`!errorMessage.includes(expectedErrorMessage)`); + } +}; diff --git a/src/e2e/utils/bootstrap/deploy.ts b/src/e2e/utils/bootstrap/deploy.ts index d213b537..51da7d9d 100644 --- a/src/e2e/utils/bootstrap/deploy.ts +++ b/src/e2e/utils/bootstrap/deploy.ts @@ -1,6 +1,6 @@ import { isAddress } from "web3-utils"; -import { DeployDocumentStoreCommand, DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; -import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "../commands"; +import { DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; +import { generateDeployTokenRegistryCommand } from "../commands"; import { defaultRunParameters, creators, EndStatus, AddressLength } from "../constants"; import { extractStatus, run } from "../shell"; @@ -15,18 +15,16 @@ const numberGenerator = (range: number): number => { return Math.floor(Math.random() * range); }; - export const checkTokenRegistrySuccess = (results: string): string => { const statusLine = extractStatus(results, EndStatus.success, "Token registry deployed at "); if (statusLine.length <= 0) throw new Error("Deployment failed"); const tokenRegistryDeploymentLine = statusLine[0].lineContent; const tokenRegistry = tokenRegistryDeploymentLine.substring(40, 40 + AddressLength); - - const tokenRegistryIsAddress = isAddress(tokenRegistry); - if (!tokenRegistryIsAddress) throw new Error("Invalid token registry address"); - return tokenRegistry; -}; + const tokenRegistryIsAddress = isAddress(tokenRegistry); + if (!tokenRegistryIsAddress) throw new Error("Invalid token registry address"); + return tokenRegistry; +}; export const deployTokenRegistry = ( privateKey: string, diff --git a/src/e2e/utils/bootstrap/endorse-owners.ts b/src/e2e/utils/bootstrap/endorse-owners.ts index ecd0c883..fb52e758 100644 --- a/src/e2e/utils/bootstrap/endorse-owners.ts +++ b/src/e2e/utils/bootstrap/endorse-owners.ts @@ -3,26 +3,25 @@ import { AddressLength, EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; - export const checkEndorseOwner = (results: string) => { - // ✔ success Transferable record with hash 0xffafc068018521a42a40ba1a8b9bf574c99c69763e42e155fae498278edb8074's holder has been successfully endorsed to new owner with address 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 and new holder with address: 0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60 - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Minting failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - const beneficiary = titleEscrowAddressLine.substring(176, 176 + AddressLength); - const holder = titleEscrowAddressLine.substring(248, 248 + AddressLength); - - const beneficiaryIsAddress = isAddress(beneficiary); - const holderIsAddress = isAddress(holder); - const isValidTokenId = isTokenId(tokenId); - - if (!beneficiaryIsAddress) throw new Error("Invalid beneficiary address"); - if (!holderIsAddress) throw new Error("Invalid holder address"); - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - beneficiary, - holder, - tokenId, - }; - } \ No newline at end of file +export const checkEndorseOwner = (results: string): { beneficiary: string; holder: string; tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const beneficiary = titleEscrowAddressLine.substring(176, 176 + AddressLength); + const holder = titleEscrowAddressLine.substring(248, 248 + AddressLength); + + const beneficiaryIsAddress = isAddress(beneficiary); + const holderIsAddress = isAddress(holder); + const isValidTokenId = isTokenId(tokenId); + + if (!beneficiaryIsAddress) throw new Error("Invalid beneficiary address"); + if (!holderIsAddress) throw new Error("Invalid holder address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + beneficiary, + holder, + tokenId, + }; +}; diff --git a/src/e2e/utils/bootstrap/endorse-transfer.ts b/src/e2e/utils/bootstrap/endorse-transfer.ts index 090628f0..e8eadcc4 100644 --- a/src/e2e/utils/bootstrap/endorse-transfer.ts +++ b/src/e2e/utils/bootstrap/endorse-transfer.ts @@ -3,21 +3,21 @@ import { AddressLength, EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; - export const checkEndorseTransfer = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Minting failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - const beneficiary = titleEscrowAddressLine.substring(177, 177 + AddressLength); - - const beneficiaryIsAddress = isAddress(beneficiary); - const isValidTokenId = isTokenId(tokenId); - - if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - beneficiary, - tokenId, - }; - } \ No newline at end of file +export const checkEndorseTransfer = (results: string): { beneficiary: string; tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const beneficiary = titleEscrowAddressLine.substring(177, 177 + AddressLength); + + const beneficiaryIsAddress = isAddress(beneficiary); + const isValidTokenId = isTokenId(tokenId); + + if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + beneficiary, + tokenId, + }; +}; diff --git a/src/e2e/utils/bootstrap/index.ts b/src/e2e/utils/bootstrap/index.ts index 53aea86a..518059ed 100644 --- a/src/e2e/utils/bootstrap/index.ts +++ b/src/e2e/utils/bootstrap/index.ts @@ -8,4 +8,3 @@ export * from "./change-holder"; export * from "./surrender"; export * from "./reject-surrender"; export * from "./accept-surrender"; - diff --git a/src/e2e/utils/bootstrap/mint.ts b/src/e2e/utils/bootstrap/mint.ts index 8a991baf..992571cd 100644 --- a/src/e2e/utils/bootstrap/mint.ts +++ b/src/e2e/utils/bootstrap/mint.ts @@ -4,7 +4,6 @@ import { deployTokenRegistry } from "."; import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; import { generateMintTitleEscrowCommand } from "../commands"; import { AddressLength, defaultRunParameters, EndStatus, TokenIdLength, TokenInfo } from "../constants"; -import { getSigner, retrieveTitleEscrowOwner } from "../contract-checks"; import { extractStatus, run } from "../shell"; import { generateTokenId, isTokenId } from "../token-management"; @@ -15,11 +14,12 @@ export interface MintData { holder: string; } -export const validateMintData = (expectedValue: MintData, value: MintData) => { - if(!(expectedValue.address === value.address)) throw new Error(`expectedValue.address === value.address`); - if(!(expectedValue.beneficiary === value.beneficiary)) throw new Error(`expectedValue.beneficiary === value.beneficiary`); - if(!(expectedValue.holder === value.holder)) throw new Error(`expectedValue.holder === value.holder`); - if(!(expectedValue.tokenId === value.tokenId)) throw new Error(`expectedValue.tokenId === value.tokenId`); +export const validateMintData = (expectedValue: MintData, value: MintData): void => { + if (!(expectedValue.address === value.address)) throw new Error(`expectedValue.address === value.address`); + if (!(expectedValue.beneficiary === value.beneficiary)) + throw new Error(`expectedValue.beneficiary === value.beneficiary`); + if (!(expectedValue.holder === value.holder)) throw new Error(`expectedValue.holder === value.holder`); + if (!(expectedValue.tokenId === value.tokenId)) throw new Error(`expectedValue.tokenId === value.tokenId`); }; export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { @@ -39,7 +39,6 @@ export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: str return mintToken(privateKey, titleEscrowParameter); }; - export const checkMintSuccess = (results: string): MintData => { const statusLine = extractStatus(results, EndStatus.success, "Token with hash "); if (statusLine.length <= 0) throw new Error("Minting failed"); @@ -70,16 +69,9 @@ export const checkMintSuccess = (results: string): MintData => { export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); const results = run(command); - const {address: tokenRegistry, tokenId, beneficiary, holder} = checkMintSuccess(results); - const signer = getSigner(titleEscrowParameter.network, privateKey); - // let titleEscrowAddress: string = retrieveTitleEscrowOwner( - // signer, - // tokenRegistry, - // tokenId, - // ); + const { address: tokenRegistry, tokenId } = checkMintSuccess(results); return { tokenRegistry, tokenId, - // titleEscrowAddress, }; }; diff --git a/src/e2e/utils/bootstrap/nominate.ts b/src/e2e/utils/bootstrap/nominate.ts index ec476111..71477af1 100644 --- a/src/e2e/utils/bootstrap/nominate.ts +++ b/src/e2e/utils/bootstrap/nominate.ts @@ -2,7 +2,7 @@ import { isAddress } from "web3-utils"; import { mintTokenRegistry } from "."; import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { generateNominateCommand } from "../commands"; -import { AddressLength, defaultRunParameters, EndStatus, network, owner, receiver, TokenIdLength, TokenInfo } from "../constants"; +import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength, TokenInfo } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; @@ -12,43 +12,39 @@ export const defaultNominateBeneficiary = { }; export const mintNominatedToken = (privateKey: string, nominee: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); - const { tokenRegistry, tokenId } = tokenDetails; - const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, - tokenId: tokenId, - tokenRegistry: tokenRegistry, - newBeneficiary: nominee, - }; - nominateToken(privateKey, nominateParameter); - return tokenDetails; + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = tokenDetails; + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newBeneficiary: nominee, }; - + nominateToken(privateKey, nominateParameter); + return tokenDetails; +}; - export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { - const command = generateNominateCommand(nominateParameter, privateKey); - const results = run(command); - checkNominateSuccess(results); - }; +export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { + const command = generateNominateCommand(nominateParameter, privateKey); + const results = run(command); + checkNominateSuccess(results); +}; +export const checkNominateSuccess = (results: string): { nominee: string; tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Nomination failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const nominee = titleEscrowAddressLine.substring(177, 177 + AddressLength); + const nomineeIsAddress = isAddress(nominee); + const isValidTokenId = isTokenId(tokenId); -export const checkNominateSuccess = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Nomination failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - const nominee = titleEscrowAddressLine.substring(177, 177 + AddressLength); - - const nomineeIsAddress = isAddress(nominee); - const isValidTokenId = isTokenId(tokenId); - - if (!nomineeIsAddress) throw new Error("Invalid nominee address"); - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - nominee, - tokenId, - }; + if (!nomineeIsAddress) throw new Error("Invalid nominee address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + nominee, + tokenId, }; - \ No newline at end of file +}; diff --git a/src/e2e/utils/bootstrap/reject-surrender.ts b/src/e2e/utils/bootstrap/reject-surrender.ts index fef9f0ec..97a5c4fb 100644 --- a/src/e2e/utils/bootstrap/reject-surrender.ts +++ b/src/e2e/utils/bootstrap/reject-surrender.ts @@ -1,20 +1,18 @@ - import { EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; -export const checkSurrenderRejectSuccess = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); - - const isValidTokenId = isTokenId(tokenId); - - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - tokenId, - }; +export const checkSurrenderRejectSuccess = (results: string): { tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, }; - \ No newline at end of file +}; diff --git a/src/e2e/utils/bootstrap/surrender.ts b/src/e2e/utils/bootstrap/surrender.ts index 4e215ad6..78f16873 100644 --- a/src/e2e/utils/bootstrap/surrender.ts +++ b/src/e2e/utils/bootstrap/surrender.ts @@ -6,43 +6,40 @@ import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; const defaultTitleEscrow = { - ...defaultRunParameters, - beneficiary: owner.ethAddress, - holder: owner.ethAddress, + ...defaultRunParameters, + beneficiary: owner.ethAddress, + holder: owner.ethAddress, }; export const mintSurrenderToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); - const { tokenRegistry, tokenId, titleEscrowAddress } = tokenDetails; - const surrenderParameter: BaseTitleEscrowCommand = { - ...defaultTitleEscrow, - tokenRegistry: tokenRegistry, - tokenId: tokenId, - }; - surrenderToken(privateKey, surrenderParameter); - return tokenDetails; - }; - - - export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { - const command = generateSurrenderCommand(surrenderParameter, privateKey); - const results = run(command); - checkSurrenderSuccess(results); + const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = tokenDetails; + const surrenderParameter: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry: tokenRegistry, + tokenId: tokenId, }; + surrenderToken(privateKey, surrenderParameter); + return tokenDetails; +}; + +export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { + const command = generateSurrenderCommand(surrenderParameter, privateKey); + const results = run(command); + checkSurrenderSuccess(results); +}; +export const checkSurrenderSuccess = (results: string): { tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Nomination failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); -export const checkSurrenderSuccess = (results: string) => { - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Nomination failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - - const isValidTokenId = isTokenId(tokenId); - - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - tokenId, - }; + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, }; - \ No newline at end of file +}; diff --git a/src/e2e/utils/constants.ts b/src/e2e/utils/constants.ts index fc772f8b..e32e45ff 100644 --- a/src/e2e/utils/constants.ts +++ b/src/e2e/utils/constants.ts @@ -1,6 +1,5 @@ // import { NonceManager } from "@ethersproject/experimental"; import { constants } from "@govtechsg/token-registry"; -import { providers, Wallet } from "ethers"; export const { contractAddress } = constants; export const network = "local"; diff --git a/src/e2e/utils/contract-checks.ts b/src/e2e/utils/contract-checks.ts index f344eea9..d47db705 100644 --- a/src/e2e/utils/contract-checks.ts +++ b/src/e2e/utils/contract-checks.ts @@ -53,7 +53,7 @@ export const retrieveEscrowDetails = async ( ): Promise => { //TODO: check ownership const { tokenRegistry, tokenId, titleEscrowAddress } = tokenInfo; - if(!titleEscrowAddress) throw new Error("Escrow Address unspecified"); + if (!titleEscrowAddress) throw new Error("Escrow Address unspecified"); const tokenDetails = await retrieveTokenInfo(signerOrProvider, tokenRegistry); let titleEscrowDetails: TitleEscrowInfo | undefined; if (!titleEscrowAddress) { diff --git a/src/e2e/utils/token-management.ts b/src/e2e/utils/token-management.ts index c5a43384..02d6fefe 100644 --- a/src/e2e/utils/token-management.ts +++ b/src/e2e/utils/token-management.ts @@ -12,11 +12,12 @@ export const generateTokenId = (): string => { throw new Error("Unable to generate tokenIds"); }; -export const isTokenId = (tokenId: string) => { - const hexRegex = /[0-9A-Fa-f]{6}/g; +export const isTokenId = (tokenId: string): boolean => { + const hexRegex = /[0-9A-Fa-f]{64}/g; tokenId = tokenId.trim(); const containsHexPrefix = tokenId.substring(0, 2) === "0x"; if (containsHexPrefix) tokenId = tokenId.substring(2); if (tokenId.length <= 0) return false; - return tokenId.match(hexRegex); + const matchedString = tokenId.match(hexRegex); + return matchedString?.length === 1; }; From 3a61c964dbb48d0bad99bd8369850ced73610ee5 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Thu, 19 Jan 2023 01:02:21 +0800 Subject: [PATCH 09/20] fix: check for valid token-registry --- .../title-escrow/acceptSurrendered.test.ts | 1 + .../title-escrow/acceptSurrendered.ts | 8 ++++---- .../endorseNominatedBeneficiary.test.ts | 3 +++ src/implementations/title-escrow/helpers.ts | 18 ++++++++++++++++-- .../title-escrow/nominateBeneficiary.test.ts | 3 +++ .../title-escrow/rejectSurrendered.test.ts | 1 + .../title-escrow/rejectSurrendered.ts | 5 +++-- .../title-escrow/surrenderDocument.test.ts | 3 +++ .../title-escrow/transferHolder.test.ts | 3 +++ .../title-escrow/transferOwners.test.ts | 3 +++ .../token-registry/issue.test.ts | 1 + src/implementations/token-registry/issue.ts | 5 +++-- 12 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/implementations/title-escrow/acceptSurrendered.test.ts b/src/implementations/title-escrow/acceptSurrendered.test.ts index 6ec16afb..2471ebf8 100644 --- a/src/implementations/title-escrow/acceptSurrendered.test.ts +++ b/src/implementations/title-escrow/acceptSurrendered.test.ts @@ -35,6 +35,7 @@ describe("title-escrow", () => { mockedConnectERC721.mockReturnValue({ burn: mockBurnToken, callStatic: { + genesis: jest.fn().mockResolvedValue(0), burn: mockCallStaticBurnToken, }, }); diff --git a/src/implementations/title-escrow/acceptSurrendered.ts b/src/implementations/title-escrow/acceptSurrendered.ts index 010805f8..9cb73dd4 100644 --- a/src/implementations/title-escrow/acceptSurrendered.ts +++ b/src/implementations/title-escrow/acceptSurrendered.ts @@ -2,10 +2,9 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; - import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; -import { TradeTrustToken__factory } from "@govtechsg/token-registry/dist/contracts"; +import { connectToTokenRegistry } from "./helpers"; const { trace } = getLogger("title-escrow:acceptSurrendered"); @@ -17,7 +16,7 @@ export const acceptSurrendered = async ({ ...rest }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); - const tokenRegistryInstance = await TradeTrustToken__factory.connect(address, wallet); + const tokenRegistryInstance = await connectToTokenRegistry({ address, wallet }); if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.burn(tokenId), @@ -27,8 +26,9 @@ export const acceptSurrendered = async ({ } signale.await(`Sending transaction to pool`); - await tokenRegistryInstance.callStatic.burn(tokenId); + console.log(await tokenRegistryInstance.callStatic.burn(tokenId)); const transaction = await tokenRegistryInstance.burn(tokenId); + console.log(transaction); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts index ee1ef662..fbff3be5 100644 --- a/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts +++ b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts @@ -38,6 +38,9 @@ describe("title-escrow", () => { mockedConnectERC721.mockReturnValue({ ownerOf: mockedOwnerOf, + callStatic: { + genesis: jest.fn().mockResolvedValue(0), + }, }); mockedConnectTokenFactory.mockReturnValue({ diff --git a/src/implementations/title-escrow/helpers.ts b/src/implementations/title-escrow/helpers.ts index 41593019..ec22278a 100644 --- a/src/implementations/title-escrow/helpers.ts +++ b/src/implementations/title-escrow/helpers.ts @@ -14,14 +14,28 @@ interface ConnectToTitleEscrowArgs { wallet: Wallet | ConnectedSigner; } +interface ConnectToTokenRegistryArgs { + address: string; + wallet: Wallet | ConnectedSigner; +} + export const connectToTitleEscrow = async ({ tokenId, address, wallet, }: ConnectToTitleEscrowArgs): Promise => { - const tokenRegistry: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + const tokenRegistry: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); const titleEscrowAddress = await tokenRegistry.ownerOf(tokenId); - return await TitleEscrow__factory.connect(titleEscrowAddress, wallet); + return TitleEscrow__factory.connect(titleEscrowAddress, wallet); +}; + +export const connectToTokenRegistry = async ({ + address, + wallet, +}: ConnectToTokenRegistryArgs): Promise => { + const tokenRegistryInstance: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + await tokenRegistryInstance.callStatic.genesis(); + return tokenRegistryInstance; }; interface validateEndorseChangeOwnerArgs { diff --git a/src/implementations/title-escrow/nominateBeneficiary.test.ts b/src/implementations/title-escrow/nominateBeneficiary.test.ts index eb755c91..45a0b229 100644 --- a/src/implementations/title-escrow/nominateBeneficiary.test.ts +++ b/src/implementations/title-escrow/nominateBeneficiary.test.ts @@ -36,6 +36,9 @@ describe("title-escrow", () => { mockGetHolder.mockResolvedValue(mockedHolder); mockedConnectERC721.mockReturnValue({ ownerOf: mockedOwnerOf, + callStatic: { + genesis: jest.fn().mockResolvedValue(0), + }, }); mockedConnectTokenFactory.mockReturnValue({ nominate: mockNominateBeneficiary, diff --git a/src/implementations/title-escrow/rejectSurrendered.test.ts b/src/implementations/title-escrow/rejectSurrendered.test.ts index e8ab72b1..40320fca 100644 --- a/src/implementations/title-escrow/rejectSurrendered.test.ts +++ b/src/implementations/title-escrow/rejectSurrendered.test.ts @@ -67,6 +67,7 @@ describe("title-escrow", () => { filters: { Transfer: mockTransferEvent }, queryFilter: mockQueryFilter, callStatic: { + genesis: jest.fn().mockResolvedValue(0), restore: mockCallStaticRestoreTitle, }, }); diff --git a/src/implementations/title-escrow/rejectSurrendered.ts b/src/implementations/title-escrow/rejectSurrendered.ts index 098fb0ef..de35e1a8 100644 --- a/src/implementations/title-escrow/rejectSurrendered.ts +++ b/src/implementations/title-escrow/rejectSurrendered.ts @@ -1,10 +1,11 @@ -import { TradeTrustToken, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { TradeTrustToken } from "@govtechsg/token-registry/contracts"; import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; +import { connectToTokenRegistry } from "./helpers"; const { trace } = getLogger("title-escrow:acceptSurrendered"); @@ -16,7 +17,7 @@ export const rejectSurrendered = async ({ ...rest }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); - const tokenRegistryInstance: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + const tokenRegistryInstance: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.restore(tokenId), diff --git a/src/implementations/title-escrow/surrenderDocument.test.ts b/src/implementations/title-escrow/surrenderDocument.test.ts index bc01d275..22aa8041 100644 --- a/src/implementations/title-escrow/surrenderDocument.test.ts +++ b/src/implementations/title-escrow/surrenderDocument.test.ts @@ -41,6 +41,9 @@ describe("title-escrow", () => { wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); mockedConnectERC721.mockReturnValue({ + callStatic: { + genesis: jest.fn().mockResolvedValue(0), + }, ownerOf: mockedOwnerOf, }); mockedConnectTitleEscrowFactory.mockReturnValue({ diff --git a/src/implementations/title-escrow/transferHolder.test.ts b/src/implementations/title-escrow/transferHolder.test.ts index 7b6ff058..552c0c1f 100644 --- a/src/implementations/title-escrow/transferHolder.test.ts +++ b/src/implementations/title-escrow/transferHolder.test.ts @@ -36,6 +36,9 @@ describe("title-escrow", () => { }); mockedConnectERC721.mockReturnValue({ ownerOf: mockedOwnerOf, + callStatic: { + genesis: jest.fn().mockResolvedValue(0), + }, }); mockedConnectTokenFactory.mockReturnValue({ transferHolder: mockTransferHolder, diff --git a/src/implementations/title-escrow/transferOwners.test.ts b/src/implementations/title-escrow/transferOwners.test.ts index 9f411473..2ceb6a4d 100644 --- a/src/implementations/title-escrow/transferOwners.test.ts +++ b/src/implementations/title-escrow/transferOwners.test.ts @@ -38,6 +38,9 @@ describe("title-escrow", () => { mockGetHolder.mockReturnValue(mockedHolder); mockedConnectERC721.mockReturnValue({ ownerOf: mockedOwnerOf, + callStatic: { + genesis: jest.fn().mockResolvedValue(0), + }, }); mockedConnectTokenFactory.mockReturnValue({ transferOwners: mockTransferOwners, diff --git a/src/implementations/token-registry/issue.test.ts b/src/implementations/token-registry/issue.test.ts index 1bf33268..a26d5546 100644 --- a/src/implementations/token-registry/issue.test.ts +++ b/src/implementations/token-registry/issue.test.ts @@ -34,6 +34,7 @@ describe("token-registry", () => { const mockTTERC721Contract = { mint: mockedIssue, callStatic: { + genesis: jest.fn().mockResolvedValue(0), mint: mockCallStaticSafeMint, }, }; diff --git a/src/implementations/token-registry/issue.ts b/src/implementations/token-registry/issue.ts index 1b845468..cc750d39 100644 --- a/src/implementations/token-registry/issue.ts +++ b/src/implementations/token-registry/issue.ts @@ -1,10 +1,11 @@ -import { TradeTrustToken, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { TradeTrustToken } from "@govtechsg/token-registry/contracts"; import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; +import { connectToTokenRegistry } from "../title-escrow/helpers"; const { trace } = getLogger("token-registry:issue"); @@ -18,7 +19,7 @@ export const issueToTokenRegistry = async ({ ...rest }: TokenRegistryIssueCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); - const tokenRegistry: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + const tokenRegistry: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); if (dryRun) { await dryRunMode({ From 6a4313db8f613fabe2ef83641c39df509703937b Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Thu, 19 Jan 2023 01:02:42 +0800 Subject: [PATCH 10/20] feat: e2e tests --- .circleci/config.yml | 2 +- src/e2e/accept-surrender.e2e.ts | 37 ++++- src/e2e/all.ts | 16 +- src/e2e/change-holder.e2e.ts | 54 ++++++- src/e2e/deploy.e2e.ts | 2 +- src/e2e/endorse-change-owner.e2e.ts | 126 ++++++++++++++- src/e2e/endorse-transfer.e2e.ts | 147 +++++++++++++++--- src/e2e/mint.e2e.ts | 70 ++++++++- src/e2e/nominate.e2e.ts | 96 ++++++++---- src/e2e/reject-surrender.e2e.ts | 31 +++- src/e2e/surrender.e2e.ts | 65 ++++++-- src/e2e/utils/bootstrap/accept-surrender.ts | 18 --- src/e2e/utils/bootstrap/endorse-transfer.ts | 23 --- src/e2e/utils/constants.ts | 12 +- src/e2e/utils/helpers/accept-surrender.ts | 41 +++++ .../{bootstrap => helpers}/change-holder.ts | 5 +- .../utils/{bootstrap => helpers}/common.ts | 0 .../utils/{bootstrap => helpers}/deploy.ts | 0 src/e2e/utils/helpers/endorse-beneficiary.ts | 48 ++++++ .../{bootstrap => helpers}/endorse-owners.ts | 0 src/e2e/utils/{bootstrap => helpers}/index.ts | 2 +- src/e2e/utils/{bootstrap => helpers}/mint.ts | 0 .../utils/{bootstrap => helpers}/nominate.ts | 0 .../reject-surrender.ts | 0 .../utils/{bootstrap => helpers}/surrender.ts | 0 tsconfig.prod.json | 3 +- 26 files changed, 658 insertions(+), 140 deletions(-) delete mode 100644 src/e2e/utils/bootstrap/accept-surrender.ts delete mode 100644 src/e2e/utils/bootstrap/endorse-transfer.ts create mode 100644 src/e2e/utils/helpers/accept-surrender.ts rename src/e2e/utils/{bootstrap => helpers}/change-holder.ts (91%) rename src/e2e/utils/{bootstrap => helpers}/common.ts (100%) rename src/e2e/utils/{bootstrap => helpers}/deploy.ts (100%) create mode 100644 src/e2e/utils/helpers/endorse-beneficiary.ts rename src/e2e/utils/{bootstrap => helpers}/endorse-owners.ts (100%) rename src/e2e/utils/{bootstrap => helpers}/index.ts (87%) rename src/e2e/utils/{bootstrap => helpers}/mint.ts (100%) rename src/e2e/utils/{bootstrap => helpers}/nominate.ts (100%) rename src/e2e/utils/{bootstrap => helpers}/reject-surrender.ts (100%) rename src/e2e/utils/{bootstrap => helpers}/surrender.ts (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index ff0ba4dc..7dd24d25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ jobs: command: npm run lint - run: name: test - command: npm run test -- --runInBand + command: npm run test -- - run: name: e2e command: npm run e2e diff --git a/src/e2e/accept-surrender.e2e.ts b/src/e2e/accept-surrender.e2e.ts index 3ad4d17f..541323e3 100644 --- a/src/e2e/accept-surrender.e2e.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -1,6 +1,12 @@ -import { checkFailure, checkSurrenderAcceptSuccess, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; +import { + checkFailure, + checkSurrenderAcceptSuccess, + deployTokenRegistry, + mintSurrenderToken, + mintTokenRegistry, +} from "./utils/helpers"; import { generateAcceptSurrenderCommand } from "./utils/commands"; -import { BurnAddress, EmptyTokenID, network, owner } from "./utils/constants"; +import { BurnAddress, defaultRunParameters, EmptyTokenID, owner, receiver } from "./utils/constants"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { run } from "./utils/shell"; @@ -8,10 +14,9 @@ export const acceptSurrender = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); // const errors: Error[] = []; const defaultTitleEscrow = { + ...defaultRunParameters, beneficiary: owner.ethAddress, holder: owner.ethAddress, - network: network, - dryRun: false, }; //"should be able to accept-surrender title-escrow on token-registry" @@ -50,6 +55,26 @@ export const acceptSurrender = async (): Promise => { const results = run(command); checkFailure(results, "null"); } -}; -// npm run dev -- title-escrow accept-surrendered --token-registry 0x0000000000000000000000000000000000000000 --tokenId 0xf32907ec66ac258f058eae34d76b160b0ca520b5c613ca93ae6bb22a8f9c451f --network local -k 0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7 + //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" + { + const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand( + { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, + receiver.privateKey + ); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to accept un-surrendered title-escrow on token-registry" + { + const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const command = generateAcceptSurrenderCommand( + { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, + owner.privateKey + ); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } +}; diff --git a/src/e2e/all.ts b/src/e2e/all.ts index 8f87e861..e9116aa9 100644 --- a/src/e2e/all.ts +++ b/src/e2e/all.ts @@ -6,17 +6,23 @@ import { endorseTransfer } from "./endorse-transfer.e2e"; import { mint } from "./mint.e2e"; import { nominate } from "./nominate.e2e"; import { rejectSurrender } from "./reject-surrender.e2e"; +import { endorseChangeOwner } from "./endorse-change-owner.e2e"; const awaitForDuration = async (runFunction: () => void): Promise => { await runFunction(); + console.log(runFunction.name) }; -awaitForDuration(surrender); -awaitForDuration(acceptSurrender); awaitForDuration(deployDocumentStore); awaitForDuration(deployTokenRegistry); -awaitForDuration(changeHolder); -awaitForDuration(endorseTransfer); + awaitForDuration(mint); -awaitForDuration(nominate); + +awaitForDuration(surrender); awaitForDuration(rejectSurrender); +awaitForDuration(acceptSurrender); + +awaitForDuration(nominate); +awaitForDuration(changeHolder); +awaitForDuration(endorseTransfer); +awaitForDuration(endorseChangeOwner); diff --git a/src/e2e/change-holder.e2e.ts b/src/e2e/change-holder.e2e.ts index 19891e11..e7ad11b0 100644 --- a/src/e2e/change-holder.e2e.ts +++ b/src/e2e/change-holder.e2e.ts @@ -5,11 +5,15 @@ import { checkFailure, defaultTransferHolder, deployTokenRegistry, + mintBurntToken, + mintSurrenderToken, mintTokenRegistry, -} from "./utils/bootstrap"; +} from "./utils/helpers"; import { generateChangeHolderCommand } from "./utils/commands"; import { BurnAddress, EmptyTokenID, owner, receiver } from "./utils/constants"; import { run } from "./utils/shell"; +import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; +import { BigNumber } from "ethers"; // "transfer holder title-escrow" export const changeHolder = async (): Promise => { @@ -26,6 +30,18 @@ export const changeHolder = async (): Promise => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); checkChangeHolderSuccess(results); + + const signer = await getSigner(transferHolder.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.holder === transferHolder.newHolder)) + throw new Error(`titleEscrowInfo.holder === transferHolder.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === transferHolder.tokenRegistry)) + throw new Error(`titleEscrowInfo.registry === transferHolder.address`); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(transferHolder.tokenId)); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); } //holder should be able to transfer holder of title-escrow" @@ -66,7 +82,7 @@ export const changeHolder = async (): Promise => { checkFailure(results, "missing revert data in call exception"); } - //should not be able to transfer holder of invalid title-escrow on token-registry" + //should not be able to transfer holder of title-escrow on invalid token-registry" { const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { @@ -103,4 +119,38 @@ export const changeHolder = async (): Promise => { const results = run(command); checkFailure(results, "missing revert data in call exception"); } + + //should not be able to transfer holder of surrendered title-escrow" + { + console.info("Skipped Test"); + console.info("should not be able to transfer holder of surrendered title-escrow"); + return; + const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: owner.ethAddress, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //should not be able to transfer holder of burnt title-escrow" + { + console.info("Skipped Test"); + console.info("should not be able to transfer holder of burnt title-escrow"); + return; + const { tokenRegistry, tokenId } = mintBurntToken(owner.privateKey, tokenRegistryAddress); + const transferHolder: TitleEscrowTransferHolderCommand = { + ...defaultTransferHolder, + tokenId: tokenId, + tokenRegistry: tokenRegistry, + newHolder: owner.ethAddress, + }; + const command = generateChangeHolderCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } }; diff --git a/src/e2e/deploy.e2e.ts b/src/e2e/deploy.e2e.ts index 62660c7c..f546aff7 100644 --- a/src/e2e/deploy.e2e.ts +++ b/src/e2e/deploy.e2e.ts @@ -4,7 +4,7 @@ import { isAddress } from "web3-utils"; import { defaultRunParameters, EndStatus, owner } from "./utils/constants"; import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "./utils/commands"; import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; -import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/bootstrap"; +import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/helpers"; export const deployTokenRegistry = async (): Promise => { //should be able to deploy token-registry" diff --git a/src/e2e/endorse-change-owner.e2e.ts b/src/e2e/endorse-change-owner.e2e.ts index 1147cdf7..9f7c447d 100644 --- a/src/e2e/endorse-change-owner.e2e.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -1,24 +1,28 @@ import { run } from "./utils/shell"; -import { network, owner, receiver } from "./utils/constants"; +import { BurnAddress, defaultRunParameters, owner, receiver, thirdParty } from "./utils/constants"; import { TitleEscrowEndorseTransferOfOwnersCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateTransferOwnersCommand } from "./utils/commands"; import { + burnToken, + changeHolderToken, checkEndorseOwner, checkFailure, deployTokenRegistry, mintNominatedToken, mintTokenRegistry, -} from "./utils/bootstrap"; + surrenderToken, +} from "./utils/helpers"; +import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; +import { BigNumber } from "ethers"; // "endorse change owner title-escrow" export const endorseChangeOwner = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTransferOwners = { + ...defaultRunParameters, newOwner: receiver.ethAddress, newHolder: receiver.ethAddress, - network: network, - dryRun: false, }; //should be able to endorse change owner title-escrow on token-registry" @@ -39,10 +43,99 @@ export const endorseChangeOwner = async (): Promise => { if (!(beneficiary === transferOwners.newOwner)) throw new Error(`beneficiary === transferOwners.newOwner`); if (!(holder === transferOwners.newHolder)) throw new Error(`holder === transferOwners.newHolder`); if (!(tokenIdResult === transferOwners.tokenId)) throw new Error(`tokenIdResult === transferOwners.tokenId`); + + const signer = await getSigner(transferOwners.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow(signer, transferOwners.tokenRegistry, transferOwners.tokenId); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === transferOwners.newOwner)) + throw new Error(`titleEscrowInfo.beneficiary === transferOwners.beneficiary`); + if (!(titleEscrowInfo.holder === transferOwners.newHolder)) + throw new Error(`titleEscrowInfo.holder === transferOwners.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === transferOwners.tokenRegistry)) + throw new Error(`titleEscrowInfo.registry === transferOwners.address`); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(transferOwners.tokenId)); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } + + //"should not be able to endorse change owner from just beneficiary title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + changeHolderToken(owner.privateKey, { + ...defaultTransferOwners, + tokenRegistry: tokenRegistry, + tokenId: tokenId, + newHolder: receiver.ethAddress, + }); + const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }; + + const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse change owner from nominee title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }; + + const command = generateTransferOwnersCommand(transferHolder, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse surrendered title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); + surrenderToken(owner.privateKey, { + ...defaultTransferOwners, + tokenRegistry, + tokenId, + }); + const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }; + + const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse burnt title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); + burnToken(owner.privateKey, { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }); + const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }; + + const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); } - //should not be able to endorse change owner on un-nominated title-escrow" + //"should not be able to endorse change owner on un-nominated title-escrow" { + console.info("Skipped Test"); + console.info("should not be able to endorse change owner on un-nominated title-escrow"); + return; const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { tokenId: tokenId, @@ -53,4 +146,27 @@ export const endorseChangeOwner = async (): Promise => { const results = run(command); checkFailure(results, ""); } + + //"should not be able to endorse change owner from holder title-escrow on token-registry" + { + console.info("Skipped Test"); + console.info("should not be able to endorse change owner from holder title-escrow on token-registry"); + return; + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + changeHolderToken(owner.privateKey, { + ...defaultTransferOwners, + tokenRegistry: tokenRegistry, + tokenId: tokenId, + newHolder: thirdParty.ethAddress, + }); + const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { + ...defaultTransferOwners, + tokenId, + tokenRegistry, + }; + + const command = generateTransferOwnersCommand(transferHolder, thirdParty.privateKey); + const results = run(command); + checkFailure(results, "null"); + } }; diff --git a/src/e2e/endorse-transfer.e2e.ts b/src/e2e/endorse-transfer.e2e.ts index 70ee5762..8fa96b23 100644 --- a/src/e2e/endorse-transfer.e2e.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -1,54 +1,161 @@ import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; import { - checkEndorseTransfer, + burnToken, + changeHolderToken, + checkEndorseBeneficiary, checkFailure, + defaultTransferBeneficiary, deployTokenRegistry, mintNominatedToken, mintTokenRegistry, -} from "./utils/bootstrap"; + surrenderToken, +} from "./utils/helpers"; import { generateEndorseTransferOwnerCommand } from "./utils/commands"; -import { BurnAddress, network, owner, receiver } from "./utils/constants"; +import { owner, receiver, thirdParty } from "./utils/constants"; import { run } from "./utils/shell"; +import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; +import { BigNumber } from "ethers"; export const endorseTransfer = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - const defaultTransferHolder = { - newBeneficiary: receiver.ethAddress, - network: network, - dryRun: false, - }; - //"should be able to endorse transfer title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken( - owner.privateKey, - defaultTransferHolder.newBeneficiary, - tokenRegistryAddress - ); + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); + const results = run(command); + checkEndorseBeneficiary(results); + + const signer = await getSigner(transferHolder.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === transferHolder.newBeneficiary)) + throw new Error(`titleEscrowInfo.beneficiary === transferHolder.beneficiary`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.registry === transferHolder.tokenRegistry)) + throw new Error(`titleEscrowInfo.registry === transferHolder.tokenRegistry`); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(transferHolder.tokenId)); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } + + //"should not be able to endorse transfer from just beneficiary title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + changeHolderToken(owner.privateKey, { + ...defaultTransferBeneficiary, + tokenRegistry: tokenRegistry, + tokenId: tokenId, + newHolder: receiver.ethAddress, + }); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse transfer from nominee title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse surrendered title-escrow on token-registry" + { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); + surrenderToken(owner.privateKey, { + ...defaultTransferBeneficiary, + tokenRegistry, + tokenId, + }); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferHolder, + ...defaultTransferBeneficiary, tokenId, tokenRegistry, + newBeneficiary: receiver.ethAddress, }; const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkEndorseTransfer(results); + checkFailure(results, "missing revert data in call exception"); } - //"should not be able to endorse un-nominated title-escrow on token-registry" + //"should not be able to endorse burnt title-escrow on token-registry" { + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); + burnToken(owner.privateKey, { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + }); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + newBeneficiary: receiver.ethAddress, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); + const results = run(command); + checkFailure(results, "null"); + } + + ("should not be able to endorse un-nominated title-escrow on token-registry"); + { + console.info("Skipped Test"); + console.info("should not be able to endorse un-nominated title-escrow on token-registry"); + return; const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferHolder, + ...defaultTransferBeneficiary, tokenId, tokenRegistry, - newBeneficiary: BurnAddress, + newBeneficiary: receiver.ethAddress, }; const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, ""); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to endorse transfer from holder title-escrow on token-registry" + { + console.info("Skipped Test"); + console.info("should not be able to endorse transfer from holder title-escrow on token-registry"); + return; + const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + changeHolderToken(owner.privateKey, { + ...defaultTransferBeneficiary, + tokenRegistry: tokenRegistry, + tokenId: tokenId, + newHolder: thirdParty.ethAddress, + }); + const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + ...defaultTransferBeneficiary, + tokenId, + tokenRegistry, + }; + + const command = generateEndorseTransferOwnerCommand(transferHolder, thirdParty.privateKey); + const results = run(command); + checkFailure(results, "null"); } }; diff --git a/src/e2e/mint.e2e.ts b/src/e2e/mint.e2e.ts index 7304e493..27b4b02c 100644 --- a/src/e2e/mint.e2e.ts +++ b/src/e2e/mint.e2e.ts @@ -5,7 +5,7 @@ import { TokenRegistryIssueCommand } from "../commands/token-registry/token-regi import { generateMintTitleEscrowCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; -import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/bootstrap"; +import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/helpers"; export const mint = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); @@ -78,6 +78,74 @@ export const mint = async (): Promise => { if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); } + //"should be able to mint title-escrow for a different beneficiary on token-registry" + { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: receiver.ethAddress, + holder: owner.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + const mintResults = checkMintSuccess(results); + validateMintData(titleEscrowParameter as MintData, mintResults); + + const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + titleEscrowParameter.address, + titleEscrowParameter.tokenId + ); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) + throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if (!(titleEscrowInfo.holder === titleEscrowParameter.holder)) + throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === titleEscrowParameter.address)) + throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } + + //"should be able to mint title-escrow for a different holder on token-registry" + { + const tokenId = generateTokenId(); + const titleEscrowParameter: TokenRegistryIssueCommand = { + address: tokenRegistryAddress, + tokenId: tokenId, + beneficiary: owner.ethAddress, + holder: receiver.ethAddress, + ...defaultRunParameters, + }; + const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); + const results = run(command); + const mintResults = checkMintSuccess(results); + validateMintData(titleEscrowParameter as MintData, mintResults); + + const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + titleEscrowParameter.address, + titleEscrowParameter.tokenId + ); + if (!(titleEscrowInfo.active === true)) throw new Error(`titleEscrowInfo.active === true`); + if (!(titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary)) + throw new Error(`titleEscrowInfo.beneficiary === titleEscrowParameter.beneficiary`); + if (!(titleEscrowInfo.holder === titleEscrowParameter.holder)) + throw new Error(`titleEscrowInfo.holder === titleEscrowParameter.holder`); + if (!(titleEscrowInfo.isHoldingToken === true)) throw new Error(`titleEscrowInfo.isHoldingToken === true`); + if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); + if (!(titleEscrowInfo.registry === titleEscrowParameter.address)) + throw new Error(`titleEscrowInfo.registry === titleEscrowParameter.address`); + const correctTokenID = titleEscrowInfo.tokenId.eq(BigNumber.from(titleEscrowParameter.tokenId)); + if (!(correctTokenID === true)) throw new Error(`correctTokenID === true`); + } + //"should fail with invalid token id" { const titleEscrowParameter: TokenRegistryIssueCommand = { diff --git a/src/e2e/nominate.e2e.ts b/src/e2e/nominate.e2e.ts index fc35daf5..1dc0b563 100644 --- a/src/e2e/nominate.e2e.ts +++ b/src/e2e/nominate.e2e.ts @@ -1,15 +1,16 @@ import { run } from "./utils/shell"; -import { BurnAddress, defaultRunParameters, owner, receiver } from "./utils/constants"; +import { BurnAddress, defaultRunParameters, owner, receiver, thirdParty } from "./utils/constants"; import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateNominateCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { + changeHolderToken, checkFailure, checkNominateSuccess, defaultNominateBeneficiary, deployTokenRegistry, mintTokenRegistry, -} from "./utils/bootstrap"; +} from "./utils/helpers"; export const nominate = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); @@ -17,58 +18,71 @@ export const nominate = async (): Promise => { //"should be able to nominate title-escrow on token-registry" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, ...defaultRunParameters, }; - const command = generateNominateCommand(transferHolder, owner.privateKey); + const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); const nominateInfo = checkNominateSuccess(results); - if (!(nominateInfo.tokenId === transferHolder.tokenId)) - throw new Error(`nominateInfo.tokenId === transferHolder.tokenId`); - if (!(nominateInfo.nominee === transferHolder.newBeneficiary)) - throw new Error(`nominateInfo.nominee === transferHolder.newBeneficiary`); - const signer = await getSigner(transferHolder.network, owner.privateKey); - const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); - if (!(titleEscrowInfo.nominee === transferHolder.newBeneficiary)) - throw new Error(`titleEscrowInfo.nominee === transferHolder.newBeneficiary`); + if (!(nominateInfo.tokenId === nominateParameter.tokenId)) + throw new Error(`nominateInfo.tokenId === nominateParameter.tokenId`); + if (!(nominateInfo.nominee === nominateParameter.newBeneficiary)) + throw new Error(`nominateInfo.nominee === nominateParameter.newBeneficiary`); + const signer = await getSigner(nominateParameter.network, owner.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + nominateParameter.tokenRegistry, + nominateParameter.tokenId + ); + if (!(titleEscrowInfo.nominee === nominateParameter.newBeneficiary)) + throw new Error(`titleEscrowInfo.nominee === nominateParameter.newBeneficiary`); } //"should be able to cancel nomination of title-escrow on token-registry" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const transferReceiverNominee: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId, tokenRegistry, }; - let results = run(generateNominateCommand(transferHolder, owner.privateKey)); - transferHolder.newBeneficiary = BurnAddress; - const command = generateNominateCommand(transferHolder, owner.privateKey); - results = run(command); + const receiverNominateCommand = generateNominateCommand(transferReceiverNominee, owner.privateKey); + const receiverNominateResults = run(receiverNominateCommand); + checkNominateSuccess(receiverNominateResults); + const transferBurnNominee = { + ...transferReceiverNominee, + newBeneficiary: BurnAddress, + }; + const command = generateNominateCommand(transferBurnNominee, owner.privateKey); + const results = run(command); const nominateInfo = checkNominateSuccess(results); - if (!(nominateInfo.tokenId === transferHolder.tokenId)) - throw new Error(`nominateInfo.tokenId === transferHolder.tokenId`); - if (!(nominateInfo.nominee === transferHolder.newBeneficiary)) - throw new Error(`nominateInfo.nominee === transferHolder.newBeneficiary`); + if (!(nominateInfo.tokenId === transferBurnNominee.tokenId)) + throw new Error(`nominateInfo.tokenId === transferBurnNominee.tokenId`); + if (!(nominateInfo.nominee === transferBurnNominee.newBeneficiary)) + throw new Error(`nominateInfo.nominee === transferBurnNominee.newBeneficiary`); - const signer = await getSigner(transferHolder.network, owner.privateKey); - const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); + const signer = await getSigner(transferBurnNominee.network, owner.privateKey); + const titleEscrowInfo = await retrieveTitleEscrow( + signer, + transferBurnNominee.tokenRegistry, + transferBurnNominee.tokenId + ); if (!(titleEscrowInfo.nominee === BurnAddress)) throw new Error(`titleEscrowInfo.nominee === BurnAddress`); } //"should not be able to nominate self" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId, tokenRegistry, newBeneficiary: owner.ethAddress, }; - const command = generateNominateCommand(transferHolder, owner.privateKey); + const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); checkFailure(results, "new beneficiary address is the same as the current beneficiary address"); } @@ -76,13 +90,33 @@ export const nominate = async (): Promise => { //"should not be able to nominate unowned token" { const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, }; - const command = generateNominateCommand(transferHolder, receiver.privateKey); + const command = generateNominateCommand(nominateParameter, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to nominate token as holder" + { + const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + changeHolderToken(owner.privateKey, { + ...defaultNominateBeneficiary, + newHolder: receiver.ethAddress, + tokenId, + tokenRegistry, + }); + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { + ...defaultNominateBeneficiary, + tokenId, + tokenRegistry, + newBeneficiary: thirdParty.ethAddress, + }; + const command = generateNominateCommand(nominateParameter, receiver.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); } @@ -90,26 +124,26 @@ export const nominate = async (): Promise => { //"should not be able to nominate non-existent token" { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", tokenRegistry: tokenRegistryAddress, newBeneficiary: receiver.ethAddress, }; - const command = generateNominateCommand(transferHolder, owner.privateKey); + const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); } //"should not be able to nominate non-existent token registry" { - const transferHolder: TitleEscrowNominateBeneficiaryCommand = { + const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", tokenRegistry: "0x0000000000000000000000000000000000000000", newBeneficiary: receiver.ethAddress, }; - const command = generateNominateCommand(transferHolder, owner.privateKey); + const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); checkFailure(results, "null"); } diff --git a/src/e2e/reject-surrender.e2e.ts b/src/e2e/reject-surrender.e2e.ts index a1de39d8..94bc47ed 100644 --- a/src/e2e/reject-surrender.e2e.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -1,9 +1,9 @@ // import { deployTokenRegistry, mintSurrenderToken } from "./utils/utils"; import { run } from "./utils/shell"; -import { BurnAddress, EmptyTokenID, network, owner } from "./utils/constants"; +import { BurnAddress, defaultRunParameters, EmptyTokenID, owner, receiver } from "./utils/constants"; import { generateRejectSurrenderCommand } from "./utils/commands"; -import { checkFailure, deployTokenRegistry, mintSurrenderToken } from "./utils/bootstrap"; -import { checkSurrenderRejectSuccess } from "./utils/bootstrap"; +import { checkFailure, deployTokenRegistry, mintSurrenderToken, mintTokenRegistry } from "./utils/helpers"; +import { checkSurrenderRejectSuccess } from "./utils/helpers"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; @@ -11,10 +11,9 @@ export const rejectSurrender = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const defaultTitleEscrow = { + ...defaultRunParameters, beneficiary: owner.ethAddress, holder: owner.ethAddress, - network: network, - dryRun: false, }; //"should be able to reject surrender title-escrow on token-registry" @@ -53,4 +52,26 @@ export const rejectSurrender = async (): Promise => { const results = run(command); checkFailure(results, "null"); } + + //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" + { + const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand( + { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, + receiver.privateKey + ); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + //"should not be able to accept un-surrendered title-escrow on token-registry" + { + const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const command = generateRejectSurrenderCommand( + { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, + owner.privateKey + ); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } }; diff --git a/src/e2e/surrender.e2e.ts b/src/e2e/surrender.e2e.ts index 8f8fd6c4..2f4ab97f 100644 --- a/src/e2e/surrender.e2e.ts +++ b/src/e2e/surrender.e2e.ts @@ -4,7 +4,14 @@ import { BaseTitleEscrowCommand } from "../commands/title-escrow/title-escrow-co import { generateSurrenderCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; -import { deployTokenRegistry, mintTokenRegistry, checkSurrenderSuccess, checkFailure } from "./utils/bootstrap"; +import { + deployTokenRegistry, + mintTokenRegistry, + checkSurrenderSuccess, + checkFailure, + changeHolderToken, + nominateAndEndorseBeneficiary, +} from "./utils/helpers"; const defaultTitleEscrow = { ...defaultRunParameters, @@ -15,6 +22,7 @@ const defaultTitleEscrow = { export const surrender = async (): Promise => { const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + // "should be able to surrender title-escrow" { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); @@ -51,6 +59,7 @@ export const surrender = async (): Promise => { } } + // "Should not be able to surrender unowned title-escrow" { const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); @@ -62,20 +71,49 @@ export const surrender = async (): Promise => { const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); checkFailure(results, "missing revert data in call exception"); - const signer = await getSigner(surrenderTitleEscrow.network, owner.privateKey); - const titleEscrowOwner: string = await retrieveTitleEscrowOwner( - signer, - surrenderTitleEscrow.tokenRegistry, - surrenderTitleEscrow.tokenId - ); - if (isAddress(titleEscrowOwner) !== true) { - throw new Error(`(isAddress(titleEscrowOwner) !== true);`); - } - if (titleEscrowOwner === surrenderTitleEscrow.tokenRegistry) { - throw new Error(`(titleEscrowOwner === surrenderTitleEscrow.tokenRegistry);`); - } } + // "Should not be able to surrender title-escrow as beneficiary" + { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry, + tokenId, + }; + nominateAndEndorseBeneficiary(owner.privateKey, { + ...defaultTitleEscrow, + tokenId, + tokenRegistry, + newBeneficiary: receiver.ethAddress, + }); + const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + // "Should not be able to surrender title-escrow as holder" + { + const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + + const surrenderTitleEscrow: BaseTitleEscrowCommand = { + ...defaultTitleEscrow, + tokenRegistry, + tokenId, + }; + changeHolderToken(owner.privateKey, { + ...defaultTitleEscrow, + tokenId, + tokenRegistry, + newHolder: receiver.ethAddress, + }); + const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); + const results = run(command); + checkFailure(results, "missing revert data in call exception"); + } + + // "Should not be able to surrender invalid title-escrow" { const { tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); @@ -89,6 +127,7 @@ export const surrender = async (): Promise => { checkFailure(results, "missing revert data in call exception"); } + // "Should not be able to surrender invalid token-registry" { const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); diff --git a/src/e2e/utils/bootstrap/accept-surrender.ts b/src/e2e/utils/bootstrap/accept-surrender.ts deleted file mode 100644 index cf2043ef..00000000 --- a/src/e2e/utils/bootstrap/accept-surrender.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EndStatus, TokenIdLength } from "../constants"; -import { extractStatus } from "../shell"; -import { isTokenId } from "../token-management"; - -export const checkSurrenderAcceptSuccess = (results: string): { tokenId: string } => { - const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); - - const isValidTokenId = isTokenId(tokenId); - - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - tokenId, - }; -}; diff --git a/src/e2e/utils/bootstrap/endorse-transfer.ts b/src/e2e/utils/bootstrap/endorse-transfer.ts deleted file mode 100644 index e8eadcc4..00000000 --- a/src/e2e/utils/bootstrap/endorse-transfer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { isAddress } from "web3-utils"; -import { AddressLength, EndStatus, TokenIdLength } from "../constants"; -import { extractStatus } from "../shell"; -import { isTokenId } from "../token-management"; - -export const checkEndorseTransfer = (results: string): { beneficiary: string; tokenId: string } => { - const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); - if (statusLine.length <= 0) throw new Error("Minting failed"); - const titleEscrowAddressLine = statusLine[0].lineContent; - const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); - const beneficiary = titleEscrowAddressLine.substring(177, 177 + AddressLength); - - const beneficiaryIsAddress = isAddress(beneficiary); - const isValidTokenId = isTokenId(tokenId); - - if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); - if (!isValidTokenId) throw new Error("Invalid token id"); - - return { - beneficiary, - tokenId, - }; -}; diff --git a/src/e2e/utils/constants.ts b/src/e2e/utils/constants.ts index e32e45ff..047e866a 100644 --- a/src/e2e/utils/constants.ts +++ b/src/e2e/utils/constants.ts @@ -2,6 +2,9 @@ import { constants } from "@govtechsg/token-registry"; export const { contractAddress } = constants; +export const silent = true; +export const verbose = false; + export const network = "local"; export const chainId = 1337; export const forkedNetwork = 5; @@ -13,16 +16,19 @@ export const EmptyTokenID = "0x0000000000000000000000000000000000000000000000000 export const owner = { ethAddress: "0xe0A71284EF59483795053266CB796B65E48B5124", - publicKey: "0x02de2454a05cdb55780b85c04128233e31ac9179235607e4d6fa0c6b38140fb51a", privateKey: "0xe82294532bcfcd8e0763ee5cef194f36f00396be59b94fb418f5f8d83140d9a7", }; export const receiver = { ethAddress: "0xcDFAcbb428DD30ddf6d99875dcad04CbEFcd6E60", - publicKey: "0x0396762cb3d373ddab0685bbd5e45ccaf7481d8deb5b75ab38704fba089abed629", privateKey: "0xc58c1ff75001afdca8cecb61b47f36964febe4188b8f7b26252286ecae5a8879", }; +export const thirdParty = { + ethAddress: "0x391aFf3942857a10958425FebF1fC1938D9F5AE7", + privateKey: "0x3760fb287bee810607433485cfa3fc665c2d682a1816991dccce645b096ae19a", +}; + export const creators = { titleEscrowFactory: contractAddress.TitleEscrowFactory[forkedNetwork], deployer: contractAddress.Deployer[forkedNetwork], @@ -39,8 +45,6 @@ export const AddressLength = 42; export type EndStatusType = typeof EndStatus[keyof typeof EndStatus]; -export const silent = true; -export const verbose = false; export const defaultRunParameters = { network: network, dryRun: false, diff --git a/src/e2e/utils/helpers/accept-surrender.ts b/src/e2e/utils/helpers/accept-surrender.ts new file mode 100644 index 00000000..8c43751e --- /dev/null +++ b/src/e2e/utils/helpers/accept-surrender.ts @@ -0,0 +1,41 @@ +import { BaseTitleEscrowCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { generateAcceptSurrenderCommand } from "../commands"; +import { defaultRunParameters, EndStatus, TokenIdLength } from "../constants"; +import { extractStatus, run } from "../shell"; +import { isTokenId } from "../token-management"; +import { mintSurrenderToken, surrenderToken } from "./surrender"; + +export const mintBurntToken = ( + privateKey: string, + tokenRegistryAddress?: string +): { tokenId: string; tokenRegistry: string } => { + const { tokenId, tokenRegistry } = mintSurrenderToken(privateKey, tokenRegistryAddress); + burnSurrenderedToken(privateKey, { tokenRegistry, tokenId, ...defaultRunParameters }); + return { tokenId, tokenRegistry }; +}; + +export const burnSurrenderedToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { + const command = generateAcceptSurrenderCommand(acceptSurrender, privateKey); + const results = run(command); + checkSurrenderAcceptSuccess(results); +}; + +export const burnToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { + surrenderToken(privateKey, acceptSurrender); + burnSurrenderedToken(privateKey, acceptSurrender); +}; + +export const checkSurrenderAcceptSuccess = (results: string): { tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(55, 55 + TokenIdLength); + + const isValidTokenId = isTokenId(tokenId); + + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + tokenId, + }; +}; diff --git a/src/e2e/utils/bootstrap/change-holder.ts b/src/e2e/utils/helpers/change-holder.ts similarity index 91% rename from src/e2e/utils/bootstrap/change-holder.ts rename to src/e2e/utils/helpers/change-holder.ts index 0c8f4498..72a1749b 100644 --- a/src/e2e/utils/bootstrap/change-holder.ts +++ b/src/e2e/utils/helpers/change-holder.ts @@ -1,14 +1,13 @@ import { isAddress } from "web3-utils"; import { TitleEscrowTransferHolderCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { generateChangeHolderCommand } from "../commands"; -import { AddressLength, EndStatus, network, receiver, TokenIdLength } from "../constants"; +import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; export const defaultTransferHolder = { + ...defaultRunParameters, newHolder: receiver.ethAddress, - network: network, - dryRun: false, }; export const checkChangeHolderSuccess = (results: string): { newHolder: string; tokenId: string } => { diff --git a/src/e2e/utils/bootstrap/common.ts b/src/e2e/utils/helpers/common.ts similarity index 100% rename from src/e2e/utils/bootstrap/common.ts rename to src/e2e/utils/helpers/common.ts diff --git a/src/e2e/utils/bootstrap/deploy.ts b/src/e2e/utils/helpers/deploy.ts similarity index 100% rename from src/e2e/utils/bootstrap/deploy.ts rename to src/e2e/utils/helpers/deploy.ts diff --git a/src/e2e/utils/helpers/endorse-beneficiary.ts b/src/e2e/utils/helpers/endorse-beneficiary.ts new file mode 100644 index 00000000..342f13dd --- /dev/null +++ b/src/e2e/utils/helpers/endorse-beneficiary.ts @@ -0,0 +1,48 @@ +import { isAddress } from "web3-utils"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; +import { generateEndorseTransferOwnerCommand } from "../commands"; +import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength } from "../constants"; +import { extractStatus, run } from "../shell"; +import { isTokenId } from "../token-management"; +import { nominateToken } from "./nominate"; + +export const defaultTransferBeneficiary = { + ...defaultRunParameters, + newBeneficiary: receiver.ethAddress, +}; + +export const nominateAndEndorseBeneficiary = ( + privateKey: string, + transferBeneficiary: TitleEscrowNominateBeneficiaryCommand +): void => { + nominateToken(privateKey, transferBeneficiary); + endorseBeneficiary(privateKey, transferBeneficiary); +}; + +export const endorseBeneficiary = ( + privateKey: string, + transferBeneficiary: TitleEscrowNominateBeneficiaryCommand +): void => { + const command = generateEndorseTransferOwnerCommand(transferBeneficiary, privateKey); + const results = run(command); + checkEndorseBeneficiary(results); +}; + +export const checkEndorseBeneficiary = (results: string): { beneficiary: string; tokenId: string } => { + const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); + if (statusLine.length <= 0) throw new Error("Minting failed"); + const titleEscrowAddressLine = statusLine[0].lineContent; + const tokenId = titleEscrowAddressLine.substring(43, 43 + TokenIdLength); + const beneficiary = titleEscrowAddressLine.substring(177, 177 + AddressLength); + + const beneficiaryIsAddress = isAddress(beneficiary); + const isValidTokenId = isTokenId(tokenId); + + if (!beneficiaryIsAddress) throw new Error("Invalid receipient address"); + if (!isValidTokenId) throw new Error("Invalid token id"); + + return { + beneficiary, + tokenId, + }; +}; diff --git a/src/e2e/utils/bootstrap/endorse-owners.ts b/src/e2e/utils/helpers/endorse-owners.ts similarity index 100% rename from src/e2e/utils/bootstrap/endorse-owners.ts rename to src/e2e/utils/helpers/endorse-owners.ts diff --git a/src/e2e/utils/bootstrap/index.ts b/src/e2e/utils/helpers/index.ts similarity index 87% rename from src/e2e/utils/bootstrap/index.ts rename to src/e2e/utils/helpers/index.ts index 518059ed..241fe517 100644 --- a/src/e2e/utils/bootstrap/index.ts +++ b/src/e2e/utils/helpers/index.ts @@ -2,7 +2,7 @@ export * from "./common"; export * from "./deploy"; export * from "./mint"; export * from "./nominate"; -export * from "./endorse-transfer"; +export * from "./endorse-beneficiary"; export * from "./endorse-owners"; export * from "./change-holder"; export * from "./surrender"; diff --git a/src/e2e/utils/bootstrap/mint.ts b/src/e2e/utils/helpers/mint.ts similarity index 100% rename from src/e2e/utils/bootstrap/mint.ts rename to src/e2e/utils/helpers/mint.ts diff --git a/src/e2e/utils/bootstrap/nominate.ts b/src/e2e/utils/helpers/nominate.ts similarity index 100% rename from src/e2e/utils/bootstrap/nominate.ts rename to src/e2e/utils/helpers/nominate.ts diff --git a/src/e2e/utils/bootstrap/reject-surrender.ts b/src/e2e/utils/helpers/reject-surrender.ts similarity index 100% rename from src/e2e/utils/bootstrap/reject-surrender.ts rename to src/e2e/utils/helpers/reject-surrender.ts diff --git a/src/e2e/utils/bootstrap/surrender.ts b/src/e2e/utils/helpers/surrender.ts similarity index 100% rename from src/e2e/utils/bootstrap/surrender.ts rename to src/e2e/utils/helpers/surrender.ts diff --git a/tsconfig.prod.json b/tsconfig.prod.json index 3c455194..ec95fd2f 100644 --- a/tsconfig.prod.json +++ b/tsconfig.prod.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig", "exclude": [ - "**/*.test.ts" + "**/*.test.ts", + "**/*.e2e.ts" ] } From a5973f2b165091e90c3a33b755bbde4a21f93785 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Thu, 19 Jan 2023 01:11:38 +0800 Subject: [PATCH 11/20] fix: lint --- package-lock.json | 458 ------------------ package.json | 1 - .../title-escrow/accept-surrendered.ts | 1 - src/e2e/all.ts | 2 +- 4 files changed, 1 insertion(+), 461 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c59f67c..c7883073 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1638,464 +1638,6 @@ "@ethersproject/transactions": "^5.4.0" } }, - "@ethersproject/experimental": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/experimental/-/experimental-5.7.0.tgz", - "integrity": "sha512-DWvhuw7Dg8JPyhMbh/CNYOwsTLjXRx/HGkacIL5rBocG8jJC0kmixwoK/J3YblO4vtcyBLMa+sV74RJZK2iyHg==", - "dev": true, - "requires": { - "@ethersproject/web": "^5.7.0", - "ethers": "^5.7.0", - "scrypt-js": "3.0.1" - }, - "dependencies": { - "@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "dev": true, - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "@ethersproject/basex": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", - "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "@ethersproject/contracts": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", - "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", - "dev": true, - "requires": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0" - } - }, - "@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/hdnode": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", - "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/json-wallets": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", - "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", - "dev": true, - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", - "dev": true - }, - "@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/pbkdf2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", - "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/sha2": "^5.7.0" - } - }, - "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "dev": true, - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/providers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", - "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0", - "bech32": "1.1.4", - "ws": "7.4.6" - } - }, - "@ethersproject/random": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", - "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/sha2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", - "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "hash.js": "1.1.7" - } - }, - "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/solidity": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", - "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "dev": true, - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "@ethersproject/units": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", - "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", - "dev": true, - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/wallet": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", - "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", - "dev": true, - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/json-wallets": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "dev": true, - "requires": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/wordlists": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", - "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", - "dev": true, - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "dev": true, - "requires": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - } - } - }, "@ethersproject/hash": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.4.0.tgz", diff --git a/package.json b/package.json index 965e4c1c..8c617ceb 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "@commitlint/cli": "^12.1.4", "@commitlint/config-conventional": "^12.1.4", "@commitlint/prompt": "^12.1.4", - "@ethersproject/experimental": "^5.7.0", "@ls-age/commitlint-circle": "1.0.0", "@semantic-release/exec": "^5.0.0", "@types/debug": "4.1.5", diff --git a/src/commands/title-escrow/accept-surrendered.ts b/src/commands/title-escrow/accept-surrendered.ts index 64af790e..2c8a426e 100644 --- a/src/commands/title-escrow/accept-surrendered.ts +++ b/src/commands/title-escrow/accept-surrendered.ts @@ -35,7 +35,6 @@ export const handler = async (args: TitleEscrowSurrenderDocumentCommand): Promis try { info(`Accepting surrendered document`); const transaction = await acceptSurrendered(args); - console.log(transaction); success(`Surrendered transferable record with hash ${args.tokenId} has been accepted.`); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transaction.transactionHash}`); } catch (e) { diff --git a/src/e2e/all.ts b/src/e2e/all.ts index e9116aa9..49f05058 100644 --- a/src/e2e/all.ts +++ b/src/e2e/all.ts @@ -10,7 +10,7 @@ import { endorseChangeOwner } from "./endorse-change-owner.e2e"; const awaitForDuration = async (runFunction: () => void): Promise => { await runFunction(); - console.log(runFunction.name) + console.log(runFunction.name); }; awaitForDuration(deployDocumentStore); From c2cda9e218e185de9fe219352b13433dd9ad7d53 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Thu, 19 Jan 2023 01:25:19 +0800 Subject: [PATCH 12/20] fix: restore jest serial --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7dd24d25..ff0ba4dc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ jobs: command: npm run lint - run: name: test - command: npm run test -- + command: npm run test -- --runInBand - run: name: e2e command: npm run e2e From 2e5c66a121ffdee9bc0c91085bd793f482297e25 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:50:20 +0800 Subject: [PATCH 13/20] feat: validation --- src/e2e/accept-surrender.e2e.ts | 8 +- src/e2e/all.ts | 2 +- src/e2e/change-holder.e2e.ts | 18 +- src/e2e/endorse-change-owner.e2e.ts | 18 +- src/e2e/endorse-transfer.e2e.ts | 24 +- src/e2e/mint.e2e.ts | 18 +- src/e2e/nominate.e2e.ts | 13 +- src/e2e/reject-surrender.e2e.ts | 8 +- src/e2e/surrender.e2e.ts | 10 +- .../title-escrow/acceptSurrendered.ts | 6 +- .../endorseNominatedBeneficiary.ts | 5 +- src/implementations/title-escrow/helpers.ts | 243 ++++++++++++++++-- .../title-escrow/nominateBeneficiary.ts | 5 +- .../title-escrow/rejectSurrendered.ts | 3 +- .../title-escrow/surrenderDocument.ts | 5 +- .../title-escrow/transferHolder.ts | 8 +- .../title-escrow/transferOwners.ts | 3 +- 17 files changed, 279 insertions(+), 118 deletions(-) diff --git a/src/e2e/accept-surrender.e2e.ts b/src/e2e/accept-surrender.e2e.ts index 541323e3..48d35bde 100644 --- a/src/e2e/accept-surrender.e2e.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -42,7 +42,7 @@ export const acceptSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Unminted Token"); } //"should not be able to accept surrender title-escrow on invalid token-registry" @@ -53,7 +53,7 @@ export const acceptSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" @@ -64,7 +64,7 @@ export const acceptSurrender = async (): Promise => { receiver.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to accept un-surrendered title-escrow on token-registry" @@ -75,6 +75,6 @@ export const acceptSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has not been surrendered"); } }; diff --git a/src/e2e/all.ts b/src/e2e/all.ts index 49f05058..ad8bdebf 100644 --- a/src/e2e/all.ts +++ b/src/e2e/all.ts @@ -24,5 +24,5 @@ awaitForDuration(acceptSurrender); awaitForDuration(nominate); awaitForDuration(changeHolder); -awaitForDuration(endorseTransfer); awaitForDuration(endorseChangeOwner); +awaitForDuration(endorseTransfer); \ No newline at end of file diff --git a/src/e2e/change-holder.e2e.ts b/src/e2e/change-holder.e2e.ts index e7ad11b0..1ceef336 100644 --- a/src/e2e/change-holder.e2e.ts +++ b/src/e2e/change-holder.e2e.ts @@ -79,7 +79,7 @@ export const changeHolder = async (): Promise => { }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Unminted Token"); } //should not be able to transfer holder of title-escrow on invalid token-registry" @@ -92,7 +92,7 @@ export const changeHolder = async (): Promise => { }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //beneficiary should not be able to transfer holder of title-escrow" @@ -117,14 +117,11 @@ export const changeHolder = async (): Promise => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); // Beneficiary attempts to transfer Holder without permission const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } - //should not be able to transfer holder of surrendered title-escrow" + // should not be able to transfer holder of surrendered title-escrow" { - console.info("Skipped Test"); - console.info("should not be able to transfer holder of surrendered title-escrow"); - return; const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { ...defaultTransferHolder, @@ -134,14 +131,11 @@ export const changeHolder = async (): Promise => { }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has already been surrendered"); } //should not be able to transfer holder of burnt title-escrow" { - console.info("Skipped Test"); - console.info("should not be able to transfer holder of burnt title-escrow"); - return; const { tokenRegistry, tokenId } = mintBurntToken(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { ...defaultTransferHolder, @@ -151,6 +145,6 @@ export const changeHolder = async (): Promise => { }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has already been shredded"); } }; diff --git a/src/e2e/endorse-change-owner.e2e.ts b/src/e2e/endorse-change-owner.e2e.ts index 9f7c447d..d79df33d 100644 --- a/src/e2e/endorse-change-owner.e2e.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -76,7 +76,7 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse change owner from nominee title-escrow on token-registry" @@ -90,7 +90,7 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse surrendered title-escrow on token-registry" @@ -109,7 +109,7 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has already been surrendered"); } //"should not be able to endorse burnt title-escrow on token-registry" @@ -128,14 +128,11 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, "Title Escrow has already been shredded"); } //"should not be able to endorse change owner on un-nominated title-escrow" { - console.info("Skipped Test"); - console.info("should not be able to endorse change owner on un-nominated title-escrow"); - return; const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { tokenId: tokenId, @@ -144,14 +141,11 @@ export const endorseChangeOwner = async (): Promise => { }; const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); - checkFailure(results, ""); + checkFailure(results, "Destination wallet has not been nominated"); } //"should not be able to endorse change owner from holder title-escrow on token-registry" { - console.info("Skipped Test"); - console.info("should not be able to endorse change owner from holder title-escrow on token-registry"); - return; const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); changeHolderToken(owner.privateKey, { ...defaultTransferOwners, @@ -167,6 +161,6 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, thirdParty.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } }; diff --git a/src/e2e/endorse-transfer.e2e.ts b/src/e2e/endorse-transfer.e2e.ts index 8fa96b23..94b798da 100644 --- a/src/e2e/endorse-transfer.e2e.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -61,7 +61,7 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse transfer from nominee title-escrow on token-registry" @@ -75,7 +75,7 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse surrendered title-escrow on token-registry" @@ -95,10 +95,10 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has already been surrendered"); } - //"should not be able to endorse burnt title-escrow on token-registry" + // "should not be able to endorse burnt title-escrow on token-registry" { const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); burnToken(owner.privateKey, { @@ -115,14 +115,11 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, "Title Escrow has already been shredded"); } - ("should not be able to endorse un-nominated title-escrow on token-registry"); + // "should not be able to endorse un-nominated title-escrow on token-registry"; { - console.info("Skipped Test"); - console.info("should not be able to endorse un-nominated title-escrow on token-registry"); - return; const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { ...defaultTransferBeneficiary, @@ -133,14 +130,11 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Destination wallet has not been nominated"); } - //"should not be able to endorse transfer from holder title-escrow on token-registry" + // "should not be able to endorse transfer from holder title-escrow on token-registry" { - console.info("Skipped Test"); - console.info("should not be able to endorse transfer from holder title-escrow on token-registry"); - return; const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); changeHolderToken(owner.privateKey, { ...defaultTransferBeneficiary, @@ -156,6 +150,6 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, thirdParty.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } }; diff --git a/src/e2e/mint.e2e.ts b/src/e2e/mint.e2e.ts index 27b4b02c..90beed9a 100644 --- a/src/e2e/mint.e2e.ts +++ b/src/e2e/mint.e2e.ts @@ -172,7 +172,7 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should fail with invalid beneficiary" @@ -204,20 +204,4 @@ export const mint = async (): Promise => { const results = run(command); checkFailure(results, "missing revert data in call exception"); } - - //"should fail with no funds" - { - const tokenId = generateTokenId(); - const titleEscrowParameter: TokenRegistryIssueCommand = { - ...defaultRunParameters, - address: tokenRegistryAddress, - tokenId: tokenId, - beneficiary: receiver.ethAddress, - holder: receiver.ethAddress, - network: "mainnet", - }; - const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); - const results = run(command); - checkFailure(results, "null"); - } }; diff --git a/src/e2e/nominate.e2e.ts b/src/e2e/nominate.e2e.ts index 1dc0b563..570d44e2 100644 --- a/src/e2e/nominate.e2e.ts +++ b/src/e2e/nominate.e2e.ts @@ -84,7 +84,7 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, "new beneficiary address is the same as the current beneficiary address"); + checkFailure(results, "Destination wallet already has the rights as beneficiary"); } //"should not be able to nominate unowned token" @@ -98,7 +98,7 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to nominate token as holder" @@ -118,12 +118,11 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to nominate non-existent token" { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", @@ -132,10 +131,10 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Unminted Token"); } - //"should not be able to nominate non-existent token registry" + // "should not be able to nominate non-existent token registry" { const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { ...defaultNominateBeneficiary, @@ -145,6 +144,6 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } }; diff --git a/src/e2e/reject-surrender.e2e.ts b/src/e2e/reject-surrender.e2e.ts index 94bc47ed..5fca3e35 100644 --- a/src/e2e/reject-surrender.e2e.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -39,7 +39,7 @@ export const rejectSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Unminted Token"); } //"should not be able to reject surrender title-escrow on invalid token-registry" @@ -50,7 +50,7 @@ export const rejectSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" @@ -61,7 +61,7 @@ export const rejectSurrender = async (): Promise => { receiver.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to accept un-surrendered title-escrow on token-registry" @@ -72,6 +72,6 @@ export const rejectSurrender = async (): Promise => { owner.privateKey ); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Title Escrow has not been surrendered"); } }; diff --git a/src/e2e/surrender.e2e.ts b/src/e2e/surrender.e2e.ts index 2f4ab97f..921593db 100644 --- a/src/e2e/surrender.e2e.ts +++ b/src/e2e/surrender.e2e.ts @@ -70,7 +70,7 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender title-escrow as beneficiary" @@ -90,7 +90,7 @@ export const surrender = async (): Promise => { }); const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender title-escrow as holder" @@ -110,7 +110,7 @@ export const surrender = async (): Promise => { }); const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender invalid title-escrow" @@ -124,7 +124,7 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkFailure(results, "Unminted Token"); } // "Should not be able to surrender invalid token-registry" @@ -138,6 +138,6 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "null"); + checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); } }; diff --git a/src/implementations/title-escrow/acceptSurrendered.ts b/src/implementations/title-escrow/acceptSurrendered.ts index 9cb73dd4..ccd57cdb 100644 --- a/src/implementations/title-escrow/acceptSurrendered.ts +++ b/src/implementations/title-escrow/acceptSurrendered.ts @@ -4,7 +4,7 @@ import { getWalletOrSigner } from "../utils/wallet"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; -import { connectToTokenRegistry } from "./helpers"; +import { connectToTokenRegistry, validateSurrenderMethod } from "./helpers"; const { trace } = getLogger("title-escrow:acceptSurrendered"); @@ -17,6 +17,7 @@ export const acceptSurrendered = async ({ }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const tokenRegistryInstance = await connectToTokenRegistry({ address, wallet }); + await validateSurrenderMethod({tokenRegistry: tokenRegistryInstance, tokenId, wallet}) if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.burn(tokenId), @@ -24,11 +25,8 @@ export const acceptSurrendered = async ({ }); process.exit(0); } - signale.await(`Sending transaction to pool`); - console.log(await tokenRegistryInstance.callStatic.burn(tokenId)); const transaction = await tokenRegistryInstance.burn(tokenId); - console.log(transaction); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/endorseNominatedBeneficiary.ts b/src/implementations/title-escrow/endorseNominatedBeneficiary.ts index df952f28..4bb031d1 100644 --- a/src/implementations/title-escrow/endorseNominatedBeneficiary.ts +++ b/src/implementations/title-escrow/endorseNominatedBeneficiary.ts @@ -1,7 +1,7 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; -import { connectToTitleEscrow, validateNominateBeneficiary } from "./helpers"; +import { connectToTitleEscrow, validateTransferBeneficiary } from "./helpers"; import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; @@ -23,7 +23,8 @@ export const endorseNominatedBeneficiary = async ({ const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); const nominatedBeneficiary = newBeneficiary; - await validateNominateBeneficiary({ beneficiaryNominee: nominatedBeneficiary, titleEscrow }); + const walletAddress = await wallet.getAddress(); + await validateTransferBeneficiary({ to: nominatedBeneficiary, titleEscrow, walletAddress }); if (dryRun) { await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.transferBeneficiary(nominatedBeneficiary), diff --git a/src/implementations/title-escrow/helpers.ts b/src/implementations/title-escrow/helpers.ts index ec22278a..fd46ebca 100644 --- a/src/implementations/title-escrow/helpers.ts +++ b/src/implementations/title-escrow/helpers.ts @@ -1,77 +1,223 @@ + import { TitleEscrow, + TitleEscrowFactory, + TitleEscrowFactory__factory, TitleEscrow__factory, TradeTrustToken, TradeTrustToken__factory, } from "@govtechsg/token-registry/contracts"; -import { Wallet, constants } from "ethers"; +import { Wallet, constants, Contract } from "ethers"; import signale from "signale"; import { ConnectedSigner } from "../utils/wallet"; +export const BurnAddress = "0x000000000000000000000000000000000000dEaD"; + interface ConnectToTitleEscrowArgs { tokenId: string; address: string; - wallet: Wallet | ConnectedSigner; + wallet: UserWallet; } interface ConnectToTokenRegistryArgs { address: string; - wallet: Wallet | ConnectedSigner; + wallet: UserWallet; +} + +type UserWallet = Wallet | ConnectedSigner; + +export const assertAddressIsSmartContract = async ( + address: string, + account: Wallet | ConnectedSigner +): Promise => { + const code = await account.provider.getCode(address); + const isContract = code !== "0x" && code !== "0x0"; // Ganache uses 0x0 instead + if(!isContract) throw new Error(`Address ${address} is not a valid Contract`); +}; + + +interface ERC165Contract extends Contract { + supportsInterface: (interfaceId: string) => Promise; +} + +export const supportsInterface = async ( + contractInstance: ERC165Contract, + interfaceId: string +): Promise => { + let isSameInterfaceType; + try { + isSameInterfaceType = await contractInstance.supportsInterface(interfaceId); + return isSameInterfaceType; + } catch (e) { + if (e instanceof Error) { + if (e.message.includes("revert") || e.message.includes("cannot estimate gas")) { + return false; + } + } + } +}; + +const isJsonString = (jsonString: string) => { + try { + JSON.parse(jsonString); + } catch (e) { + return false; + } + return true; +} + +export const getTitleEscrowAddress = async (tokenRegistry: TradeTrustToken, tokenId: string) => { + try{ + return await tokenRegistry.ownerOf(tokenId); + } catch (e: any) { + if(e?.code === "CALL_EXCEPTION" && e?.reason === "missing revert data in call exception" && e?.data === "0x"){ + const providerError = e?.error; + if(providerError?.code === "SERVER_ERROR" && providerError?.reason === "processing response error"){ + if(isJsonString(providerError?.body)){ + const VMError: any = JSON.parse(providerError?.body); + if(VMError?.error?.message === "VM Exception while processing transaction: revert ERC721: owner query for nonexistent token"){ + throw new Error(`Unminted Token`); + } + } + } + } + throw e; + } } + +export const connectToTitleEscrowAddress = async ( + address: string, + wallet: UserWallet, +): Promise => { + await assertAddressIsSmartContract(address, wallet); + const titleEscrow = TitleEscrow__factory.connect(address, wallet); + // const isTitleEscrow = await supportsInterface(titleEscrow, "0x8a198f04") + // if(!isTitleEscrow) throw new Error(`Address ${titleEscrowAddress} is not a supported escrow contract`) + return titleEscrow; +}; + export const connectToTitleEscrow = async ({ tokenId, address, wallet, }: ConnectToTitleEscrowArgs): Promise => { + await assertAddressIsSmartContract(address, wallet); const tokenRegistry: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); - const titleEscrowAddress = await tokenRegistry.ownerOf(tokenId); - return TitleEscrow__factory.connect(titleEscrowAddress, wallet); + const titleEscrowAddress = await getTitleEscrowAddress(tokenRegistry, tokenId); + if (titleEscrowAddress === BurnAddress) throw new Error(`Title Escrow has already been shredded`); + if (titleEscrowAddress === address) throw new Error(`Title Escrow has already been surrendered`); + await assertAddressIsSmartContract(titleEscrowAddress, wallet); + const titleEscrow = connectToTitleEscrowAddress(titleEscrowAddress, wallet); + return titleEscrow; }; export const connectToTokenRegistry = async ({ address, wallet, }: ConnectToTokenRegistryArgs): Promise => { + await assertAddressIsSmartContract(address, wallet); const tokenRegistryInstance: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + // const isTokenRegistry = await supportsInterface(tokenRegistryInstance, "0x8a198f04") + // if(!isTokenRegistry) throw new Error(`Address ${address} is not a supported token registry contract`) await tokenRegistryInstance.callStatic.genesis(); return tokenRegistryInstance; }; +// State Checks + +export const validateActiveTitleEscrow = async (titleEscrow: TitleEscrow) => { + const activeEscrow = await titleEscrow.active(); + if (!activeEscrow) throw new Error(`Inactive Title Escrow`); +}; + +// Permissions check + +export const EscrowRoles = { + beneficiary: "beneficiary", + holder: "holder", + nominee: "nominee", +} as const; + +export type EscrowRolesType = typeof EscrowRoles[keyof typeof EscrowRoles]; + +const hasTransferRights = async (titleEscrow: TitleEscrow, walletAddress: string, expectedPermissions: string[]) => { + for (const roles of expectedPermissions) { + let rightsHolder = ""; + let results = true; + switch (roles) { + case EscrowRoles.beneficiary: + rightsHolder = await titleEscrow.beneficiary(); + results = results && (rightsHolder === walletAddress); + break; + case EscrowRoles.holder: + rightsHolder = await titleEscrow.holder(); + results = results && (rightsHolder === walletAddress); + break; + case EscrowRoles.nominee: + rightsHolder = await titleEscrow.nominee(); + results = results && (rightsHolder === walletAddress); + break; + default: + throw new Error(`Unimplemented role: ${roles}`); + } + if(!results) return false; + } + return true; +}; + +interface validateTransferArgs { + titleEscrow: TitleEscrow; + to: string; + walletAddress: string; +} + +export const validateTransferHolder = async ({ titleEscrow, to, walletAddress }: validateTransferArgs) => { + await validateActiveTitleEscrow(titleEscrow); + const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder]); + if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); + const isHolder = await hasTransferRights(titleEscrow, to, [EscrowRoles.holder]); + if (isHolder) throw new Error(`Destination wallet already has the rights of holdership`); +}; + +export const validateTransferBeneficiary = async ({ titleEscrow, to, walletAddress }: validateTransferArgs) => { + await validateActiveTitleEscrow(titleEscrow); + const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.beneficiary, EscrowRoles.holder]); + if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); + const isBeneficiary = await hasTransferRights(titleEscrow, to, [EscrowRoles.beneficiary]); + if (isBeneficiary) throw new Error(`Destination wallet already has the rights as beneficiary`); + const isNominated = await hasTransferRights(titleEscrow, to, [EscrowRoles.nominee]); + if (!isNominated) throw new Error(`Destination wallet has not been nominated`); +}; + +export const validateNominateBeneficiary = async ({ + walletAddress, + to, + titleEscrow, +}: validateTransferArgs): Promise => { + await validateActiveTitleEscrow(titleEscrow); + const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.beneficiary]); + if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); + const isBeneficiary = await hasTransferRights(titleEscrow, to, [EscrowRoles.beneficiary]); + if (isBeneficiary) throw new Error(`Destination wallet already has the rights as beneficiary`); +}; + interface validateEndorseChangeOwnerArgs { newHolder: string; newOwner: string; titleEscrow: TitleEscrow; + walletAddress: string; } export const validateEndorseChangeOwner = async ({ newHolder, newOwner, titleEscrow, + walletAddress, }: validateEndorseChangeOwnerArgs): Promise => { - const beneficiary = await titleEscrow.beneficiary(); - const holder = await titleEscrow.holder(); - if (newOwner === beneficiary && newHolder === holder) { - const error = "new owner and new holder addresses are the same as the current owner and holder addresses"; - signale.error(error); - throw new Error(error); - } + await validateTransferHolder({ titleEscrow, to: newHolder, walletAddress }); + await validateTransferBeneficiary({ titleEscrow, to: newOwner, walletAddress }); }; -interface validateNominateBeneficiaryArgs { - beneficiaryNominee: string; - titleEscrow: TitleEscrow; -} -export const validateNominateBeneficiary = async ({ - beneficiaryNominee, - titleEscrow, -}: validateNominateBeneficiaryArgs): Promise => { - const beneficiary = await titleEscrow.beneficiary(); - if (beneficiaryNominee === beneficiary) { - const error = "new beneficiary address is the same as the current beneficiary address"; - signale.error(error); - throw new Error(error); - } -}; interface validateEndorseTransferOwnerArgs { approvedOwner: string | undefined; @@ -88,3 +234,46 @@ export const validateEndorseTransferOwner = ({ throw new Error(error); } }; + +interface validateSurrenderArgs { + titleEscrow: TitleEscrow, + walletAddress: string, +} + +interface validateAcceptSurrenderArgs { + tokenRegistry: TradeTrustToken, + tokenId: string, + wallet: UserWallet, +} + +export const validateSurrender = async ({ + titleEscrow, + walletAddress, +}: validateSurrenderArgs): Promise => { + const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder, EscrowRoles.beneficiary]); + if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); +}; + +export const connectToTitleEscrowFactory = async (tokenRegistry: TradeTrustToken, wallet: UserWallet): Promise => { + const titleEscrowFactoryAddress = await tokenRegistry.titleEscrowFactory(); + await assertAddressIsSmartContract(titleEscrowFactoryAddress, wallet) + const titleEscrowFactory = TitleEscrowFactory__factory.connect(titleEscrowFactoryAddress, wallet); + return titleEscrowFactory; +} + +export const validateSurrenderMethod = async ({ + tokenRegistry, + tokenId, + wallet, +}: validateAcceptSurrenderArgs): Promise => { + const ownerOfTitleEscrow = await getTitleEscrowAddress(tokenRegistry, tokenId); + if(ownerOfTitleEscrow !== tokenRegistry.address) throw new Error(`Title Escrow has not been surrendered`) + const titleEscrowFactory = await connectToTitleEscrowFactory(tokenRegistry, wallet); + // const isTitleEscrowFactory = await supportsInterface(validateAcceptSurrenderArgs, "?") + // if(!isTitleEscrowFactory) throw new Error(`Address ${address} is not a supported title escrow factory contract`) + const titleEscrowAddress = await titleEscrowFactory.getAddress(tokenRegistry.address, tokenId); + const walletAddress = await wallet.getAddress(); + const titleEscrow = await connectToTitleEscrowAddress(titleEscrowAddress, wallet); + const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder, EscrowRoles.beneficiary]); + if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); +}; \ No newline at end of file diff --git a/src/implementations/title-escrow/nominateBeneficiary.ts b/src/implementations/title-escrow/nominateBeneficiary.ts index 7bddd11b..a5f68a46 100644 --- a/src/implementations/title-escrow/nominateBeneficiary.ts +++ b/src/implementations/title-escrow/nominateBeneficiary.ts @@ -19,17 +19,16 @@ export const nominateBeneficiary = async ({ }: TitleEscrowNominateBeneficiaryCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + const walletAddress = await wallet.getAddress(); + await validateNominateBeneficiary({ to: newBeneficiary, titleEscrow, walletAddress }); if (dryRun) { - await validateNominateBeneficiary({ beneficiaryNominee: newBeneficiary, titleEscrow }); await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.nominate(newBeneficiary), network, }); process.exit(0); } - signale.await(`Sending transaction to pool`); - await validateNominateBeneficiary({ beneficiaryNominee: newBeneficiary, titleEscrow }); await titleEscrow.callStatic.nominate(newBeneficiary); const transaction = await titleEscrow.nominate(newBeneficiary); trace(`Tx hash: ${transaction.hash}`); diff --git a/src/implementations/title-escrow/rejectSurrendered.ts b/src/implementations/title-escrow/rejectSurrendered.ts index de35e1a8..bc441c40 100644 --- a/src/implementations/title-escrow/rejectSurrendered.ts +++ b/src/implementations/title-escrow/rejectSurrendered.ts @@ -5,7 +5,7 @@ import { getWalletOrSigner } from "../utils/wallet"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; -import { connectToTokenRegistry } from "./helpers"; +import { connectToTokenRegistry, validateSurrenderMethod } from "./helpers"; const { trace } = getLogger("title-escrow:acceptSurrendered"); @@ -18,6 +18,7 @@ export const rejectSurrendered = async ({ }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const tokenRegistryInstance: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); + await validateSurrenderMethod({tokenRegistry: tokenRegistryInstance, tokenId, wallet}) if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.restore(tokenId), diff --git a/src/implementations/title-escrow/surrenderDocument.ts b/src/implementations/title-escrow/surrenderDocument.ts index 00d62a00..b2186cf8 100644 --- a/src/implementations/title-escrow/surrenderDocument.ts +++ b/src/implementations/title-escrow/surrenderDocument.ts @@ -1,7 +1,7 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; -import { connectToTitleEscrow } from "./helpers"; +import { connectToTitleEscrow, validateSurrender } from "./helpers"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; @@ -17,7 +17,8 @@ export const surrenderDocument = async ({ }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - + const walletAddress = await wallet.getAddress(); + await validateSurrender({titleEscrow, walletAddress}) if (dryRun) { await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.surrender(), diff --git a/src/implementations/title-escrow/transferHolder.ts b/src/implementations/title-escrow/transferHolder.ts index aaa21687..86edc5f0 100644 --- a/src/implementations/title-escrow/transferHolder.ts +++ b/src/implementations/title-escrow/transferHolder.ts @@ -1,7 +1,7 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; -import { connectToTitleEscrow } from "./helpers"; +import { connectToTitleEscrow, validateTransferHolder } from "./helpers"; import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; @@ -19,6 +19,12 @@ export const transferHolder = async ({ }: TitleEscrowTransferHolderCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + const walletAddress = await wallet.getAddress(); + await validateTransferHolder({ + walletAddress, + titleEscrow, + to, + }); if (dryRun) { await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.transferHolder(to), diff --git a/src/implementations/title-escrow/transferOwners.ts b/src/implementations/title-escrow/transferOwners.ts index 2baf0393..33ca7d0c 100644 --- a/src/implementations/title-escrow/transferOwners.ts +++ b/src/implementations/title-escrow/transferOwners.ts @@ -20,7 +20,8 @@ export const transferOwners = async ({ }: TitleEscrowEndorseTransferOfOwnersCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - await validateEndorseChangeOwners({ newHolder, newOwner, titleEscrow }); + const walletAddress = await wallet.getAddress(); + await validateEndorseChangeOwners({ newHolder, newOwner, titleEscrow, walletAddress }); if (dryRun) { await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.transferOwners(newOwner, newHolder), From a6639c5cdbe6849418a058333c9a6f60cff5c4c9 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:56:24 +0800 Subject: [PATCH 14/20] fix: lint --- src/e2e/all.ts | 2 +- .../title-escrow/acceptSurrendered.ts | 2 +- src/implementations/title-escrow/helpers.ts | 100 ++++++++++-------- .../title-escrow/rejectSurrendered.ts | 2 +- .../title-escrow/surrenderDocument.ts | 2 +- 5 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/e2e/all.ts b/src/e2e/all.ts index ad8bdebf..a01b2945 100644 --- a/src/e2e/all.ts +++ b/src/e2e/all.ts @@ -25,4 +25,4 @@ awaitForDuration(acceptSurrender); awaitForDuration(nominate); awaitForDuration(changeHolder); awaitForDuration(endorseChangeOwner); -awaitForDuration(endorseTransfer); \ No newline at end of file +awaitForDuration(endorseTransfer); diff --git a/src/implementations/title-escrow/acceptSurrendered.ts b/src/implementations/title-escrow/acceptSurrendered.ts index ccd57cdb..62b0f596 100644 --- a/src/implementations/title-escrow/acceptSurrendered.ts +++ b/src/implementations/title-escrow/acceptSurrendered.ts @@ -17,7 +17,7 @@ export const acceptSurrendered = async ({ }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const tokenRegistryInstance = await connectToTokenRegistry({ address, wallet }); - await validateSurrenderMethod({tokenRegistry: tokenRegistryInstance, tokenId, wallet}) + await validateSurrenderMethod({ tokenRegistry: tokenRegistryInstance, tokenId, wallet }); if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.burn(tokenId), diff --git a/src/implementations/title-escrow/helpers.ts b/src/implementations/title-escrow/helpers.ts index fd46ebca..6a432705 100644 --- a/src/implementations/title-escrow/helpers.ts +++ b/src/implementations/title-escrow/helpers.ts @@ -1,4 +1,3 @@ - import { TitleEscrow, TitleEscrowFactory, @@ -32,10 +31,9 @@ export const assertAddressIsSmartContract = async ( ): Promise => { const code = await account.provider.getCode(address); const isContract = code !== "0x" && code !== "0x0"; // Ganache uses 0x0 instead - if(!isContract) throw new Error(`Address ${address} is not a valid Contract`); + if (!isContract) throw new Error(`Address ${address} is not a valid Contract`); }; - interface ERC165Contract extends Contract { supportsInterface: (interfaceId: string) => Promise; } @@ -57,25 +55,28 @@ export const supportsInterface = async ( } }; -const isJsonString = (jsonString: string) => { +const isJsonString = (jsonString: string): boolean => { try { - JSON.parse(jsonString); + JSON.parse(jsonString); } catch (e) { - return false; + return false; } return true; -} +}; -export const getTitleEscrowAddress = async (tokenRegistry: TradeTrustToken, tokenId: string) => { - try{ +export const getTitleEscrowAddress = async (tokenRegistry: TradeTrustToken, tokenId: string): Promise => { + try { return await tokenRegistry.ownerOf(tokenId); } catch (e: any) { - if(e?.code === "CALL_EXCEPTION" && e?.reason === "missing revert data in call exception" && e?.data === "0x"){ + if (e?.code === "CALL_EXCEPTION" && e?.reason === "missing revert data in call exception" && e?.data === "0x") { const providerError = e?.error; - if(providerError?.code === "SERVER_ERROR" && providerError?.reason === "processing response error"){ - if(isJsonString(providerError?.body)){ + if (providerError?.code === "SERVER_ERROR" && providerError?.reason === "processing response error") { + if (isJsonString(providerError?.body)) { const VMError: any = JSON.parse(providerError?.body); - if(VMError?.error?.message === "VM Exception while processing transaction: revert ERC721: owner query for nonexistent token"){ + if ( + VMError?.error?.message === + "VM Exception while processing transaction: revert ERC721: owner query for nonexistent token" + ) { throw new Error(`Unminted Token`); } } @@ -83,13 +84,9 @@ export const getTitleEscrowAddress = async (tokenRegistry: TradeTrustToken, toke } throw e; } -} - +}; -export const connectToTitleEscrowAddress = async ( - address: string, - wallet: UserWallet, -): Promise => { +export const connectToTitleEscrowAddress = async (address: string, wallet: UserWallet): Promise => { await assertAddressIsSmartContract(address, wallet); const titleEscrow = TitleEscrow__factory.connect(address, wallet); // const isTitleEscrow = await supportsInterface(titleEscrow, "0x8a198f04") @@ -126,7 +123,7 @@ export const connectToTokenRegistry = async ({ // State Checks -export const validateActiveTitleEscrow = async (titleEscrow: TitleEscrow) => { +export const validateActiveTitleEscrow = async (titleEscrow: TitleEscrow): Promise => { const activeEscrow = await titleEscrow.active(); if (!activeEscrow) throw new Error(`Inactive Title Escrow`); }; @@ -141,27 +138,31 @@ export const EscrowRoles = { export type EscrowRolesType = typeof EscrowRoles[keyof typeof EscrowRoles]; -const hasTransferRights = async (titleEscrow: TitleEscrow, walletAddress: string, expectedPermissions: string[]) => { +const hasTransferRights = async ( + titleEscrow: TitleEscrow, + walletAddress: string, + expectedPermissions: string[] +): Promise => { for (const roles of expectedPermissions) { let rightsHolder = ""; let results = true; switch (roles) { case EscrowRoles.beneficiary: rightsHolder = await titleEscrow.beneficiary(); - results = results && (rightsHolder === walletAddress); + results = results && rightsHolder === walletAddress; break; case EscrowRoles.holder: rightsHolder = await titleEscrow.holder(); - results = results && (rightsHolder === walletAddress); + results = results && rightsHolder === walletAddress; break; case EscrowRoles.nominee: rightsHolder = await titleEscrow.nominee(); - results = results && (rightsHolder === walletAddress); + results = results && rightsHolder === walletAddress; break; - default: + default: throw new Error(`Unimplemented role: ${roles}`); } - if(!results) return false; + if (!results) return false; } return true; }; @@ -172,7 +173,11 @@ interface validateTransferArgs { walletAddress: string; } -export const validateTransferHolder = async ({ titleEscrow, to, walletAddress }: validateTransferArgs) => { +export const validateTransferHolder = async ({ + titleEscrow, + to, + walletAddress, +}: validateTransferArgs): Promise => { await validateActiveTitleEscrow(titleEscrow); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); @@ -180,7 +185,11 @@ export const validateTransferHolder = async ({ titleEscrow, to, walletAddress }: if (isHolder) throw new Error(`Destination wallet already has the rights of holdership`); }; -export const validateTransferBeneficiary = async ({ titleEscrow, to, walletAddress }: validateTransferArgs) => { +export const validateTransferBeneficiary = async ({ + titleEscrow, + to, + walletAddress, +}: validateTransferArgs): Promise => { await validateActiveTitleEscrow(titleEscrow); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.beneficiary, EscrowRoles.holder]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); @@ -218,7 +227,6 @@ export const validateEndorseChangeOwner = async ({ await validateTransferBeneficiary({ titleEscrow, to: newOwner, walletAddress }); }; - interface validateEndorseTransferOwnerArgs { approvedOwner: string | undefined; approvedHolder: string | undefined; @@ -236,30 +244,30 @@ export const validateEndorseTransferOwner = ({ }; interface validateSurrenderArgs { - titleEscrow: TitleEscrow, - walletAddress: string, + titleEscrow: TitleEscrow; + walletAddress: string; } interface validateAcceptSurrenderArgs { - tokenRegistry: TradeTrustToken, - tokenId: string, - wallet: UserWallet, + tokenRegistry: TradeTrustToken; + tokenId: string; + wallet: UserWallet; } -export const validateSurrender = async ({ - titleEscrow, - walletAddress, -}: validateSurrenderArgs): Promise => { +export const validateSurrender = async ({ titleEscrow, walletAddress }: validateSurrenderArgs): Promise => { const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder, EscrowRoles.beneficiary]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); }; -export const connectToTitleEscrowFactory = async (tokenRegistry: TradeTrustToken, wallet: UserWallet): Promise => { +export const connectToTitleEscrowFactory = async ( + tokenRegistry: TradeTrustToken, + wallet: UserWallet +): Promise => { const titleEscrowFactoryAddress = await tokenRegistry.titleEscrowFactory(); - await assertAddressIsSmartContract(titleEscrowFactoryAddress, wallet) + await assertAddressIsSmartContract(titleEscrowFactoryAddress, wallet); const titleEscrowFactory = TitleEscrowFactory__factory.connect(titleEscrowFactoryAddress, wallet); return titleEscrowFactory; -} +}; export const validateSurrenderMethod = async ({ tokenRegistry, @@ -267,13 +275,13 @@ export const validateSurrenderMethod = async ({ wallet, }: validateAcceptSurrenderArgs): Promise => { const ownerOfTitleEscrow = await getTitleEscrowAddress(tokenRegistry, tokenId); - if(ownerOfTitleEscrow !== tokenRegistry.address) throw new Error(`Title Escrow has not been surrendered`) + if (ownerOfTitleEscrow !== tokenRegistry.address) throw new Error(`Title Escrow has not been surrendered`); const titleEscrowFactory = await connectToTitleEscrowFactory(tokenRegistry, wallet); // const isTitleEscrowFactory = await supportsInterface(validateAcceptSurrenderArgs, "?") // if(!isTitleEscrowFactory) throw new Error(`Address ${address} is not a supported title escrow factory contract`) - const titleEscrowAddress = await titleEscrowFactory.getAddress(tokenRegistry.address, tokenId); - const walletAddress = await wallet.getAddress(); - const titleEscrow = await connectToTitleEscrowAddress(titleEscrowAddress, wallet); + const titleEscrowAddress = await titleEscrowFactory.getAddress(tokenRegistry.address, tokenId); + const walletAddress = await wallet.getAddress(); + const titleEscrow = await connectToTitleEscrowAddress(titleEscrowAddress, wallet); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder, EscrowRoles.beneficiary]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); -}; \ No newline at end of file +}; diff --git a/src/implementations/title-escrow/rejectSurrendered.ts b/src/implementations/title-escrow/rejectSurrendered.ts index bc441c40..d86cb8e0 100644 --- a/src/implementations/title-escrow/rejectSurrendered.ts +++ b/src/implementations/title-escrow/rejectSurrendered.ts @@ -18,7 +18,7 @@ export const rejectSurrendered = async ({ }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); const tokenRegistryInstance: TradeTrustToken = await connectToTokenRegistry({ address, wallet }); - await validateSurrenderMethod({tokenRegistry: tokenRegistryInstance, tokenId, wallet}) + await validateSurrenderMethod({ tokenRegistry: tokenRegistryInstance, tokenId, wallet }); if (dryRun) { await dryRunMode({ estimatedGas: await tokenRegistryInstance.estimateGas.restore(tokenId), diff --git a/src/implementations/title-escrow/surrenderDocument.ts b/src/implementations/title-escrow/surrenderDocument.ts index b2186cf8..7dfc0e9e 100644 --- a/src/implementations/title-escrow/surrenderDocument.ts +++ b/src/implementations/title-escrow/surrenderDocument.ts @@ -18,7 +18,7 @@ export const surrenderDocument = async ({ const wallet = await getWalletOrSigner({ network, ...rest }); const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); const walletAddress = await wallet.getAddress(); - await validateSurrender({titleEscrow, walletAddress}) + await validateSurrender({ titleEscrow, walletAddress }); if (dryRun) { await dryRunMode({ estimatedGas: await titleEscrow.estimateGas.surrender(), From e6bd4937bf9f43ede8806f53b76a5322ea0029d0 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Tue, 31 Jan 2023 03:31:27 +0800 Subject: [PATCH 15/20] feat: update jest tests for validation (wip) --- src/implementations/testsHelpers.ts | 177 ++++++++++++++++++ .../title-escrow/acceptSurrendered.test.ts | 51 ++++- src/implementations/title-escrow/helpers.ts | 10 + .../title-escrow/transferOwners.test.ts | 1 + .../token-registry/issue.test.ts | 19 +- 5 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 src/implementations/testsHelpers.ts diff --git a/src/implementations/testsHelpers.ts b/src/implementations/testsHelpers.ts new file mode 100644 index 00000000..5cf8e026 --- /dev/null +++ b/src/implementations/testsHelpers.ts @@ -0,0 +1,177 @@ +import { TradeTrustToken } from "@govtechsg/token-registry/dist/contracts"; +import { BaseContract, BigNumber, constants } from "ethers"; + +export const AddressZero = constants.AddressZero; +export const BurnAddress = "0x000000000000000000000000000000000000dEaD"; +export const EmptyTokenID = "0x0000000000000000000000000000000000000000000000000000000000000000"; +type defaultMockType = jest.Mock; +type SmartContractDataTypes = BigNumber | number | boolean | string; // bytes and unused not included +type EthersResponseType = SmartContractDataTypes | Error; + +export const mockResolve = (value?: EthersResponseType): defaultMockType => { + const fn = jest.fn(); + if (value instanceof Error) { + fn.mockRejectedValue(value); + } else { + fn.mockResolvedValue(value); + } + return fn; +}; + +export const mockReturn = (value?: EthersResponseType): defaultMockType => { + const fn = jest.fn(); + if (value instanceof Error) { + fn.mockImplementation(() => { + throw value; + }); + } else { + fn.mockRejectedValue(value); + } + return fn; +}; + +export const getMockCode = (): defaultMockType => { + const fn = jest.fn(); + fn.mockResolvedValue(true); + return fn; +}; + +export interface ValidContractMockParameters { + supportInterfaceValue?: boolean | Error; +} + +export const getMockContract = ({ supportInterfaceValue = true }: ValidContractMockParameters) => { + const supportInterface = mockResolve(supportInterfaceValue); + return { + supportInterface, + callStatic: { + supportInterface, + }, + }; +}; + +export interface TokenRegistryMockParameters extends ValidContractMockParameters { + ownerOfValue?: string | Error; + address?: string; + titleEscrowAddress?: string; +} + +export const getMockTokenRegistry = ({ + ownerOfValue = AddressZero, + supportInterfaceValue = true, + address = AddressZero, + titleEscrowAddress = AddressZero, +}: TokenRegistryMockParameters): jest.Mock => { + const validContract = getMockContract({ supportInterfaceValue }); + const ownerOf = mockResolve(ownerOfValue); + const genesis = mockResolve(BigNumber.from(0)); + const titleEscrowFactory = mockResolve(titleEscrowAddress); + const contractFunctions = { + ownerOf, + genesis, + titleEscrowFactory, + }; + const mockTokenRegistry = { + ...contractFunctions, + address: address, + callStatic: contractFunctions, + }; + return mergeMockSmartContract({ base: validContract, override: mockTokenRegistry }); +}; + +export interface TokenRegistryMockParameters extends ValidContractMockParameters { + getAddressValue?: string | Error; +} + +export const getMockTitleEscrowFactory = ({ + getAddressValue = AddressZero, + supportInterfaceValue = true, +}: TokenRegistryMockParameters): jest.Mock => { + const validContract = getMockContract({ supportInterfaceValue }); + const getAddress = mockResolve(getAddressValue); + const contractFunctions = { + getAddress, + }; + const mockTokenRegistry = { + ...contractFunctions, + callStatic: contractFunctions, + }; + return mergeMockSmartContract({ base: validContract, override: mockTokenRegistry }); +}; + +export interface TitleEscrowMockParameters extends ValidContractMockParameters { + beneficiaryValue?: string | Error; + holderValue?: string | Error; + nomineeValue?: string | Error; + activeValue?: boolean | Error; +} + +export const getMockTitleEscrow = ({ + beneficiaryValue = AddressZero, + holderValue = AddressZero, + nomineeValue = AddressZero, + activeValue = true, + supportInterfaceValue = true, +}: TitleEscrowMockParameters) => { + const validContract = getMockContract({ supportInterfaceValue }); + const activeContract = mockResolve(activeValue); + const beneficiary = mockResolve(beneficiaryValue); + const holder = mockResolve(holderValue); + const nominee = mockResolve(nomineeValue); + const contractFunctions = { + activeContract, + beneficiary, + holder, + nominee, + }; + const mockTitleEscrow = { + ...contractFunctions, + callStatic: contractFunctions, + }; + return mergeMockSmartContract({ base: validContract, override: mockTitleEscrow }); +}; + +export interface WalletMockParameters { + codeValue?: string | Error; +} + +export const getValidWalletContract = ({ codeValue = `0x` }: WalletMockParameters) => { + const getCode = mockResolve(codeValue); + return { + provider: { + getCode, + }, + }; +}; + +export interface MergeObjectParameters { + base: any; + override: any; +} + +export const mergeMockSmartContract = ({ base, override }: MergeObjectParameters): any => { + // Sowie + override = mergeMockBaseContract(base, override, "functions"); + override = mergeMockBaseContract(base, override, "callStatic"); + override = mergeMockBaseContract(base, override, "estimateGas"); + override = mergeMockBaseContract(base, override, "populateTransaction"); + override = mergeMockBaseContract(base, override, "filters"); + override = mergeMockBaseContract(base, override, "_runningEvents"); + override = mergeMockBaseContract(base, override, "_wrappedEmits"); + return { + ...base, + ...override, + }; +}; + +const mergeMockBaseContract = (base: any, override: any, keyName: key): any => { + if (keyName in override && keyName in base) { + if (typeof base[keyName] === "object" && typeof override[keyName] === "object") { + override[keyName] = { + ...base[keyName], + ...override[keyName], + }; + } + } + return override; +}; diff --git a/src/implementations/title-escrow/acceptSurrendered.test.ts b/src/implementations/title-escrow/acceptSurrendered.test.ts index 2471ebf8..6ef53bdf 100644 --- a/src/implementations/title-escrow/acceptSurrendered.test.ts +++ b/src/implementations/title-escrow/acceptSurrendered.test.ts @@ -1,44 +1,78 @@ -import { TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { + TitleEscrowFactory__factory, + TitleEscrow__factory, + TradeTrustToken__factory, +} from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { + AddressZero, + getMockTitleEscrow, + getMockTitleEscrowFactory, + getMockTokenRegistry, + mergeMockSmartContract, +} from "../testsHelpers"; import { acceptSurrendered } from "./acceptSurrendered"; jest.mock("@govtechsg/token-registry/contracts"); const acceptSurrenderedDocumentParams: TitleEscrowSurrenderDocumentCommand = { - tokenRegistry: "0x1122", - tokenId: "0x12345", + tokenRegistry: "0x0000000000000000000000000000000000000001", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; +const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; + describe("title-escrow", () => { describe("accepts surrendered transferable record", () => { const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + const mockedTitleEscrowFactory: jest.Mock = TitleEscrow__factory as any; + const mockedTitleEscrowFactoryFactory: jest.Mock = TitleEscrowFactory__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTitleEscrowFactory: jest.Mock = mockedTitleEscrowFactoryFactory.connect; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTitleEscrow: jest.Mock = mockedTitleEscrowFactory.connect; const mockBurnToken = jest.fn(); const mockCallStaticBurnToken = jest.fn().mockResolvedValue(undefined); + const tokenRegistryAddress = acceptSurrenderedDocumentParams.tokenRegistry; + const mockBaseTokenRegistry = getMockTokenRegistry({ + ownerOfValue: tokenRegistryAddress, + address: tokenRegistryAddress, + titleEscrowAddress: AddressZero, + }); + let mockTokenRegistry = mockBaseTokenRegistry; + const mockTitleEscrowFactory = getMockTitleEscrowFactory({}); + const mockTitleEscrow = getMockTitleEscrow({ holderValue: walletAddress, beneficiaryValue: walletAddress }); + beforeEach(() => { delete process.env.OA_PRIVATE_KEY; mockedTradeTrustTokenFactory.mockReset(); mockedConnectERC721.mockReset(); - mockBurnToken.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); - - mockedConnectERC721.mockReturnValue({ + const mockCustomTokenRegistry = { burn: mockBurnToken, callStatic: { genesis: jest.fn().mockResolvedValue(0), burn: mockCallStaticBurnToken, }, - }); + }; + + mockTokenRegistry = mergeMockSmartContract({ base: mockBaseTokenRegistry, override: mockCustomTokenRegistry }); + mockedConnectERC721.mockReturnValue(mockTokenRegistry); + mockedConnectTitleEscrow.mockReturnValue(mockTitleEscrow); + mockedConnectTitleEscrowFactory.mockReturnValue(mockTitleEscrowFactory); mockBurnToken.mockClear(); mockCallStaticBurnToken.mockClear(); }); @@ -48,12 +82,9 @@ describe("title-escrow", () => { ...acceptSurrenderedDocumentParams, key: privateKey, }); - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(acceptSurrenderedDocumentParams.tokenRegistry, passedSigner); - expect(mockCallStaticBurnToken).toHaveBeenCalledTimes(1); expect(mockBurnToken).toHaveBeenCalledTimes(1); }); }); diff --git a/src/implementations/title-escrow/helpers.ts b/src/implementations/title-escrow/helpers.ts index 6a432705..88ef978b 100644 --- a/src/implementations/title-escrow/helpers.ts +++ b/src/implementations/title-escrow/helpers.ts @@ -7,6 +7,7 @@ import { TradeTrustToken__factory, } from "@govtechsg/token-registry/contracts"; import { Wallet, constants, Contract } from "ethers"; +import { isAddress } from "ethers/lib/utils"; import signale from "signale"; import { ConnectedSigner } from "../utils/wallet"; @@ -178,6 +179,9 @@ export const validateTransferHolder = async ({ to, walletAddress, }: validateTransferArgs): Promise => { + if (!isAddress(walletAddress)) { + ("Destination Holder is not a valid address"); + } await validateActiveTitleEscrow(titleEscrow); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.holder]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); @@ -190,6 +194,9 @@ export const validateTransferBeneficiary = async ({ to, walletAddress, }: validateTransferArgs): Promise => { + if (!isAddress(walletAddress)) { + ("Destination Beneficiary is not a valid address"); + } await validateActiveTitleEscrow(titleEscrow); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.beneficiary, EscrowRoles.holder]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); @@ -204,6 +211,9 @@ export const validateNominateBeneficiary = async ({ to, titleEscrow, }: validateTransferArgs): Promise => { + if (!isAddress(walletAddress)) { + ("Destination Nominee is not a valid address"); + } await validateActiveTitleEscrow(titleEscrow); const haveRights = await hasTransferRights(titleEscrow, walletAddress, [EscrowRoles.beneficiary]); if (!haveRights) throw new Error(`Wallet lack the rights for the transfer operation`); diff --git a/src/implementations/title-escrow/transferOwners.test.ts b/src/implementations/title-escrow/transferOwners.test.ts index 2ceb6a4d..b03a6c63 100644 --- a/src/implementations/title-escrow/transferOwners.test.ts +++ b/src/implementations/title-escrow/transferOwners.test.ts @@ -37,6 +37,7 @@ describe("title-escrow", () => { mockGetBeneficiary.mockReturnValue(mockedBeneficiary); mockGetHolder.mockReturnValue(mockedHolder); mockedConnectERC721.mockReturnValue({ + supportsInterface: jest.fn().mockReturnValue(true), ownerOf: mockedOwnerOf, callStatic: { genesis: jest.fn().mockResolvedValue(0), diff --git a/src/implementations/token-registry/issue.test.ts b/src/implementations/token-registry/issue.test.ts index a26d5546..80a62c5a 100644 --- a/src/implementations/token-registry/issue.test.ts +++ b/src/implementations/token-registry/issue.test.ts @@ -3,15 +3,16 @@ import { Wallet } from "ethers"; import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { addAddressPrefix } from "../../utils"; +import { getMockTokenRegistry, mergeMockSmartContract } from "../testsHelpers"; import { issueToTokenRegistry } from "./issue"; jest.mock("@govtechsg/token-registry/contracts"); const deployParams: TokenRegistryIssueCommand = { - beneficiary: "0xabcd", - holder: "0xabce", - tokenId: "0xzyxw", - address: "0x1234", + address: "0x0000000000000000000000000000000000000001", + beneficiary: "0x0x0000000000000000000000000000000000000002", + holder: "0x0000000000000000000000000000000000000003", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; @@ -31,20 +32,24 @@ describe("token-registry", () => { wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); - const mockTTERC721Contract = { + const mockBaseTokenRegistry = getMockTokenRegistry({}); + const mockCustomTokenRegistry = { mint: mockedIssue, callStatic: { - genesis: jest.fn().mockResolvedValue(0), mint: mockCallStaticSafeMint, }, }; + const mockTokenRegistry = mergeMockSmartContract({ + base: mockBaseTokenRegistry, + override: mockCustomTokenRegistry, + }); beforeEach(() => { delete process.env.OA_PRIVATE_KEY; mockedTradeTrustTokenFactory.mockClear(); mockCallStaticSafeMint.mockClear(); mockedConnectERC721.mockReset(); - mockedConnectERC721.mockResolvedValue(mockTTERC721Contract); + mockedConnectERC721.mockResolvedValue(mockTokenRegistry); }); it("should pass in the correct params and return the deployed instance", async () => { From a070e2c3084a2ecd3d1c921a368bc6c8fe93e275 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:15:53 +0800 Subject: [PATCH 16/20] feat: fix jest tests --- src/implementations/testsHelpers.ts | 79 ++++++++++++--- .../title-escrow/acceptSurrendered.test.ts | 7 +- .../endorseNominatedBeneficiary.test.ts | 46 +++++---- .../title-escrow/nominateBeneficiary.test.ts | 43 ++++----- .../title-escrow/rejectSurrendered.test.ts | 96 ++++++++++--------- .../title-escrow/surrenderDocument.test.ts | 36 +++---- .../title-escrow/transferHolder.test.ts | 36 +++---- .../title-escrow/transferOwners.test.ts | 62 ++++++------ .../token-registry/issue.test.ts | 4 +- 9 files changed, 237 insertions(+), 172 deletions(-) diff --git a/src/implementations/testsHelpers.ts b/src/implementations/testsHelpers.ts index 5cf8e026..c3a8c73d 100644 --- a/src/implementations/testsHelpers.ts +++ b/src/implementations/testsHelpers.ts @@ -1,5 +1,4 @@ -import { TradeTrustToken } from "@govtechsg/token-registry/dist/contracts"; -import { BaseContract, BigNumber, constants } from "ethers"; +import { BaseContract, BigNumber, constants, providers } from "ethers"; export const AddressZero = constants.AddressZero; export const BurnAddress = "0x000000000000000000000000000000000000dEaD"; @@ -40,7 +39,16 @@ export interface ValidContractMockParameters { supportInterfaceValue?: boolean | Error; } -export const getMockContract = ({ supportInterfaceValue = true }: ValidContractMockParameters) => { +interface MockContractInterface { + supportInterface: jest.Mock; + callStatic: { + supportInterface: jest.Mock; + }; +} + +export const getMockContract = ({ + supportInterfaceValue = true, +}: ValidContractMockParameters): MockContractInterface => { const supportInterface = mockResolve(supportInterfaceValue); return { supportInterface, @@ -53,19 +61,32 @@ export const getMockContract = ({ supportInterfaceValue = true }: ValidContractM export interface TokenRegistryMockParameters extends ValidContractMockParameters { ownerOfValue?: string | Error; address?: string; - titleEscrowAddress?: string; + titleEscrowFactoryAddress?: string; +} + +interface MockTokenRegistryInterface { + ownerOf: jest.Mock; + genesis: jest.Mock; + titleEscrowFactory: jest.Mock; + supportInterfaces: jest.Mock; + callStatic: { + ownerOf: jest.Mock; + genesis: jest.Mock; + titleEscrowFactory: jest.Mock; + supportInterfaces: jest.Mock; + }; } export const getMockTokenRegistry = ({ ownerOfValue = AddressZero, supportInterfaceValue = true, address = AddressZero, - titleEscrowAddress = AddressZero, -}: TokenRegistryMockParameters): jest.Mock => { + titleEscrowFactoryAddress = AddressZero, +}: TokenRegistryMockParameters): MockTokenRegistryInterface => { const validContract = getMockContract({ supportInterfaceValue }); const ownerOf = mockResolve(ownerOfValue); const genesis = mockResolve(BigNumber.from(0)); - const titleEscrowFactory = mockResolve(titleEscrowAddress); + const titleEscrowFactory = mockResolve(titleEscrowFactoryAddress); const contractFunctions = { ownerOf, genesis, @@ -83,10 +104,19 @@ export interface TokenRegistryMockParameters extends ValidContractMockParameters getAddressValue?: string | Error; } +interface MockTitleEscrowFactoryInterface { + getAddress: jest.Mock; + supportInterfaces: jest.Mock; + callStatic: { + getAddress: jest.Mock; + supportInterfaces: jest.Mock; + }; +} + export const getMockTitleEscrowFactory = ({ getAddressValue = AddressZero, supportInterfaceValue = true, -}: TokenRegistryMockParameters): jest.Mock => { +}: TokenRegistryMockParameters): MockTitleEscrowFactoryInterface => { const validContract = getMockContract({ supportInterfaceValue }); const getAddress = mockResolve(getAddressValue); const contractFunctions = { @@ -106,20 +136,35 @@ export interface TitleEscrowMockParameters extends ValidContractMockParameters { activeValue?: boolean | Error; } +interface MockTitleEscrowInterface { + active: jest.Mock; + beneficiary: jest.Mock; + holder: jest.Mock; + nominee: jest.Mock; + supportInterfaces: jest.Mock; + callStatic: { + active: jest.Mock; + beneficiary: jest.Mock; + holder: jest.Mock; + nominee: jest.Mock; + supportInterfaces: jest.Mock; + }; +} + export const getMockTitleEscrow = ({ beneficiaryValue = AddressZero, holderValue = AddressZero, nomineeValue = AddressZero, activeValue = true, supportInterfaceValue = true, -}: TitleEscrowMockParameters) => { +}: TitleEscrowMockParameters): MockTitleEscrowInterface => { const validContract = getMockContract({ supportInterfaceValue }); - const activeContract = mockResolve(activeValue); + const active = mockResolve(activeValue); const beneficiary = mockResolve(beneficiaryValue); const holder = mockResolve(holderValue); const nominee = mockResolve(nomineeValue); const contractFunctions = { - activeContract, + active, beneficiary, holder, nominee, @@ -131,11 +176,20 @@ export const getMockTitleEscrow = ({ return mergeMockSmartContract({ base: validContract, override: mockTitleEscrow }); }; +export const initMockGetCode = (fn?: jest.Mock): void => { + if (!fn) { + const fn = jest.fn(); + fn.mockResolvedValue(`0x`); + } + jest.spyOn(providers.BaseProvider.prototype, "getCode").mockImplementation(fn); +}; export interface WalletMockParameters { codeValue?: string | Error; } -export const getValidWalletContract = ({ codeValue = `0x` }: WalletMockParameters) => { +export const getValidWalletContract = ({ + codeValue = `0x`, +}: WalletMockParameters): { provider: { getCode: jest.Mock } } => { const getCode = mockResolve(codeValue); return { provider: { @@ -150,7 +204,6 @@ export interface MergeObjectParameters { } export const mergeMockSmartContract = ({ base, override }: MergeObjectParameters): any => { - // Sowie override = mergeMockBaseContract(base, override, "functions"); override = mergeMockBaseContract(base, override, "callStatic"); override = mergeMockBaseContract(base, override, "estimateGas"); diff --git a/src/implementations/title-escrow/acceptSurrendered.test.ts b/src/implementations/title-escrow/acceptSurrendered.test.ts index 6ef53bdf..3fc1b649 100644 --- a/src/implementations/title-escrow/acceptSurrendered.test.ts +++ b/src/implementations/title-escrow/acceptSurrendered.test.ts @@ -4,13 +4,13 @@ import { TradeTrustToken__factory, } from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; - import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { AddressZero, getMockTitleEscrow, getMockTitleEscrowFactory, getMockTokenRegistry, + initMockGetCode, mergeMockSmartContract, } from "../testsHelpers"; import { acceptSurrendered } from "./acceptSurrendered"; @@ -47,7 +47,7 @@ describe("title-escrow", () => { const mockBaseTokenRegistry = getMockTokenRegistry({ ownerOfValue: tokenRegistryAddress, address: tokenRegistryAddress, - titleEscrowAddress: AddressZero, + titleEscrowFactoryAddress: AddressZero, }); let mockTokenRegistry = mockBaseTokenRegistry; const mockTitleEscrowFactory = getMockTitleEscrowFactory({}); @@ -64,11 +64,10 @@ describe("title-escrow", () => { const mockCustomTokenRegistry = { burn: mockBurnToken, callStatic: { - genesis: jest.fn().mockResolvedValue(0), burn: mockCallStaticBurnToken, }, }; - + initMockGetCode(); mockTokenRegistry = mergeMockSmartContract({ base: mockBaseTokenRegistry, override: mockCustomTokenRegistry }); mockedConnectERC721.mockReturnValue(mockTokenRegistry); mockedConnectTitleEscrow.mockReturnValue(mockTitleEscrow); diff --git a/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts index fbff3be5..a7e4ddc4 100644 --- a/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts +++ b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts @@ -2,14 +2,15 @@ import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token import { Wallet } from "ethers"; import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { getMockTitleEscrow, getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { endorseNominatedBeneficiary } from "./endorseNominatedBeneficiary"; jest.mock("@govtechsg/token-registry/contracts"); const endorseNominatedBeneficiaryParams: TitleEscrowNominateBeneficiaryCommand = { - tokenId: "0xzyxw", - tokenRegistry: "0x1234", - newBeneficiary: "0x1232", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", + tokenRegistry: "0x0000000000000000000000000000000000000001", + newBeneficiary: "0x0000000000000000000000000000000000000002", network: "goerli", dryRun: false, }; @@ -25,36 +26,50 @@ describe("title-escrow", () => { // @ts-ignore mock static method const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedTitleEscrowAddress = "0x2133"; + const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; + + const mockedTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; const mockedOwnerOf = jest.fn(); mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); const mockTransferOwners = jest.fn(); const mockCallStaticTransferOwners = jest.fn().mockResolvedValue(undefined); - const mockedBeneficiary = "0xdssfs"; + const mockedBeneficiary = "0x0000000000000000000000000000000000000004"; const mockGetBeneficiary = jest.fn(); mockGetBeneficiary.mockReturnValue(mockedBeneficiary); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - }, + const mockBaseTokenRegistry = getMockTokenRegistry({ + ownerOfValue: mockedTitleEscrowAddress, + address: endorseNominatedBeneficiaryParams.tokenRegistry, }); + const mockTokenRegistry = mockBaseTokenRegistry; + const mockBaseTitleEscrow = getMockTitleEscrow({ + holderValue: walletAddress, + beneficiaryValue: walletAddress, + nomineeValue: endorseNominatedBeneficiaryParams.newBeneficiary, + }); + + mockedConnectERC721.mockReturnValue(mockTokenRegistry); - mockedConnectTokenFactory.mockReturnValue({ + const customMockTitleEscrow = { transferBeneficiary: mockTransferOwners, - beneficiary: mockGetBeneficiary, + // beneficiary: mockGetBeneficiary, callStatic: { transferBeneficiary: mockCallStaticTransferOwners, }, - }); + }; + + const mockTitleEscrow = mergeMockSmartContract({ base: mockBaseTitleEscrow, override: customMockTitleEscrow }); + + mockedConnectTokenFactory.mockReturnValue(mockTitleEscrow); mockTransferOwners.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); + initMockGetCode(); + beforeEach(() => { delete process.env.OA_PRIVATE_KEY; mockedTradeTrustTokenFactory.mockClear(); @@ -77,7 +92,6 @@ describe("title-escrow", () => { expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(endorseNominatedBeneficiaryParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(endorseNominatedBeneficiaryParams.tokenId); expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); expect(mockCallStaticTransferOwners).toHaveBeenCalledTimes(1); expect(mockTransferOwners).toHaveBeenCalledTimes(1); @@ -88,10 +102,10 @@ describe("title-escrow", () => { await expect( endorseNominatedBeneficiary({ ...endorseNominatedBeneficiaryParams, - newBeneficiary: "0xdssfs", + newBeneficiary: walletAddress, key: privateKey, }) - ).rejects.toThrow(`new beneficiary address is the same as the current beneficiary address`); + ).rejects.toThrow(`Destination wallet already has the rights as beneficiary`); }); }); }); diff --git a/src/implementations/title-escrow/nominateBeneficiary.test.ts b/src/implementations/title-escrow/nominateBeneficiary.test.ts index 45a0b229..12acf77e 100644 --- a/src/implementations/title-escrow/nominateBeneficiary.test.ts +++ b/src/implementations/title-escrow/nominateBeneficiary.test.ts @@ -2,14 +2,15 @@ import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token import { Wallet } from "ethers"; import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { getMockTitleEscrow, getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { nominateBeneficiary } from "./nominateBeneficiary"; jest.mock("@govtechsg/token-registry/contracts"); const nominateBeneficiaryParams: TitleEscrowNominateBeneficiaryCommand = { - newBeneficiary: "0fosui", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", + newBeneficiary: "0x0000000000000000000000000000000000000002", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", + tokenRegistry: "0x0000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; @@ -24,31 +25,30 @@ describe("title-escrow", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); + // const mockedOwnerOf = jest.fn(); const mockNominateBeneficiary = jest.fn(); - const mockedTitleEscrowAddress = "0x2133"; - const mockedBeneficiary = "0xdssfs"; - const mockedHolder = "0xdsfls"; + const mockedTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; + const mockedBeneficiary = "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"; + const mockedHolder = "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf"; const mockGetBeneficiary = jest.fn(); const mockGetHolder = jest.fn(); const mockCallStaticNominateBeneficiary = jest.fn().mockResolvedValue(undefined); mockGetBeneficiary.mockResolvedValue(mockedBeneficiary); mockGetHolder.mockResolvedValue(mockedHolder); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - }, - }); - mockedConnectTokenFactory.mockReturnValue({ + const mockBaseTokenRegistry = getMockTokenRegistry({ ownerOfValue: mockedTitleEscrowAddress }); + mockedConnectERC721.mockReturnValue(mockBaseTokenRegistry); + const mockBaseTitleEscrow = getMockTitleEscrow({ beneficiaryValue: mockedBeneficiary, holderValue: mockedHolder }); + const mockCustomTitleEscrow = { nominate: mockNominateBeneficiary, - beneficiary: mockGetBeneficiary, - holder: mockGetHolder, + // beneficiary: mockGetBeneficiary, + // holder: mockGetHolder, callStatic: { nominate: mockCallStaticNominateBeneficiary, }, - }); - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + }; + initMockGetCode(); + const mockTitleEscrow = mergeMockSmartContract({ base: mockBaseTitleEscrow, override: mockCustomTitleEscrow }); + mockedConnectTokenFactory.mockReturnValue(mockTitleEscrow); mockNominateBeneficiary.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), @@ -60,7 +60,6 @@ describe("title-escrow", () => { mockedConnectERC721.mockClear(); mockedTokenFactory.mockClear(); mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); mockNominateBeneficiary.mockClear(); mockGetBeneficiary.mockClear(); mockGetHolder.mockClear(); @@ -75,24 +74,22 @@ describe("title-escrow", () => { }); const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(nominateBeneficiaryParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(nominateBeneficiaryParams.tokenId); expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); expect(mockCallStaticNominateBeneficiary).toHaveBeenCalledTimes(1); expect(mockNominateBeneficiary).toHaveBeenCalledTimes(1); }); it("should throw an error if new owner addresses is the same as current owner", async () => { - mockGetBeneficiary.mockReturnValue(nominateBeneficiaryParams.newBeneficiary); const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; await expect( nominateBeneficiary({ ...nominateBeneficiaryParams, + newBeneficiary: mockedBeneficiary, key: privateKey, }) - ).rejects.toThrow("new beneficiary address is the same as the current beneficiary address"); + ).rejects.toThrow("Destination wallet already has the rights as beneficiary"); }); }); }); diff --git a/src/implementations/title-escrow/rejectSurrendered.test.ts b/src/implementations/title-escrow/rejectSurrendered.test.ts index 40320fca..744d0a14 100644 --- a/src/implementations/title-escrow/rejectSurrendered.test.ts +++ b/src/implementations/title-escrow/rejectSurrendered.test.ts @@ -1,39 +1,74 @@ -import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { + TitleEscrowFactory__factory, + TitleEscrow__factory, + TradeTrustToken__factory, +} from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { + getMockTitleEscrow, + getMockTitleEscrowFactory, + getMockTokenRegistry, + initMockGetCode, + mergeMockSmartContract, +} from "../testsHelpers"; import { rejectSurrendered } from "./rejectSurrendered"; jest.mock("@govtechsg/token-registry/contracts"); const rejectSurrenderedDocumentParams: TitleEscrowSurrenderDocumentCommand = { - tokenRegistry: "0x1122", - tokenId: "0x12345", + tokenRegistry: "0x0000000000000000000000000000000000000001", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; +const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; + describe("title-escrow", () => { describe("rejects surrendered transferable record", () => { const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + const mockedTitleEscrowFactory: jest.Mock = TitleEscrow__factory as any; + const mockedTitleEscrowFactoryFactory: jest.Mock = TitleEscrowFactory__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; - const mockedTitleEscrowFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTitleEscrowFactory: jest.Mock = mockedTitleEscrowFactory.connect; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTitleEscrowFactoryFactory: jest.Mock = mockedTitleEscrowFactoryFactory.connect; - const mockedBeneficiary = jest.fn(); - const mockedHolder = jest.fn(); const mockRestoreTitle = jest.fn(); - const mockTransferEvent = jest.fn(); - const mockQueryFilter = jest.fn(); const mockCallStaticRestoreTitle = jest.fn().mockResolvedValue(undefined); - const mockedLastTitleEscrowAddress = "0xMockedLastTitleEscrowAddress"; - const mockedLastBeneficiary = "0xMockedLastBeneficiaryAddress"; - const mockedLastHolder = "0xMockedLastHolderAddress"; + const mockedLastTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; + initMockGetCode(); + + const mockBaseTokenRegistry = getMockTokenRegistry({ + ownerOfValue: rejectSurrenderedDocumentParams.tokenRegistry, + address: rejectSurrenderedDocumentParams.tokenRegistry, + }); + + const mockCustomTokenRegistry = { + restore: mockRestoreTitle, + callStatic: { + restore: mockCallStaticRestoreTitle, + }, + }; + const mockTokenRegistry = mergeMockSmartContract({ + base: mockBaseTokenRegistry, + override: mockCustomTokenRegistry, + }); + const mockTitleEscrow = getMockTitleEscrow({ beneficiaryValue: walletAddress, holderValue: walletAddress }); + const mockTitleEscrowFactory = getMockTitleEscrowFactory({ getAddressValue: mockedLastTitleEscrowAddress }); + + mockRestoreTitle.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); beforeEach(() => { delete process.env.OA_PRIVATE_KEY; @@ -41,41 +76,10 @@ describe("title-escrow", () => { mockedConnectERC721.mockReset(); mockedTitleEscrowFactory.mockReset(); mockedConnectTitleEscrowFactory.mockReset(); - - mockedBeneficiary.mockReturnValue(mockedLastBeneficiary); - mockedHolder.mockReturnValue(mockedLastHolder); - mockRestoreTitle.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - mockTransferEvent.mockReturnValue({ - address: "0x1122", - topics: ["0x00000", null, null, "0x12345"], - }); - mockQueryFilter.mockReturnValue([ - { - args: [mockedLastTitleEscrowAddress, "0x1122"], - }, - ]); - - mockedConnectTitleEscrowFactory.mockReturnValue({ - beneficiary: mockedBeneficiary, - holder: mockedHolder, - }); - mockedConnectERC721.mockReturnValue({ - restore: mockRestoreTitle, - filters: { Transfer: mockTransferEvent }, - queryFilter: mockQueryFilter, - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - restore: mockCallStaticRestoreTitle, - }, - }); - mockedBeneficiary.mockClear(); - mockedHolder.mockClear(); + mockedConnectERC721.mockReturnValue(mockTokenRegistry); + mockedConnectTitleEscrowFactory.mockReturnValue(mockTitleEscrow); + mockedConnectTitleEscrowFactoryFactory.mockReturnValue(mockTitleEscrowFactory); mockRestoreTitle.mockClear(); - mockTransferEvent.mockClear(); - mockQueryFilter.mockClear(); mockCallStaticRestoreTitle.mockClear(); }); @@ -85,9 +89,7 @@ describe("title-escrow", () => { ...rejectSurrenderedDocumentParams, key: privateKey, }); - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(rejectSurrenderedDocumentParams.tokenRegistry, passedSigner); expect(mockCallStaticRestoreTitle).toHaveBeenCalledTimes(1); diff --git a/src/implementations/title-escrow/surrenderDocument.test.ts b/src/implementations/title-escrow/surrenderDocument.test.ts index 22aa8041..42524a8c 100644 --- a/src/implementations/title-escrow/surrenderDocument.test.ts +++ b/src/implementations/title-escrow/surrenderDocument.test.ts @@ -2,17 +2,18 @@ import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token import { Wallet } from "ethers"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { getMockTitleEscrow, getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { surrenderDocument } from "./surrenderDocument"; jest.mock("@govtechsg/token-registry/contracts"); const surrenderDocumentParams: TitleEscrowSurrenderDocumentCommand = { - tokenRegistry: "0x1122", - tokenId: "0x12345", + tokenRegistry: "0x0000000000000000000000000000000000000001", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; - +const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; describe("title-escrow", () => { describe("surrender transferable record", () => { const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; @@ -23,10 +24,14 @@ describe("title-escrow", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTitleEscrowFactory: jest.Mock = mockedTitleEscrowFactory.connect; - const mockedOwnerOf = jest.fn(); + // const mockedOwnerOf = jest.fn(); const mockSurrender = jest.fn(); const mockCallStaticSurrender = jest.fn().mockResolvedValue(undefined); - const mockedTitleEscrowAddress = "0x2133"; + const mockedTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; + initMockGetCode(); + + const mockBaseTokenRegistry = getMockTokenRegistry({ ownerOfValue: mockedTitleEscrowAddress }); + const mockTokenRegistry = mockBaseTokenRegistry; beforeEach(() => { delete process.env.OA_PRIVATE_KEY; @@ -34,26 +39,22 @@ describe("title-escrow", () => { mockedConnectERC721.mockReset(); mockedTitleEscrowFactory.mockReset(); mockedConnectTitleEscrowFactory.mockReset(); - - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); mockSurrender.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); - mockedConnectERC721.mockReturnValue({ - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - }, - ownerOf: mockedOwnerOf, - }); - mockedConnectTitleEscrowFactory.mockReturnValue({ + + mockedConnectERC721.mockReturnValue(mockTokenRegistry); + + const mockBaseTitleEscrow = getMockTitleEscrow({ beneficiaryValue: walletAddress, holderValue: walletAddress }); + const mockCustomTitleEscrow = { surrender: mockSurrender, callStatic: { surrender: mockCallStaticSurrender, }, - }); - - mockedOwnerOf.mockClear(); + }; + const mockTitleEscrow = mergeMockSmartContract({ base: mockBaseTitleEscrow, override: mockCustomTitleEscrow }); + mockedConnectTitleEscrowFactory.mockReturnValue(mockTitleEscrow); mockSurrender.mockClear(); mockCallStaticSurrender.mockClear(); }); @@ -68,7 +69,6 @@ describe("title-escrow", () => { expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(surrenderDocumentParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(surrenderDocumentParams.tokenId); expect(mockedConnectTitleEscrowFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); expect(mockCallStaticSurrender).toHaveBeenCalledTimes(1); expect(mockSurrender).toHaveBeenCalledTimes(1); diff --git a/src/implementations/title-escrow/transferHolder.test.ts b/src/implementations/title-escrow/transferHolder.test.ts index 552c0c1f..5832ec4d 100644 --- a/src/implementations/title-escrow/transferHolder.test.ts +++ b/src/implementations/title-escrow/transferHolder.test.ts @@ -2,50 +2,54 @@ import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token import { Wallet } from "ethers"; import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { getMockTitleEscrow, getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { transferHolder } from "./transferHolder"; jest.mock("@govtechsg/token-registry/contracts"); const transferHolderParams: TitleEscrowTransferHolderCommand = { newHolder: "0xabcd", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", + tokenRegistry: "0x0000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; +const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; + describe("title-escrow", () => { describe("change holder of transferable record", () => { const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; - - const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); + // const mockedOwnerOf = jest.fn(); const mockTransferHolder = jest.fn(); const mockCallStaticTransferHolder = jest.fn().mockResolvedValue(undefined); - const mockedTitleEscrowAddress = "0x2133"; - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + const mockedTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; mockTransferHolder.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - }, - }); - mockedConnectTokenFactory.mockReturnValue({ + + initMockGetCode(); + + const mockBaseTokenRegistry = getMockTokenRegistry({ ownerOfValue: mockedTitleEscrowAddress }); + mockedConnectERC721.mockReturnValue(mockBaseTokenRegistry); + + const mockBaseTitleEscrow = getMockTitleEscrow({ beneficiaryValue: walletAddress, holderValue: walletAddress }); + const mockCustomTitleEscrow = { transferHolder: mockTransferHolder, callStatic: { transferHolder: mockCallStaticTransferHolder, }, - }); + }; + const mockTitleEscrow = mergeMockSmartContract({ base: mockBaseTitleEscrow, override: mockCustomTitleEscrow }); + mockedConnectTokenFactory.mockReturnValue(mockTitleEscrow); beforeEach(() => { delete process.env.OA_PRIVATE_KEY; @@ -53,7 +57,6 @@ describe("title-escrow", () => { mockedConnectERC721.mockClear(); mockedTokenFactory.mockClear(); mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); mockTransferHolder.mockClear(); mockCallStaticTransferHolder.mockClear(); }); @@ -69,7 +72,6 @@ describe("title-escrow", () => { expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(transferHolderParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(transferHolderParams.tokenId); expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); expect(mockCallStaticTransferHolder).toHaveBeenCalledTimes(1); expect(mockTransferHolder).toHaveBeenCalledTimes(1); diff --git a/src/implementations/title-escrow/transferOwners.test.ts b/src/implementations/title-escrow/transferOwners.test.ts index b03a6c63..7bde12d2 100644 --- a/src/implementations/title-escrow/transferOwners.test.ts +++ b/src/implementations/title-escrow/transferOwners.test.ts @@ -2,19 +2,25 @@ import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token import { Wallet } from "ethers"; import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { getMockTitleEscrow, getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { transferOwners } from "./transferOwners"; jest.mock("@govtechsg/token-registry/contracts"); const endorseChangeOwnersParams: TitleEscrowEndorseTransferOfOwnersCommand = { - newHolder: "0xabcd", - newOwner: "0fosui", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", + newHolder: "0x0000000000000000000000000000000000000004", + newOwner: "0x0000000000000000000000000000000000000004", + tokenId: "0x0000000000000000000000000000000000000000000000000000000000000001", + tokenRegistry: "0x0000000000000000000000000000000000000001", network: "goerli", dryRun: false, }; +const mockedTitleEscrowAddress = "0x0000000000000000000000000000000000000003"; +// const mockedBeneficiary = "0x0000000000000000000000000000000000000004"; +// const mockedHolder = "0xdsfls"; +const walletAddress = `0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf`; + describe("title-escrow", () => { describe("endorse change of owners of transferable record", () => { const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; @@ -24,49 +30,42 @@ describe("title-escrow", () => { const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method - const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); + const mockTransferOwners = jest.fn(); const mockCallStaticTransferOwners = jest.fn().mockResolvedValue(undefined); - const mockedTitleEscrowAddress = "0x2133"; - const mockedBeneficiary = "0xdssfs"; - const mockedHolder = "0xdsfls"; - const mockGetBeneficiary = jest.fn(); - const mockGetHolder = jest.fn(); - mockGetBeneficiary.mockReturnValue(mockedBeneficiary); - mockGetHolder.mockReturnValue(mockedHolder); - mockedConnectERC721.mockReturnValue({ - supportsInterface: jest.fn().mockReturnValue(true), - ownerOf: mockedOwnerOf, - callStatic: { - genesis: jest.fn().mockResolvedValue(0), - }, + + const mockBaseTokenRegistry = getMockTokenRegistry({ ownerOfValue: mockedTitleEscrowAddress }); + const mockTokenRegistry = mockBaseTokenRegistry; + mockedConnectERC721.mockReturnValue(mockTokenRegistry); + + const mockBaseTitleEscrow = getMockTitleEscrow({ + beneficiaryValue: walletAddress, + holderValue: walletAddress, + nomineeValue: endorseChangeOwnersParams.newOwner, }); - mockedConnectTokenFactory.mockReturnValue({ + const mockCustomTitleEscrow = { transferOwners: mockTransferOwners, - beneficiary: mockGetBeneficiary, - holder: mockGetHolder, callStatic: { transferOwners: mockCallStaticTransferOwners, }, - }); - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + }; + const mockTitleEscrow = mergeMockSmartContract({ base: mockBaseTitleEscrow, override: mockCustomTitleEscrow }); + mockedConnectTokenFactory.mockReturnValue(mockTitleEscrow); mockTransferOwners.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); + initMockGetCode(); + beforeEach(() => { delete process.env.OA_PRIVATE_KEY; mockedTradeTrustTokenFactory.mockClear(); mockedConnectERC721.mockClear(); mockedTokenFactory.mockClear(); mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); mockTransferOwners.mockClear(); - mockGetBeneficiary.mockClear(); - mockGetHolder.mockClear(); mockCallStaticTransferOwners.mockClear(); }); @@ -81,24 +80,21 @@ describe("title-escrow", () => { expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(endorseChangeOwnersParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(endorseChangeOwnersParams.tokenId); expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockGetBeneficiary).toHaveBeenCalledTimes(1); - expect(mockGetHolder).toHaveBeenCalledTimes(1); expect(mockCallStaticTransferOwners).toHaveBeenCalledTimes(1); expect(mockTransferOwners).toHaveBeenCalledTimes(1); }); it("should throw an error if new owner and new holder addresses are the same as current owner and holder addressses", async () => { - mockGetBeneficiary.mockReturnValue(endorseChangeOwnersParams.newOwner); - mockGetHolder.mockReturnValue(endorseChangeOwnersParams.newHolder); const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; await expect( transferOwners({ ...endorseChangeOwnersParams, + newOwner: walletAddress, + newHolder: walletAddress, key: privateKey, }) - ).rejects.toThrow("new owner and new holder addresses are the same as the current owner and holder addresses"); + ).rejects.toThrow("Destination wallet already has the rights of holdership"); }); }); }); diff --git a/src/implementations/token-registry/issue.test.ts b/src/implementations/token-registry/issue.test.ts index 80a62c5a..cf52f440 100644 --- a/src/implementations/token-registry/issue.test.ts +++ b/src/implementations/token-registry/issue.test.ts @@ -3,7 +3,7 @@ import { Wallet } from "ethers"; import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { addAddressPrefix } from "../../utils"; -import { getMockTokenRegistry, mergeMockSmartContract } from "../testsHelpers"; +import { getMockTokenRegistry, initMockGetCode, mergeMockSmartContract } from "../testsHelpers"; import { issueToTokenRegistry } from "./issue"; jest.mock("@govtechsg/token-registry/contracts"); @@ -44,6 +44,8 @@ describe("token-registry", () => { override: mockCustomTokenRegistry, }); + initMockGetCode(); + beforeEach(() => { delete process.env.OA_PRIVATE_KEY; mockedTradeTrustTokenFactory.mockClear(); From 0c823f5b3132f9944c01312971b553e90d291bb2 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 1 Feb 2023 12:51:24 +0800 Subject: [PATCH 17/20] fix: renaming and scope changes --- package.json | 2 +- src/e2e/accept-surrender.e2e.ts | 32 +++---- src/e2e/change-holder.e2e.ts | 68 +++++++------- src/e2e/deploy.e2e.ts | 6 +- src/e2e/endorse-change-owner.e2e.ts | 66 ++++++++------ src/e2e/endorse-transfer.e2e.ts | 94 ++++++++++++-------- src/e2e/{all.ts => index.ts} | 0 src/e2e/mint.e2e.ts | 34 ++++--- src/e2e/nominate.e2e.ts | 56 ++++++------ src/e2e/reject-surrender.e2e.ts | 26 +++--- src/e2e/surrender.e2e.ts | 42 ++++----- src/e2e/utils/constants.ts | 1 - src/e2e/utils/helpers/accept-surrender.ts | 20 ++--- src/e2e/utils/helpers/change-holder.ts | 8 +- src/e2e/utils/helpers/common.ts | 2 +- src/e2e/utils/helpers/deploy.ts | 10 +-- src/e2e/utils/helpers/endorse-beneficiary.ts | 16 ++-- src/e2e/utils/helpers/endorse-owners.ts | 2 +- src/e2e/utils/helpers/mint.ts | 18 ++-- src/e2e/utils/helpers/nominate.ts | 25 ++++-- src/e2e/utils/helpers/reject-surrender.ts | 2 +- src/e2e/utils/helpers/surrender.ts | 18 ++-- src/e2e/utils/shell.ts | 17 ++-- 23 files changed, 303 insertions(+), 262 deletions(-) rename src/e2e/{all.ts => index.ts} (100%) diff --git a/package.json b/package.json index 0b1b85d0..683757d0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test": "jest --ci", "test:coverage": "npm run test -- --coverage", "test:watch": "npm run test -- --watch", - "e2e-test": "ts-node src/e2e/all.ts", + "e2e-test": "ts-node src/e2e/index.ts", "lint": "eslint . --ext .ts --max-warnings 0", "lint:fix": "eslint . --ext .ts --fix", "benchmark:make-certs": "./scripts/makeCerts.sh 20000", diff --git a/src/e2e/accept-surrender.e2e.ts b/src/e2e/accept-surrender.e2e.ts index 48d35bde..3f1acea9 100644 --- a/src/e2e/accept-surrender.e2e.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -1,9 +1,9 @@ import { - checkFailure, - checkSurrenderAcceptSuccess, - deployTokenRegistry, - mintSurrenderToken, - mintTokenRegistry, + checkE2EFailure, + checkE2ESurrenderAcceptSuccess, + deployE2ETokenRegistry, + mintSurrenderE2EToken, + mintE2ETokenRegistry, } from "./utils/helpers"; import { generateAcceptSurrenderCommand } from "./utils/commands"; import { BurnAddress, defaultRunParameters, EmptyTokenID, owner, receiver } from "./utils/constants"; @@ -11,7 +11,7 @@ import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { run } from "./utils/shell"; export const acceptSurrender = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); // const errors: Error[] = []; const defaultTitleEscrow = { ...defaultRunParameters, @@ -21,7 +21,7 @@ export const acceptSurrender = async (): Promise => { //"should be able to accept-surrender title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); let titleEscrowOwner: string = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); @@ -31,50 +31,50 @@ export const acceptSurrender = async (): Promise => { titleEscrowOwner = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); if (!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")) throw new Error(`!(titleEscrowOwner === "0x000000000000000000000000000000000000dEaD")`); - checkSurrenderAcceptSuccess(results); + checkE2ESurrenderAcceptSuccess(results); } //"should not be able to accept surrender invalid title-escrow on token-registry" { - const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand( { tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, "Unminted Token"); + checkE2EFailure(results, "Unminted Token"); } //"should not be able to accept surrender title-escrow on invalid token-registry" { - const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand( { tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" { - const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand( { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, receiver.privateKey ); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to accept un-surrendered title-escrow on token-registry" { - const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const command = generateAcceptSurrenderCommand( { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, "Title Escrow has not been surrendered"); + checkE2EFailure(results, "Title Escrow has not been surrendered"); } }; diff --git a/src/e2e/change-holder.e2e.ts b/src/e2e/change-holder.e2e.ts index 1ceef336..442a9800 100644 --- a/src/e2e/change-holder.e2e.ts +++ b/src/e2e/change-holder.e2e.ts @@ -1,13 +1,13 @@ import { TitleEscrowTransferHolderCommand } from "../commands/title-escrow/title-escrow-command.type"; import { - changeHolderToken, - checkChangeHolderSuccess, - checkFailure, - defaultTransferHolder, - deployTokenRegistry, - mintBurntToken, - mintSurrenderToken, - mintTokenRegistry, + changeHolderE2EToken, + checkE2EChangeHolderSuccess, + checkE2EFailure, + defaultE2ETransferHolder, + deployE2ETokenRegistry, + mintBurntE2EToken, + mintSurrenderE2EToken, + mintE2ETokenRegistry, } from "./utils/helpers"; import { generateChangeHolderCommand } from "./utils/commands"; import { BurnAddress, EmptyTokenID, owner, receiver } from "./utils/constants"; @@ -17,19 +17,19 @@ import { BigNumber } from "ethers"; // "transfer holder title-escrow" export const changeHolder = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //should be able to transfer holder title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: tokenId, tokenRegistry: tokenRegistry, - ...defaultTransferHolder, + ...defaultE2ETransferHolder, }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkChangeHolderSuccess(results); + checkE2EChangeHolderSuccess(results); const signer = await getSigner(transferHolder.network, receiver.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); @@ -46,19 +46,19 @@ export const changeHolder = async (): Promise => { //holder should be able to transfer holder of title-escrow" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver const initialHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: receiver.ethAddress, }; - changeHolderToken(owner.privateKey, initialHolder); + changeHolderE2EToken(owner.privateKey, initialHolder); // Transfer Holder to Receiver // Holder attempts to transfer Holder with permission const transferHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: owner.ethAddress, @@ -66,50 +66,50 @@ export const changeHolder = async (): Promise => { const command = generateChangeHolderCommand(transferHolder, receiver.privateKey); // Holder attempts to transfer Holder with permission const results = run(command); - checkChangeHolderSuccess(results); + checkE2EChangeHolderSuccess(results); } //should not be able to transfer holder of invalid title-escrow on token-registry" { - const { tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: EmptyTokenID, tokenRegistry: tokenRegistry, - ...defaultTransferHolder, + ...defaultE2ETransferHolder, }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Unminted Token"); + checkE2EFailure(results, "Unminted Token"); } //should not be able to transfer holder of title-escrow on invalid token-registry" { - const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { tokenId: tokenId, tokenRegistry: BurnAddress, - ...defaultTransferHolder, + ...defaultE2ETransferHolder, }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //beneficiary should not be able to transfer holder of title-escrow" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); // Transfer Holder to Receiver const initialHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: receiver.ethAddress, }; - changeHolderToken(owner.privateKey, initialHolder); + changeHolderE2EToken(owner.privateKey, initialHolder); // Transfer Holder to Receiver // Beneficiary attempts to transfer Holder without permission const transferHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: owner.ethAddress, @@ -117,34 +117,34 @@ export const changeHolder = async (): Promise => { const command = generateChangeHolderCommand(transferHolder, owner.privateKey); // Beneficiary attempts to transfer Holder without permission const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } // should not be able to transfer holder of surrendered title-escrow" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: owner.ethAddress, }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been surrendered"); + checkE2EFailure(results, "Title Escrow has already been surrendered"); } //should not be able to transfer holder of burnt title-escrow" { - const { tokenRegistry, tokenId } = mintBurntToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintBurntE2EToken(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowTransferHolderCommand = { - ...defaultTransferHolder, + ...defaultE2ETransferHolder, tokenId: tokenId, tokenRegistry: tokenRegistry, newHolder: owner.ethAddress, }; const command = generateChangeHolderCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been shredded"); + checkE2EFailure(results, "Title Escrow has already been shredded"); } }; diff --git a/src/e2e/deploy.e2e.ts b/src/e2e/deploy.e2e.ts index f546aff7..1579e22d 100644 --- a/src/e2e/deploy.e2e.ts +++ b/src/e2e/deploy.e2e.ts @@ -4,7 +4,7 @@ import { isAddress } from "web3-utils"; import { defaultRunParameters, EndStatus, owner } from "./utils/constants"; import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand } from "./utils/commands"; import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; -import { checkTokenRegistrySuccess, defaultTokenRegistry } from "./utils/helpers"; +import { checkE2ETokenRegistrySuccess, defaultE2ETokenRegistryParams } from "./utils/helpers"; export const deployTokenRegistry = async (): Promise => { //should be able to deploy token-registry" @@ -12,12 +12,12 @@ export const deployTokenRegistry = async (): Promise => { const tokenRegistryParameter: DeployTokenRegistryCommand = { registryName: "Test Token", registrySymbol: "TKN", - ...defaultTokenRegistry, + ...defaultE2ETokenRegistryParams, }; const command = generateDeployTokenRegistryCommand(tokenRegistryParameter, owner.privateKey); const results = run(command); - const tokenRegistryAddress = checkTokenRegistrySuccess(results); + const tokenRegistryAddress = checkE2ETokenRegistrySuccess(results); const signer = getSigner(defaultRunParameters.network, owner.privateKey); const tokenInfo = await retrieveTokenInfo(signer, tokenRegistryAddress); if (!(tokenInfo.name === tokenRegistryParameter.registryName)) { diff --git a/src/e2e/endorse-change-owner.e2e.ts b/src/e2e/endorse-change-owner.e2e.ts index d79df33d..9af962f2 100644 --- a/src/e2e/endorse-change-owner.e2e.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -3,21 +3,21 @@ import { BurnAddress, defaultRunParameters, owner, receiver, thirdParty } from " import { TitleEscrowEndorseTransferOfOwnersCommand } from "../commands/title-escrow/title-escrow-command.type"; import { generateTransferOwnersCommand } from "./utils/commands"; import { - burnToken, - changeHolderToken, - checkEndorseOwner, - checkFailure, - deployTokenRegistry, - mintNominatedToken, - mintTokenRegistry, - surrenderToken, + burnE2EToken, + changeHolderE2EToken, + checkE2EEndorseOwner, + checkE2EFailure, + deployE2ETokenRegistry, + mintNominatedE2EToken, + mintE2ETokenRegistry, + surrenderE2EToken, } from "./utils/helpers"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; // "endorse change owner title-escrow" export const endorseChangeOwner = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); const defaultTransferOwners = { ...defaultRunParameters, @@ -27,7 +27,7 @@ export const endorseChangeOwner = async (): Promise => { //should be able to endorse change owner title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken( + const { tokenRegistry, tokenId } = mintNominatedE2EToken( owner.privateKey, defaultTransferOwners.newOwner, tokenRegistryAddress @@ -39,7 +39,7 @@ export const endorseChangeOwner = async (): Promise => { }; const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); - const { beneficiary, holder, tokenId: tokenIdResult } = checkEndorseOwner(results); + const { beneficiary, holder, tokenId: tokenIdResult } = checkE2EEndorseOwner(results); if (!(beneficiary === transferOwners.newOwner)) throw new Error(`beneficiary === transferOwners.newOwner`); if (!(holder === transferOwners.newHolder)) throw new Error(`holder === transferOwners.newHolder`); if (!(tokenIdResult === transferOwners.tokenId)) throw new Error(`tokenIdResult === transferOwners.tokenId`); @@ -61,8 +61,12 @@ export const endorseChangeOwner = async (): Promise => { //"should not be able to endorse change owner from just beneficiary title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); - changeHolderToken(owner.privateKey, { + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); + changeHolderE2EToken(owner.privateKey, { ...defaultTransferOwners, tokenRegistry: tokenRegistry, tokenId: tokenId, @@ -76,12 +80,16 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse change owner from nominee title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); const transferHolder: TitleEscrowEndorseTransferOfOwnersCommand = { ...defaultTransferOwners, tokenId, @@ -90,13 +98,13 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse surrendered title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); - surrenderToken(owner.privateKey, { + const { tokenRegistry, tokenId } = mintNominatedE2EToken(owner.privateKey, tokenRegistryAddress); + surrenderE2EToken(owner.privateKey, { ...defaultTransferOwners, tokenRegistry, tokenId, @@ -109,13 +117,13 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been surrendered"); + checkE2EFailure(results, "Title Escrow has already been surrendered"); } //"should not be able to endorse burnt title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); - burnToken(owner.privateKey, { + const { tokenRegistry, tokenId } = mintNominatedE2EToken(owner.privateKey, tokenRegistryAddress); + burnE2EToken(owner.privateKey, { ...defaultTransferOwners, tokenId, tokenRegistry, @@ -128,12 +136,12 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been shredded"); + checkE2EFailure(results, "Title Escrow has already been shredded"); } //"should not be able to endorse change owner on un-nominated title-escrow" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferOwners: TitleEscrowEndorseTransferOfOwnersCommand = { tokenId: tokenId, tokenRegistry: tokenRegistry, @@ -141,13 +149,17 @@ export const endorseChangeOwner = async (): Promise => { }; const command = generateTransferOwnersCommand(transferOwners, owner.privateKey); const results = run(command); - checkFailure(results, "Destination wallet has not been nominated"); + checkE2EFailure(results, "Destination wallet has not been nominated"); } //"should not be able to endorse change owner from holder title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); - changeHolderToken(owner.privateKey, { + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); + changeHolderE2EToken(owner.privateKey, { ...defaultTransferOwners, tokenRegistry: tokenRegistry, tokenId: tokenId, @@ -161,6 +173,6 @@ export const endorseChangeOwner = async (): Promise => { const command = generateTransferOwnersCommand(transferHolder, thirdParty.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } }; diff --git a/src/e2e/endorse-transfer.e2e.ts b/src/e2e/endorse-transfer.e2e.ts index 94b798da..df0389b3 100644 --- a/src/e2e/endorse-transfer.e2e.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -1,14 +1,14 @@ import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/title-escrow-command.type"; import { - burnToken, - changeHolderToken, - checkEndorseBeneficiary, - checkFailure, - defaultTransferBeneficiary, - deployTokenRegistry, - mintNominatedToken, - mintTokenRegistry, - surrenderToken, + burnE2EToken, + changeHolderE2EToken, + checkE2EEndorseBeneficiary, + checkE2EFailure, + defaultE2ETransferBeneficiary, + deployE2ETokenRegistry, + mintNominatedE2EToken, + mintE2ETokenRegistry, + surrenderE2EToken, } from "./utils/helpers"; import { generateEndorseTransferOwnerCommand } from "./utils/commands"; import { owner, receiver, thirdParty } from "./utils/constants"; @@ -17,20 +17,24 @@ import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; export const endorseTransfer = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to endorse transfer title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, }; const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkEndorseBeneficiary(results); + checkE2EEndorseBeneficiary(results); const signer = await getSigner(transferHolder.network, receiver.privateKey); const titleEscrowInfo = await retrieveTitleEscrow(signer, transferHolder.tokenRegistry, transferHolder.tokenId); @@ -46,48 +50,56 @@ export const endorseTransfer = async (): Promise => { //"should not be able to endorse transfer from just beneficiary title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); - changeHolderToken(owner.privateKey, { - ...defaultTransferBeneficiary, + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); + changeHolderE2EToken(owner.privateKey, { + ...defaultE2ETransferBeneficiary, tokenRegistry: tokenRegistry, tokenId: tokenId, newHolder: receiver.ethAddress, }); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, }; const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse transfer from nominee title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, }; const command = generateEndorseTransferOwnerCommand(transferHolder, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to endorse surrendered title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); - surrenderToken(owner.privateKey, { - ...defaultTransferBeneficiary, + const { tokenRegistry, tokenId } = mintNominatedE2EToken(owner.privateKey, tokenRegistryAddress); + surrenderE2EToken(owner.privateKey, { + ...defaultE2ETransferBeneficiary, tokenRegistry, tokenId, }); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, @@ -95,19 +107,19 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been surrendered"); + checkE2EFailure(results, "Title Escrow has already been surrendered"); } // "should not be able to endorse burnt title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, tokenRegistryAddress); - burnToken(owner.privateKey, { - ...defaultTransferBeneficiary, + const { tokenRegistry, tokenId } = mintNominatedE2EToken(owner.privateKey, tokenRegistryAddress); + burnE2EToken(owner.privateKey, { + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, }); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, @@ -115,14 +127,14 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Title Escrow has already been shredded"); + checkE2EFailure(results, "Title Escrow has already been shredded"); } // "should not be able to endorse un-nominated title-escrow on token-registry"; { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, @@ -130,26 +142,30 @@ export const endorseTransfer = async (): Promise => { const command = generateEndorseTransferOwnerCommand(transferHolder, owner.privateKey); const results = run(command); - checkFailure(results, "Destination wallet has not been nominated"); + checkE2EFailure(results, "Destination wallet has not been nominated"); } // "should not be able to endorse transfer from holder title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintNominatedToken(owner.privateKey, receiver.ethAddress, tokenRegistryAddress); - changeHolderToken(owner.privateKey, { - ...defaultTransferBeneficiary, + const { tokenRegistry, tokenId } = mintNominatedE2EToken( + owner.privateKey, + receiver.ethAddress, + tokenRegistryAddress + ); + changeHolderE2EToken(owner.privateKey, { + ...defaultE2ETransferBeneficiary, tokenRegistry: tokenRegistry, tokenId: tokenId, newHolder: thirdParty.ethAddress, }); const transferHolder: TitleEscrowNominateBeneficiaryCommand = { - ...defaultTransferBeneficiary, + ...defaultE2ETransferBeneficiary, tokenId, tokenRegistry, }; const command = generateEndorseTransferOwnerCommand(transferHolder, thirdParty.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } }; diff --git a/src/e2e/all.ts b/src/e2e/index.ts similarity index 100% rename from src/e2e/all.ts rename to src/e2e/index.ts diff --git a/src/e2e/mint.e2e.ts b/src/e2e/mint.e2e.ts index 90beed9a..96090801 100644 --- a/src/e2e/mint.e2e.ts +++ b/src/e2e/mint.e2e.ts @@ -5,10 +5,16 @@ import { TokenRegistryIssueCommand } from "../commands/token-registry/token-regi import { generateMintTitleEscrowCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; -import { deployTokenRegistry, validateMintData, MintData, checkFailure, checkMintSuccess } from "./utils/helpers"; +import { + deployE2ETokenRegistry, + validateE2EMintData, + E2EMintData, + checkE2EFailure, + checkE2EMintSuccess, +} from "./utils/helpers"; export const mint = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to mint title-escrow on token-registry" { @@ -22,8 +28,8 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - const mintResults = checkMintSuccess(results); - validateMintData(titleEscrowParameter as MintData, mintResults); + const mintResults = checkE2EMintSuccess(results); + validateE2EMintData(titleEscrowParameter as E2EMintData, mintResults); const signer = await getSigner(titleEscrowParameter.network, owner.privateKey); const titleEscrowInfo = await retrieveTitleEscrow( @@ -56,8 +62,8 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - const mintResults = checkMintSuccess(results); - validateMintData(titleEscrowParameter as MintData, mintResults); + const mintResults = checkE2EMintSuccess(results); + validateE2EMintData(titleEscrowParameter as E2EMintData, mintResults); const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); const titleEscrowInfo = await retrieveTitleEscrow( @@ -90,8 +96,8 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - const mintResults = checkMintSuccess(results); - validateMintData(titleEscrowParameter as MintData, mintResults); + const mintResults = checkE2EMintSuccess(results); + validateE2EMintData(titleEscrowParameter as E2EMintData, mintResults); const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); const titleEscrowInfo = await retrieveTitleEscrow( @@ -124,8 +130,8 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - const mintResults = checkMintSuccess(results); - validateMintData(titleEscrowParameter as MintData, mintResults); + const mintResults = checkE2EMintSuccess(results); + validateE2EMintData(titleEscrowParameter as E2EMintData, mintResults); const signer = await getSigner(titleEscrowParameter.network, receiver.privateKey); const titleEscrowInfo = await retrieveTitleEscrow( @@ -157,7 +163,7 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - checkFailure(results, "invalid BigNumber string"); + checkE2EFailure(results, "invalid BigNumber string"); } //"should fail with invalid token registry" @@ -172,7 +178,7 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should fail with invalid beneficiary" @@ -187,7 +193,7 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkE2EFailure(results, "missing revert data in call exception"); } //"should fail with invalid holder" @@ -202,6 +208,6 @@ export const mint = async (): Promise => { }; const command = generateMintTitleEscrowCommand(titleEscrowParameter, owner.privateKey); const results = run(command); - checkFailure(results, "missing revert data in call exception"); + checkE2EFailure(results, "missing revert data in call exception"); } }; diff --git a/src/e2e/nominate.e2e.ts b/src/e2e/nominate.e2e.ts index 570d44e2..58f2118b 100644 --- a/src/e2e/nominate.e2e.ts +++ b/src/e2e/nominate.e2e.ts @@ -4,20 +4,20 @@ import { TitleEscrowNominateBeneficiaryCommand } from "../commands/title-escrow/ import { generateNominateCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { - changeHolderToken, - checkFailure, - checkNominateSuccess, - defaultNominateBeneficiary, - deployTokenRegistry, - mintTokenRegistry, + changeHolderE2EToken, + checkE2EFailure, + checkE2ENominateSuccess, + defaultE2ENominateBeneficiary, + deployE2ETokenRegistry, + mintE2ETokenRegistry, } from "./utils/helpers"; export const nominate = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to nominate title-escrow on token-registry" { - const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId, tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { tokenId, tokenRegistry, @@ -26,7 +26,7 @@ export const nominate = async (): Promise => { }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - const nominateInfo = checkNominateSuccess(results); + const nominateInfo = checkE2ENominateSuccess(results); if (!(nominateInfo.tokenId === nominateParameter.tokenId)) throw new Error(`nominateInfo.tokenId === nominateParameter.tokenId`); if (!(nominateInfo.nominee === nominateParameter.newBeneficiary)) @@ -43,22 +43,22 @@ export const nominate = async (): Promise => { //"should be able to cancel nomination of title-escrow on token-registry" { - const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId, tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const transferReceiverNominee: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId, tokenRegistry, }; const receiverNominateCommand = generateNominateCommand(transferReceiverNominee, owner.privateKey); const receiverNominateResults = run(receiverNominateCommand); - checkNominateSuccess(receiverNominateResults); + checkE2ENominateSuccess(receiverNominateResults); const transferBurnNominee = { ...transferReceiverNominee, newBeneficiary: BurnAddress, }; const command = generateNominateCommand(transferBurnNominee, owner.privateKey); const results = run(command); - const nominateInfo = checkNominateSuccess(results); + const nominateInfo = checkE2ENominateSuccess(results); if (!(nominateInfo.tokenId === transferBurnNominee.tokenId)) throw new Error(`nominateInfo.tokenId === transferBurnNominee.tokenId`); if (!(nominateInfo.nominee === transferBurnNominee.newBeneficiary)) @@ -75,75 +75,75 @@ export const nominate = async (): Promise => { //"should not be able to nominate self" { - const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId, tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId, tokenRegistry, newBeneficiary: owner.ethAddress, }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, "Destination wallet already has the rights as beneficiary"); + checkE2EFailure(results, "Destination wallet already has the rights as beneficiary"); } //"should not be able to nominate unowned token" { - const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId, tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId, tokenRegistry, newBeneficiary: receiver.ethAddress, }; const command = generateNominateCommand(nominateParameter, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to nominate token as holder" { - const { tokenId, tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); - changeHolderToken(owner.privateKey, { - ...defaultNominateBeneficiary, + const { tokenId, tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); + changeHolderE2EToken(owner.privateKey, { + ...defaultE2ENominateBeneficiary, newHolder: receiver.ethAddress, tokenId, tokenRegistry, }); const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId, tokenRegistry, newBeneficiary: thirdParty.ethAddress, }; const command = generateNominateCommand(nominateParameter, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to nominate non-existent token" { const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", tokenRegistry: tokenRegistryAddress, newBeneficiary: receiver.ethAddress, }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, "Unminted Token"); + checkE2EFailure(results, "Unminted Token"); } // "should not be able to nominate non-existent token registry" { const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId: "0x0000000000000000000000000000000000000000000000000000000000000000", tokenRegistry: "0x0000000000000000000000000000000000000000", newBeneficiary: receiver.ethAddress, }; const command = generateNominateCommand(nominateParameter, owner.privateKey); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } }; diff --git a/src/e2e/reject-surrender.e2e.ts b/src/e2e/reject-surrender.e2e.ts index 5fca3e35..1b65e25a 100644 --- a/src/e2e/reject-surrender.e2e.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -2,13 +2,13 @@ import { run } from "./utils/shell"; import { BurnAddress, defaultRunParameters, EmptyTokenID, owner, receiver } from "./utils/constants"; import { generateRejectSurrenderCommand } from "./utils/commands"; -import { checkFailure, deployTokenRegistry, mintSurrenderToken, mintTokenRegistry } from "./utils/helpers"; -import { checkSurrenderRejectSuccess } from "./utils/helpers"; +import { checkE2EFailure, deployE2ETokenRegistry, mintSurrenderE2EToken, mintE2ETokenRegistry } from "./utils/helpers"; +import { checkE2ESurrenderRejectSuccess } from "./utils/helpers"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; export const rejectSurrender = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); const defaultTitleEscrow = { ...defaultRunParameters, @@ -18,14 +18,14 @@ export const rejectSurrender = async (): Promise => { //"should be able to reject surrender title-escrow on token-registry" { - const { tokenRegistry, tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const signer = await getSigner(defaultTitleEscrow.network, owner.privateKey); const command = generateRejectSurrenderCommand({ tokenRegistry, tokenId, ...defaultTitleEscrow }, owner.privateKey); let titleEscrowOwner: string = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); if (!(titleEscrowOwner === tokenRegistry)) throw new Error(`titleEscrowOwner === tokenRegistry`); const results = run(command); - checkSurrenderRejectSuccess(results); + checkE2ESurrenderRejectSuccess(results); titleEscrowOwner = await retrieveTitleEscrowOwner(signer, tokenRegistry, tokenId); if (!(isAddress(titleEscrowOwner) === true)) throw new Error(`isAddress(titleEscrowOwner) === true`); if (!(titleEscrowOwner !== tokenRegistry)) throw new Error(`titleEscrowOwner !== tokenRegistry`); @@ -33,45 +33,45 @@ export const rejectSurrender = async (): Promise => { //"should not be able to reject surrender invalid title-escrow on token-registry" { - const { tokenRegistry } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand( { tokenRegistry, tokenId: EmptyTokenID, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, "Unminted Token"); + checkE2EFailure(results, "Unminted Token"); } //"should not be able to reject surrender title-escrow on invalid token-registry" { - const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand( { tokenRegistry: BurnAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } //"should not be able to accept un-owned/held surrendered title-escrow on invalid token-registry" { - const { tokenId } = mintSurrenderToken(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintSurrenderE2EToken(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand( { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, receiver.privateKey ); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } //"should not be able to accept un-surrendered title-escrow on token-registry" { - const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const command = generateRejectSurrenderCommand( { tokenRegistry: tokenRegistryAddress, tokenId, ...defaultTitleEscrow }, owner.privateKey ); const results = run(command); - checkFailure(results, "Title Escrow has not been surrendered"); + checkE2EFailure(results, "Title Escrow has not been surrendered"); } }; diff --git a/src/e2e/surrender.e2e.ts b/src/e2e/surrender.e2e.ts index 921593db..b7e233a8 100644 --- a/src/e2e/surrender.e2e.ts +++ b/src/e2e/surrender.e2e.ts @@ -5,12 +5,12 @@ import { generateSurrenderCommand } from "./utils/commands"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; import { - deployTokenRegistry, - mintTokenRegistry, - checkSurrenderSuccess, - checkFailure, - changeHolderToken, - nominateAndEndorseBeneficiary, + deployE2ETokenRegistry, + mintE2ETokenRegistry, + checkE2ESurrenderSuccess, + checkE2EFailure, + changeHolderE2EToken, + nominateAndEndorseE2EBeneficiary, } from "./utils/helpers"; const defaultTitleEscrow = { @@ -20,11 +20,11 @@ const defaultTitleEscrow = { }; export const surrender = async (): Promise => { - const tokenRegistryAddress = deployTokenRegistry(owner.privateKey); + const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); // "should be able to surrender title-escrow" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -45,7 +45,7 @@ export const surrender = async (): Promise => { } const command = generateSurrenderCommand(surrenderTitleEscrow, owner.privateKey); const results = run(command); - const surrenderResults = checkSurrenderSuccess(results); + const surrenderResults = checkE2ESurrenderSuccess(results); if (surrenderResults.tokenId !== surrenderTitleEscrow.tokenId) { throw new Error(`(surrenderResults.tokenId !== surrenderTitleEscrow.tokenId);`); } @@ -61,7 +61,7 @@ export const surrender = async (): Promise => { // "Should not be able to surrender unowned title-escrow" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -70,19 +70,19 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender title-escrow as beneficiary" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, tokenRegistry, tokenId, }; - nominateAndEndorseBeneficiary(owner.privateKey, { + nominateAndEndorseE2EBeneficiary(owner.privateKey, { ...defaultTitleEscrow, tokenId, tokenRegistry, @@ -90,19 +90,19 @@ export const surrender = async (): Promise => { }); const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender title-escrow as holder" { - const { tokenRegistry, tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry, tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, tokenRegistry, tokenId, }; - changeHolderToken(owner.privateKey, { + changeHolderE2EToken(owner.privateKey, { ...defaultTitleEscrow, tokenId, tokenRegistry, @@ -110,12 +110,12 @@ export const surrender = async (): Promise => { }); const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "Wallet lack the rights for the transfer operation"); + checkE2EFailure(results, "Wallet lack the rights for the transfer operation"); } // "Should not be able to surrender invalid title-escrow" { - const { tokenRegistry } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenRegistry } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -124,12 +124,12 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, "Unminted Token"); + checkE2EFailure(results, "Unminted Token"); } // "Should not be able to surrender invalid token-registry" { - const { tokenId } = mintTokenRegistry(owner.privateKey, tokenRegistryAddress); + const { tokenId } = mintE2ETokenRegistry(owner.privateKey, tokenRegistryAddress); const surrenderTitleEscrow: BaseTitleEscrowCommand = { ...defaultTitleEscrow, @@ -138,6 +138,6 @@ export const surrender = async (): Promise => { }; const command = generateSurrenderCommand(surrenderTitleEscrow, receiver.privateKey); const results = run(command); - checkFailure(results, `Address ${BurnAddress} is not a valid Contract`); + checkE2EFailure(results, `Address ${BurnAddress} is not a valid Contract`); } }; diff --git a/src/e2e/utils/constants.ts b/src/e2e/utils/constants.ts index 047e866a..65bac013 100644 --- a/src/e2e/utils/constants.ts +++ b/src/e2e/utils/constants.ts @@ -1,4 +1,3 @@ -// import { NonceManager } from "@ethersproject/experimental"; import { constants } from "@govtechsg/token-registry"; export const { contractAddress } = constants; diff --git a/src/e2e/utils/helpers/accept-surrender.ts b/src/e2e/utils/helpers/accept-surrender.ts index 8c43751e..5aa87e93 100644 --- a/src/e2e/utils/helpers/accept-surrender.ts +++ b/src/e2e/utils/helpers/accept-surrender.ts @@ -3,29 +3,29 @@ import { generateAcceptSurrenderCommand } from "../commands"; import { defaultRunParameters, EndStatus, TokenIdLength } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -import { mintSurrenderToken, surrenderToken } from "./surrender"; +import { mintSurrenderE2EToken, surrenderE2EToken } from "./surrender"; -export const mintBurntToken = ( +export const mintBurntE2EToken = ( privateKey: string, tokenRegistryAddress?: string ): { tokenId: string; tokenRegistry: string } => { - const { tokenId, tokenRegistry } = mintSurrenderToken(privateKey, tokenRegistryAddress); - burnSurrenderedToken(privateKey, { tokenRegistry, tokenId, ...defaultRunParameters }); + const { tokenId, tokenRegistry } = mintSurrenderE2EToken(privateKey, tokenRegistryAddress); + burnSurrenderedE2EToken(privateKey, { tokenRegistry, tokenId, ...defaultRunParameters }); return { tokenId, tokenRegistry }; }; -export const burnSurrenderedToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { +export const burnSurrenderedE2EToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { const command = generateAcceptSurrenderCommand(acceptSurrender, privateKey); const results = run(command); - checkSurrenderAcceptSuccess(results); + checkE2ESurrenderAcceptSuccess(results); }; -export const burnToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { - surrenderToken(privateKey, acceptSurrender); - burnSurrenderedToken(privateKey, acceptSurrender); +export const burnE2EToken = (privateKey: string, acceptSurrender: BaseTitleEscrowCommand): void => { + surrenderE2EToken(privateKey, acceptSurrender); + burnSurrenderedE2EToken(privateKey, acceptSurrender); }; -export const checkSurrenderAcceptSuccess = (results: string): { tokenId: string } => { +export const checkE2ESurrenderAcceptSuccess = (results: string): { tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/helpers/change-holder.ts b/src/e2e/utils/helpers/change-holder.ts index 72a1749b..e6ad4a26 100644 --- a/src/e2e/utils/helpers/change-holder.ts +++ b/src/e2e/utils/helpers/change-holder.ts @@ -5,12 +5,12 @@ import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -export const defaultTransferHolder = { +export const defaultE2ETransferHolder = { ...defaultRunParameters, newHolder: receiver.ethAddress, }; -export const checkChangeHolderSuccess = (results: string): { newHolder: string; tokenId: string } => { +export const checkE2EChangeHolderSuccess = (results: string): { newHolder: string; tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); if (statusLine.length <= 0) throw new Error("Change Holder failed"); const titleEscrowAddressLine = statusLine[0].lineContent; @@ -29,8 +29,8 @@ export const checkChangeHolderSuccess = (results: string): { newHolder: string; }; }; -export const changeHolderToken = (privateKey: string, transferHolder: TitleEscrowTransferHolderCommand): void => { +export const changeHolderE2EToken = (privateKey: string, transferHolder: TitleEscrowTransferHolderCommand): void => { const command = generateChangeHolderCommand(transferHolder, privateKey); const results = run(command); - checkChangeHolderSuccess(results); + checkE2EChangeHolderSuccess(results); }; diff --git a/src/e2e/utils/helpers/common.ts b/src/e2e/utils/helpers/common.ts index a727d91f..da389f9b 100644 --- a/src/e2e/utils/helpers/common.ts +++ b/src/e2e/utils/helpers/common.ts @@ -1,7 +1,7 @@ import { EndStatus } from "../constants"; import { extractStatus } from "../shell"; -export const checkFailure = (results: string, expectedErrorMessage: string): void => { +export const checkE2EFailure = (results: string, expectedErrorMessage: string): void => { const statusMessage = extractStatus(results, EndStatus.error); if (!(statusMessage.length > 0)) throw new Error(`!statusMessage.length > 0`); const errorMessage = statusMessage[0].lineContent.substring(13); diff --git a/src/e2e/utils/helpers/deploy.ts b/src/e2e/utils/helpers/deploy.ts index 51da7d9d..9d087aca 100644 --- a/src/e2e/utils/helpers/deploy.ts +++ b/src/e2e/utils/helpers/deploy.ts @@ -4,7 +4,7 @@ import { generateDeployTokenRegistryCommand } from "../commands"; import { defaultRunParameters, creators, EndStatus, AddressLength } from "../constants"; import { extractStatus, run } from "../shell"; -export const defaultTokenRegistry = { +export const defaultE2ETokenRegistryParams = { ...defaultRunParameters, factoryAddress: creators.titleEscrowFactory, tokenImplementationAddress: creators.tokenImplementation, @@ -15,7 +15,7 @@ const numberGenerator = (range: number): number => { return Math.floor(Math.random() * range); }; -export const checkTokenRegistrySuccess = (results: string): string => { +export const checkE2ETokenRegistrySuccess = (results: string): string => { const statusLine = extractStatus(results, EndStatus.success, "Token registry deployed at "); if (statusLine.length <= 0) throw new Error("Deployment failed"); const tokenRegistryDeploymentLine = statusLine[0].lineContent; @@ -26,20 +26,20 @@ export const checkTokenRegistrySuccess = (results: string): string => { return tokenRegistry; }; -export const deployTokenRegistry = ( +export const deployE2ETokenRegistry = ( privateKey: string, tokenRegistryParameters?: DeployTokenRegistryCommand ): string => { if (!tokenRegistryParameters) { const index = numberGenerator(100); tokenRegistryParameters = { - ...defaultTokenRegistry, + ...defaultE2ETokenRegistryParams, registryName: `Test Token ${index}`, registrySymbol: `TKN${index}`, }; } const command = generateDeployTokenRegistryCommand(tokenRegistryParameters, privateKey); const results = run(command); - const tokenRegistryAddress = checkTokenRegistrySuccess(results); + const tokenRegistryAddress = checkE2ETokenRegistrySuccess(results); return tokenRegistryAddress; }; diff --git a/src/e2e/utils/helpers/endorse-beneficiary.ts b/src/e2e/utils/helpers/endorse-beneficiary.ts index 342f13dd..fbbab2fa 100644 --- a/src/e2e/utils/helpers/endorse-beneficiary.ts +++ b/src/e2e/utils/helpers/endorse-beneficiary.ts @@ -4,31 +4,31 @@ import { generateEndorseTransferOwnerCommand } from "../commands"; import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -import { nominateToken } from "./nominate"; +import { nominateE2EToken } from "./nominate"; -export const defaultTransferBeneficiary = { +export const defaultE2ETransferBeneficiary = { ...defaultRunParameters, newBeneficiary: receiver.ethAddress, }; -export const nominateAndEndorseBeneficiary = ( +export const nominateAndEndorseE2EBeneficiary = ( privateKey: string, transferBeneficiary: TitleEscrowNominateBeneficiaryCommand ): void => { - nominateToken(privateKey, transferBeneficiary); - endorseBeneficiary(privateKey, transferBeneficiary); + nominateE2EToken(privateKey, transferBeneficiary); + endorseE2EBeneficiary(privateKey, transferBeneficiary); }; -export const endorseBeneficiary = ( +export const endorseE2EBeneficiary = ( privateKey: string, transferBeneficiary: TitleEscrowNominateBeneficiaryCommand ): void => { const command = generateEndorseTransferOwnerCommand(transferBeneficiary, privateKey); const results = run(command); - checkEndorseBeneficiary(results); + checkE2EEndorseBeneficiary(results); }; -export const checkEndorseBeneficiary = (results: string): { beneficiary: string; tokenId: string } => { +export const checkE2EEndorseBeneficiary = (results: string): { beneficiary: string; tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); if (statusLine.length <= 0) throw new Error("Minting failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/helpers/endorse-owners.ts b/src/e2e/utils/helpers/endorse-owners.ts index fb52e758..7a4d923a 100644 --- a/src/e2e/utils/helpers/endorse-owners.ts +++ b/src/e2e/utils/helpers/endorse-owners.ts @@ -3,7 +3,7 @@ import { AddressLength, EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; -export const checkEndorseOwner = (results: string): { beneficiary: string; holder: string; tokenId: string } => { +export const checkE2EEndorseOwner = (results: string): { beneficiary: string; holder: string; tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); if (statusLine.length <= 0) throw new Error("Minting failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/helpers/mint.ts b/src/e2e/utils/helpers/mint.ts index 992571cd..5a917f79 100644 --- a/src/e2e/utils/helpers/mint.ts +++ b/src/e2e/utils/helpers/mint.ts @@ -1,20 +1,20 @@ import { Wallet } from "ethers"; import { isAddress } from "web3-utils"; -import { deployTokenRegistry } from "."; +import { deployE2ETokenRegistry } from "."; import { TokenRegistryIssueCommand } from "../../../commands/token-registry/token-registry-command.type"; import { generateMintTitleEscrowCommand } from "../commands"; import { AddressLength, defaultRunParameters, EndStatus, TokenIdLength, TokenInfo } from "../constants"; import { extractStatus, run } from "../shell"; import { generateTokenId, isTokenId } from "../token-management"; -export interface MintData { +export interface E2EMintData { tokenId: string; address: string; beneficiary: string; holder: string; } -export const validateMintData = (expectedValue: MintData, value: MintData): void => { +export const validateE2EMintData = (expectedValue: E2EMintData, value: E2EMintData): void => { if (!(expectedValue.address === value.address)) throw new Error(`expectedValue.address === value.address`); if (!(expectedValue.beneficiary === value.beneficiary)) throw new Error(`expectedValue.beneficiary === value.beneficiary`); @@ -22,9 +22,9 @@ export const validateMintData = (expectedValue: MintData, value: MintData): void if (!(expectedValue.tokenId === value.tokenId)) throw new Error(`expectedValue.tokenId === value.tokenId`); }; -export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { +export const mintE2ETokenRegistry = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { if (!tokenRegistryAddress) { - tokenRegistryAddress = deployTokenRegistry(privateKey); + tokenRegistryAddress = deployE2ETokenRegistry(privateKey); } if (!isAddress(tokenRegistryAddress)) throw new Error("Invalid Token Registry Address"); const wallet = new Wallet(privateKey); @@ -36,10 +36,10 @@ export const mintTokenRegistry = (privateKey: string, tokenRegistryAddress?: str tokenId: generateTokenId(), }; - return mintToken(privateKey, titleEscrowParameter); + return mintE2EToken(privateKey, titleEscrowParameter); }; -export const checkMintSuccess = (results: string): MintData => { +export const checkE2EMintSuccess = (results: string): E2EMintData => { const statusLine = extractStatus(results, EndStatus.success, "Token with hash "); if (statusLine.length <= 0) throw new Error("Minting failed"); const titleEscrowAddressLine = statusLine[0].lineContent; @@ -66,10 +66,10 @@ export const checkMintSuccess = (results: string): MintData => { }; }; -export const mintToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { +export const mintE2EToken = (privateKey: string, titleEscrowParameter: TokenRegistryIssueCommand): TokenInfo => { const command = generateMintTitleEscrowCommand(titleEscrowParameter, privateKey); const results = run(command); - const { address: tokenRegistry, tokenId } = checkMintSuccess(results); + const { address: tokenRegistry, tokenId } = checkE2EMintSuccess(results); return { tokenRegistry, tokenId, diff --git a/src/e2e/utils/helpers/nominate.ts b/src/e2e/utils/helpers/nominate.ts index 71477af1..612d6dd2 100644 --- a/src/e2e/utils/helpers/nominate.ts +++ b/src/e2e/utils/helpers/nominate.ts @@ -1,36 +1,43 @@ import { isAddress } from "web3-utils"; -import { mintTokenRegistry } from "."; +import { mintE2ETokenRegistry } from "."; import { TitleEscrowNominateBeneficiaryCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { generateNominateCommand } from "../commands"; import { AddressLength, defaultRunParameters, EndStatus, receiver, TokenIdLength, TokenInfo } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -export const defaultNominateBeneficiary = { +export const defaultE2ENominateBeneficiary = { ...defaultRunParameters, newBeneficiary: receiver.ethAddress, }; -export const mintNominatedToken = (privateKey: string, nominee: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); +export const mintNominatedE2EToken = ( + privateKey: string, + nominee: string, + tokenRegistryAddress?: string +): TokenInfo => { + const tokenDetails = mintE2ETokenRegistry(privateKey, tokenRegistryAddress); const { tokenRegistry, tokenId } = tokenDetails; const nominateParameter: TitleEscrowNominateBeneficiaryCommand = { - ...defaultNominateBeneficiary, + ...defaultE2ENominateBeneficiary, tokenId: tokenId, tokenRegistry: tokenRegistry, newBeneficiary: nominee, }; - nominateToken(privateKey, nominateParameter); + nominateE2EToken(privateKey, nominateParameter); return tokenDetails; }; -export const nominateToken = (privateKey: string, nominateParameter: TitleEscrowNominateBeneficiaryCommand): void => { +export const nominateE2EToken = ( + privateKey: string, + nominateParameter: TitleEscrowNominateBeneficiaryCommand +): void => { const command = generateNominateCommand(nominateParameter, privateKey); const results = run(command); - checkNominateSuccess(results); + checkE2ENominateSuccess(results); }; -export const checkNominateSuccess = (results: string): { nominee: string; tokenId: string } => { +export const checkE2ENominateSuccess = (results: string): { nominee: string; tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); if (statusLine.length <= 0) throw new Error("Nomination failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/helpers/reject-surrender.ts b/src/e2e/utils/helpers/reject-surrender.ts index 97a5c4fb..43e04b4c 100644 --- a/src/e2e/utils/helpers/reject-surrender.ts +++ b/src/e2e/utils/helpers/reject-surrender.ts @@ -2,7 +2,7 @@ import { EndStatus, TokenIdLength } from "../constants"; import { extractStatus } from "../shell"; import { isTokenId } from "../token-management"; -export const checkSurrenderRejectSuccess = (results: string): { tokenId: string } => { +export const checkE2ESurrenderRejectSuccess = (results: string): { tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Surrendered transferable record with hash "); if (statusLine.length <= 0) throw new Error("Surrender Reject failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/helpers/surrender.ts b/src/e2e/utils/helpers/surrender.ts index 78f16873..3c9826b6 100644 --- a/src/e2e/utils/helpers/surrender.ts +++ b/src/e2e/utils/helpers/surrender.ts @@ -1,35 +1,35 @@ -import { mintTokenRegistry } from "."; +import { mintE2ETokenRegistry } from "."; import { BaseTitleEscrowCommand } from "../../../commands/title-escrow/title-escrow-command.type"; import { generateSurrenderCommand } from "../commands"; import { defaultRunParameters, EndStatus, owner, TokenIdLength, TokenInfo } from "../constants"; import { extractStatus, run } from "../shell"; import { isTokenId } from "../token-management"; -const defaultTitleEscrow = { +const defaultE2ETitleEscrow = { ...defaultRunParameters, beneficiary: owner.ethAddress, holder: owner.ethAddress, }; -export const mintSurrenderToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { - const tokenDetails = mintTokenRegistry(privateKey, tokenRegistryAddress); +export const mintSurrenderE2EToken = (privateKey: string, tokenRegistryAddress?: string): TokenInfo => { + const tokenDetails = mintE2ETokenRegistry(privateKey, tokenRegistryAddress); const { tokenRegistry, tokenId } = tokenDetails; const surrenderParameter: BaseTitleEscrowCommand = { - ...defaultTitleEscrow, + ...defaultE2ETitleEscrow, tokenRegistry: tokenRegistry, tokenId: tokenId, }; - surrenderToken(privateKey, surrenderParameter); + surrenderE2EToken(privateKey, surrenderParameter); return tokenDetails; }; -export const surrenderToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { +export const surrenderE2EToken = (privateKey: string, surrenderParameter: BaseTitleEscrowCommand): void => { const command = generateSurrenderCommand(surrenderParameter, privateKey); const results = run(command); - checkSurrenderSuccess(results); + checkE2ESurrenderSuccess(results); }; -export const checkSurrenderSuccess = (results: string): { tokenId: string } => { +export const checkE2ESurrenderSuccess = (results: string): { tokenId: string } => { const statusLine = extractStatus(results, EndStatus.success, "Transferable record with hash "); if (statusLine.length <= 0) throw new Error("Nomination failed"); const titleEscrowAddressLine = statusLine[0].lineContent; diff --git a/src/e2e/utils/shell.ts b/src/e2e/utils/shell.ts index 10aaa9c5..0c7fb23f 100644 --- a/src/e2e/utils/shell.ts +++ b/src/e2e/utils/shell.ts @@ -30,7 +30,7 @@ export const extractStatus = (results: string, expectedStatus: EndStatusType, ex return lineInfo; }; -export const printLines = (lines: LineInfo[]): void => { +const printLines = (lines: LineInfo[]): void => { lines.sort((a: LineInfo, b: LineInfo): number => { return a.lineNumber - b.lineNumber; }); @@ -39,7 +39,7 @@ export const printLines = (lines: LineInfo[]): void => { } }; -export const extractLine = (result: string, query: string): LineInfo[] | void => { +const extractLine = (result: string, query: string): LineInfo[] | void => { const splitResults = result.trim().split("\n"); const matchedLines = []; for (let count = 0; count < splitResults.length; count++) { @@ -57,21 +57,22 @@ export const extractLine = (result: string, query: string): LineInfo[] | void => }; // https://github.com/chalk/strip-ansi/blob/main/index.js -export function stripAnsi(ansiString: string): string { +const stripAnsi = (ansiString: string): string => { if (typeof ansiString !== "string") { - throw new TypeError(`Expected a \`string\`, got \`${typeof ansiString}\``); + const errorMessage = `Expected a \`string\`, got \`${typeof ansiString}\``; + throw new TypeError(errorMessage); } return ansiString.replace(ansiRegex(), ""); -} +}; // https://github.com/chalk/ansi-regex/blob/main/index.js -export function ansiRegex({ onlyFirst = false } = {}): RegExp { +const ansiRegex = ({ onlyFirst = false } = {}): RegExp => { const pattern = [ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", ].join("|"); return new RegExp(pattern, onlyFirst ? undefined : "g"); -} +}; -export { shell, ShellString }; +// export { shell, ShellString }; From 49137d73a0c0a7e9585515223dc34c7bbc40c73f Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 1 Feb 2023 13:03:33 +0800 Subject: [PATCH 18/20] fix: rename e2e functions --- src/e2e/accept-surrender.e2e.ts | 2 +- src/e2e/change-holder.e2e.ts | 2 +- src/e2e/deploy.e2e.ts | 4 +-- src/e2e/endorse-change-owner.e2e.ts | 2 +- src/e2e/endorse-transfer.e2e.ts | 2 +- src/e2e/index.ts | 38 ++++++++++++++--------------- src/e2e/mint.e2e.ts | 2 +- src/e2e/nominate.e2e.ts | 2 +- src/e2e/reject-surrender.e2e.ts | 2 +- src/e2e/surrender.e2e.ts | 2 +- 10 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/e2e/accept-surrender.e2e.ts b/src/e2e/accept-surrender.e2e.ts index 3f1acea9..f71cfd15 100644 --- a/src/e2e/accept-surrender.e2e.ts +++ b/src/e2e/accept-surrender.e2e.ts @@ -10,7 +10,7 @@ import { BurnAddress, defaultRunParameters, EmptyTokenID, owner, receiver } from import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { run } from "./utils/shell"; -export const acceptSurrender = async (): Promise => { +export const acceptE2Esurrender = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); // const errors: Error[] = []; const defaultTitleEscrow = { diff --git a/src/e2e/change-holder.e2e.ts b/src/e2e/change-holder.e2e.ts index 442a9800..696f7fbf 100644 --- a/src/e2e/change-holder.e2e.ts +++ b/src/e2e/change-holder.e2e.ts @@ -16,7 +16,7 @@ import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; // "transfer holder title-escrow" -export const changeHolder = async (): Promise => { +export const changeE2EHolder = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //should be able to transfer holder title-escrow on token-registry" diff --git a/src/e2e/deploy.e2e.ts b/src/e2e/deploy.e2e.ts index 1579e22d..75c41e2a 100644 --- a/src/e2e/deploy.e2e.ts +++ b/src/e2e/deploy.e2e.ts @@ -6,7 +6,7 @@ import { generateDeployDocumentStoreCommand, generateDeployTokenRegistryCommand import { getSigner, retrieveTokenInfo, rolesCheck } from "./utils/contract-checks"; import { checkE2ETokenRegistrySuccess, defaultE2ETokenRegistryParams } from "./utils/helpers"; -export const deployTokenRegistry = async (): Promise => { +export const deployE2ETokenRegistry = async (): Promise => { //should be able to deploy token-registry" { const tokenRegistryParameter: DeployTokenRegistryCommand = { @@ -42,7 +42,7 @@ export const deployTokenRegistry = async (): Promise => { } }; -export const deployDocumentStore = async (): Promise => { +export const deployE2EDocumentStore = async (): Promise => { //should be able to deploy document-store { const documentStoreParameters: DeployDocumentStoreCommand = { diff --git a/src/e2e/endorse-change-owner.e2e.ts b/src/e2e/endorse-change-owner.e2e.ts index 9af962f2..77de2cc4 100644 --- a/src/e2e/endorse-change-owner.e2e.ts +++ b/src/e2e/endorse-change-owner.e2e.ts @@ -16,7 +16,7 @@ import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; // "endorse change owner title-escrow" -export const endorseChangeOwner = async (): Promise => { +export const endorseE2EChangeOwner = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); const defaultTransferOwners = { diff --git a/src/e2e/endorse-transfer.e2e.ts b/src/e2e/endorse-transfer.e2e.ts index df0389b3..b93228d2 100644 --- a/src/e2e/endorse-transfer.e2e.ts +++ b/src/e2e/endorse-transfer.e2e.ts @@ -16,7 +16,7 @@ import { run } from "./utils/shell"; import { getSigner, retrieveTitleEscrow } from "./utils/contract-checks"; import { BigNumber } from "ethers"; -export const endorseTransfer = async (): Promise => { +export const endorseE2ETransfer = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to endorse transfer title-escrow on token-registry" diff --git a/src/e2e/index.ts b/src/e2e/index.ts index a01b2945..6678446e 100644 --- a/src/e2e/index.ts +++ b/src/e2e/index.ts @@ -1,28 +1,28 @@ -import { acceptSurrender } from "./accept-surrender.e2e"; -import { surrender } from "./surrender.e2e"; -import { changeHolder } from "./change-holder.e2e"; -import { deployDocumentStore, deployTokenRegistry } from "./deploy.e2e"; -import { endorseTransfer } from "./endorse-transfer.e2e"; -import { mint } from "./mint.e2e"; -import { nominate } from "./nominate.e2e"; -import { rejectSurrender } from "./reject-surrender.e2e"; -import { endorseChangeOwner } from "./endorse-change-owner.e2e"; +import { acceptE2Esurrender } from "./accept-surrender.e2e"; +import { surrenderE2EToken } from "./surrender.e2e"; +import { changeE2EHolder } from "./change-holder.e2e"; +import { deployE2EDocumentStore, deployE2ETokenRegistry } from "./deploy.e2e"; +import { endorseE2ETransfer } from "./endorse-transfer.e2e"; +import { mintE2EToken } from "./mint.e2e"; +import { nominateE2E } from "./nominate.e2e"; +import { rejectE2ESurrender } from "./reject-surrender.e2e"; +import { endorseE2EChangeOwner as endorseE2EChangeOwner } from "./endorse-change-owner.e2e"; const awaitForDuration = async (runFunction: () => void): Promise => { await runFunction(); console.log(runFunction.name); }; -awaitForDuration(deployDocumentStore); -awaitForDuration(deployTokenRegistry); +awaitForDuration(deployE2EDocumentStore); +awaitForDuration(deployE2ETokenRegistry); -awaitForDuration(mint); +awaitForDuration(mintE2EToken); -awaitForDuration(surrender); -awaitForDuration(rejectSurrender); -awaitForDuration(acceptSurrender); +awaitForDuration(surrenderE2EToken); +awaitForDuration(rejectE2ESurrender); +awaitForDuration(acceptE2Esurrender); -awaitForDuration(nominate); -awaitForDuration(changeHolder); -awaitForDuration(endorseChangeOwner); -awaitForDuration(endorseTransfer); +awaitForDuration(nominateE2E); +awaitForDuration(changeE2EHolder); +awaitForDuration(endorseE2EChangeOwner); +awaitForDuration(endorseE2ETransfer); diff --git a/src/e2e/mint.e2e.ts b/src/e2e/mint.e2e.ts index 96090801..3a92d2f0 100644 --- a/src/e2e/mint.e2e.ts +++ b/src/e2e/mint.e2e.ts @@ -13,7 +13,7 @@ import { checkE2EMintSuccess, } from "./utils/helpers"; -export const mint = async (): Promise => { +export const mintE2EToken = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to mint title-escrow on token-registry" diff --git a/src/e2e/nominate.e2e.ts b/src/e2e/nominate.e2e.ts index 58f2118b..96fce166 100644 --- a/src/e2e/nominate.e2e.ts +++ b/src/e2e/nominate.e2e.ts @@ -12,7 +12,7 @@ import { mintE2ETokenRegistry, } from "./utils/helpers"; -export const nominate = async (): Promise => { +export const nominateE2E = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); //"should be able to nominate title-escrow on token-registry" diff --git a/src/e2e/reject-surrender.e2e.ts b/src/e2e/reject-surrender.e2e.ts index 1b65e25a..c18f980c 100644 --- a/src/e2e/reject-surrender.e2e.ts +++ b/src/e2e/reject-surrender.e2e.ts @@ -7,7 +7,7 @@ import { checkE2ESurrenderRejectSuccess } from "./utils/helpers"; import { getSigner, retrieveTitleEscrowOwner } from "./utils/contract-checks"; import { isAddress } from "web3-utils"; -export const rejectSurrender = async (): Promise => { +export const rejectE2ESurrender = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); const defaultTitleEscrow = { diff --git a/src/e2e/surrender.e2e.ts b/src/e2e/surrender.e2e.ts index b7e233a8..a0b6e9f7 100644 --- a/src/e2e/surrender.e2e.ts +++ b/src/e2e/surrender.e2e.ts @@ -19,7 +19,7 @@ const defaultTitleEscrow = { holder: owner.ethAddress, }; -export const surrender = async (): Promise => { +export const surrenderE2EToken = async (): Promise => { const tokenRegistryAddress = deployE2ETokenRegistry(owner.privateKey); // "should be able to surrender title-escrow" From b195ad7c7ce6e43542337ebcbf7e2095e79758c8 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:37:10 +0800 Subject: [PATCH 19/20] feat!: Token Registry V4 (#228) * feat(token-registry)!: token registry v4 --- .DS_Store | Bin 0 -> 8196 bytes README.md | 43 ++--- package-lock.json | 28 ++- package.json | 2 +- src/commands/deploy/deploy.types.ts | 13 +- src/commands/deploy/title-escrow-creator.ts | 32 ---- src/commands/deploy/title-escrow.ts | 62 ------- src/commands/deploy/token-registry.ts | 16 +- src/commands/shared.ts | 20 +-- src/commands/title-escrow/change-holder.ts | 15 +- .../title-escrow/endorse-change-of-owner.ts | 9 +- .../title-escrow/endorse-transfer-of-owner.ts | 16 +- .../title-escrow/nominate-change-of-owner.ts | 17 +- .../title-escrow/title-escrow-command.type.ts | 10 +- src/commands/token-registry/issue.ts | 15 +- .../token-registry-command.type.ts | 3 +- src/implementations/config/helpers.ts | 2 - .../document-store/document-store.test.ts | 6 - .../deploy/document-store/document-store.ts | 7 +- .../deploy/title-escrow-creator/index.ts | 1 - .../title-escrow-creator.test.ts | 86 --------- .../title-escrow-creator.ts | 35 ---- .../deploy/title-escrow/index.ts | 1 - .../deploy/title-escrow/title-escrow.test.ts | 106 ----------- .../deploy/title-escrow/title-escrow.ts | 66 ------- .../deploy/token-registry/helpers.test.ts | 44 +++++ .../deploy/token-registry/helpers.ts | 48 +++++ .../token-registry/token-registry.test.ts | 138 +++++++-------- .../deploy/token-registry/token-registry.ts | 67 +++++-- .../document-store/issue.test.ts | 164 +++++++++--------- src/implementations/document-store/issue.ts | 7 +- .../document-store/revoke.test.ts | 55 +++--- src/implementations/document-store/revoke.ts | 8 +- .../document-store/transfer-ownership.test.ts | 70 +++----- .../document-store/transfer-ownership.ts | 7 +- .../title-escrow/acceptSurrendered.test.ts | 67 ++----- .../title-escrow/acceptSurrendered.ts | 14 +- .../title-escrow/changeHolder.test.ts | 110 ------------ .../title-escrow/endorseChangeOfOwner.test.ts | 135 -------------- .../endorseNominatedBeneficiary.test.ts | 94 ++++++++++ .../endorseNominatedBeneficiary.ts | 46 +++++ .../endorseTransferOfOwner.test.ts | 150 ---------------- .../title-escrow/endorseTransferOfOwner.ts | 56 ------ src/implementations/title-escrow/helpers.ts | 34 ++-- .../title-escrow/nominateBeneficiary.test.ts | 95 ++++++++++ .../title-escrow/nominateBeneficiary.ts | 39 +++++ .../nominateChangeOfOwner.test.ts | 133 -------------- .../title-escrow/nominateChangeOfOwner.ts | 46 ----- .../title-escrow/rejectSurrendered.test.ts | 83 +++------ .../title-escrow/rejectSurrendered.ts | 45 +---- .../title-escrow/surrenderDocument.test.ts | 69 ++------ .../title-escrow/surrenderDocument.ts | 15 +- .../title-escrow/transferHolder.test.ts | 75 ++++++++ .../{changeHolder.ts => transferHolder.ts} | 23 ++- .../title-escrow/transferOwners.test.ts | 100 +++++++++++ ...orseChangeOfOwner.ts => transferOwners.ts} | 24 +-- .../token-registry/issue.test.ts | 95 +++++----- src/implementations/token-registry/issue.ts | 21 +-- src/implementations/utils/dryRun.ts | 10 +- 59 files changed, 1058 insertions(+), 1740 deletions(-) create mode 100644 .DS_Store delete mode 100644 src/commands/deploy/title-escrow-creator.ts delete mode 100644 src/commands/deploy/title-escrow.ts delete mode 100644 src/implementations/deploy/title-escrow-creator/index.ts delete mode 100644 src/implementations/deploy/title-escrow-creator/title-escrow-creator.test.ts delete mode 100644 src/implementations/deploy/title-escrow-creator/title-escrow-creator.ts delete mode 100644 src/implementations/deploy/title-escrow/index.ts delete mode 100644 src/implementations/deploy/title-escrow/title-escrow.test.ts delete mode 100644 src/implementations/deploy/title-escrow/title-escrow.ts create mode 100644 src/implementations/deploy/token-registry/helpers.test.ts create mode 100644 src/implementations/deploy/token-registry/helpers.ts delete mode 100644 src/implementations/title-escrow/changeHolder.test.ts delete mode 100644 src/implementations/title-escrow/endorseChangeOfOwner.test.ts create mode 100644 src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts create mode 100644 src/implementations/title-escrow/endorseNominatedBeneficiary.ts delete mode 100644 src/implementations/title-escrow/endorseTransferOfOwner.test.ts delete mode 100644 src/implementations/title-escrow/endorseTransferOfOwner.ts create mode 100644 src/implementations/title-escrow/nominateBeneficiary.test.ts create mode 100644 src/implementations/title-escrow/nominateBeneficiary.ts delete mode 100644 src/implementations/title-escrow/nominateChangeOfOwner.test.ts delete mode 100644 src/implementations/title-escrow/nominateChangeOfOwner.ts create mode 100644 src/implementations/title-escrow/transferHolder.test.ts rename src/implementations/title-escrow/{changeHolder.ts => transferHolder.ts} (54%) create mode 100644 src/implementations/title-escrow/transferOwners.test.ts rename src/implementations/title-escrow/{endorseChangeOfOwner.ts => transferOwners.ts} (56%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..c2cfc0681767194c22d71c3ecf99ffc708c7baaa GIT binary patch literal 8196 zcmeHMTWl3Y7@lui=AD=AZwc z`DbUo`Tm_Vdl+L#l?rzrTM!k`o?dC}T;ELXn zAwnQRAVMHQAVMHQ;6^}z&TNtRHO_t68kG?O5dt?Q0^*xhaL9`xdw#q0XA#Onodt# zknNQ_3<&@o07T=Ur!4(G=}{ zdDkutJ6=Yw&1da=$g|SblS-do97oStk;c2#^Q>X?0eal>kYiWl(UYMEdQ8%h7n(?a{ax1 z1)A4Rw;)C=1TBUUU&i@fVecrKR7$gEG_E9vY}%uxh0|QUMpbn$eVF!Fx@s-0T-6JE z>}<}oL(_&fGHvfGOMFA)J)1erzPUf|4iD3MJZUyk9h~du4v1&($a;SFi0O>aDz%mK zL2oEqG`mNMU=@$;oCkYGcHy$6VEJxQ0und6IUf*%wHFFBj=r#d!2`PugEq!;$`;X? zHe9k=|C)2&kX$MECjhk}nUI=FN0h-<(^1&P_A!%{s>&yU6}RHKw5k9JN@4Td){Q(1cd3!+NB!9XqfS-AH3U4j_Xp4#LFhCBDT^_!+<8BL2odxTL7cTxFSZhq6Lx zR8}f2$_AxfPF<-)%iv_%G}OrK7Smuh3IDW)qFXi{ArOK5r(g^?Qgw#1q=9b77RX>FQbO#vm< zP1-X80S=^{M-15w{*AF@x0sOO2Ozp>xhpX?Hmb2jRUn9H#OjcCRyBIgFQ zVIwwS3$~&Q+lZ=Z^r8;~7(^Z>5p@U_ittfF84u$TqU$L+z^)j0k}U zfg2P7RCo4tieCcPdfAG*cAWBqltmK##-t2fs1RL*J-CjO9RI_R>hZ8G8Okv!sfE)2 U{vlxE58deekIw&qg*RXEFN!-G2LJ#7 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 6c65c411..fd2be45e 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,6 @@ npx -p @govtechsg/open-attestation-cli open-attestation | -------------------------------------------------------------------------- | ----------- | ------ | ------- | | [Create config](#config-create-configuration-file) | ❎ | ✔️ | ❎ | | [Deploy document store](#deploy-new-document-store) | ✔ | ✔ | ✔ | -| [Deploy title escrow](#deploy-new-title-escrow) | ✔ | ✔ | ✔ | -| [Deploy title escrow creator](#title-escrow) | ✔ | ✔ | ✔ | | [Deploy token registry](#deploy-new-token-registry) | ✔ | ✔ | ✔ | | [Dns txt create](#dns-txt-record) | ❎ | ❎ | ❎ | | [Dns txt get](#dns-txt-record) | ❎ | ❎ | ❎ | @@ -244,14 +242,14 @@ open-attestation decrypt ./src/__tests__/fixture/did-dns-encrypted.json decrypte ✔ success Decrypted document saved to: decrypted.json ``` -### Token registry +### Token Registry -#### Deploy new token registry +#### Deploy new Token Registry -Deploys a token registry contract on the blockchain +Deploys a token registry contract on the blockchain. Factory Contract that have been deployed using token-registry can be used with the factory address flag. To deploy a standalone token registry, please refer to [Token-Registry](https://github.com/Open-Attestation/token-registry) deployment. ```bash -open-attestation deploy token-registry [options] +open-attestation deploy token-registry --factory-address [options] ``` Example - with private key set in `OA_PRIVATE_KEY` environment variable (recommended). [More options](#providing-the-wallet). @@ -262,28 +260,12 @@ open-attestation deploy token-registry "My Sample Token" MST --network goerli ✔ success Token registry deployed at 0x4B127b8d5e53872d403ce43414afeb1db67B1842 ``` -#### Deploy new title escrow - -Deploys a title escrow contract on the blockchain - -```bash -open-attestation deploy title-escrow --network --address --beneficiary --holder -``` - -Example - with private key set in `OA_PRIVATE_KEY` environment variable (recommended). [More options](#providing-the-wallet). - -```bash -open-attestation deploy title-escrow --network goerli --address 0x4B127b8d5e53872d403ce43414afeb1db67B1842 --beneficiary 0x6FFeD6E6591b808130a9b248fEA32101b5220eca --holder 0x6FFeD6E6591b808130a9b248fEA32101b5220eca - -✔ success Title escrow deployed at 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 -``` - #### Issue document to token registry `Issue` a hash to a token registry deployed on the blockchain. The `tokenId` option would be used to indicate the document hash, and the `to` option to indicate the title escrow address the document is mapped to. ```bash -open-attestation token-registry issue --network --address --tokenId --to [options] +open-attestation token-registry issue --network --address --tokenId --beneficiary --holder [options] ``` Example - with private key set in `OA_PRIVATE_KEY` environment variable (recommended). [More options](#providing-the-wallet). @@ -297,6 +279,11 @@ open-attestation token-registry mint --network goerli --address 0x6133f580aE903b `mint` can be used instead of issue and will be strictly equivalent. +#### Token Registry Roles + +Interfaces for the Assignment and Revocation of roles are available on the Token Registry repository. + + ### Document Store #### Deploy new document store @@ -595,13 +582,13 @@ open-attestation transaction cancel --transaction-hash 0x000 --network goerli -- This command will allow the owner of a transferable record to change its holder. ```bash -open-attestation title-escrow change-holder --token-registry --tokenId --to [options] +open-attestation title-escrow change-holder --token-registry --tokenId --newHolder [options] ``` Example - with private key set in `OA_PRIVATE_KEY` environment variable (recommended). [More options](#providing-the-wallet). ```bash -open-attestation title-escrow change-holder --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 --to 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 +open-attestation title-escrow change-holder --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 --newHolder 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 ✔ success Transferable record with hash 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990's holder has been successfully changed to holder with address: 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 ``` @@ -629,15 +616,15 @@ This command will allow the holder of the transferable record to endorse the tra **This command will fail if there is no approved owner or holder record on the transferable record.** ```bash -open-attestation title-escrow endorse-transfer-owner --token-registry --tokenId [options] +open-attestation title-escrow endorse-transfer-owner --token-registry --tokenId --newBeneficiary [options] ``` Example - with private key set in `OA_PRIVATE_KEY` environment variable (recommended). [More options](#providing-the-wallet). ```bash -open-attestation title-escrow endorse-transfer-owner --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 +open-attestation title-escrow endorse-transfer-owner --token-registry 0x4933e30eF8A083f49d14759b2eafC94E56F0b3A7 --tokenId 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990 --newBeneficiary 0x2f60375e8144e16Adf1979936301D8341D58C36C -✔ success Transferable record with hash 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990's holder has been successfully endorsed to approved owner at 0x2f60375e8144e16Adf1979936301D8341D58C36C and approved holder at 0xB26B4941941C51a4885E5B7D3A1B861E54405f90 +✔ success Transferable record with hash 0x951b39bcaddc0e8882883db48ca258ca35ccb01fee328355f0dfda1ff9be9990's holder has been successfully endorsed to approved beneficiary at 0x2f60375e8144e16Adf1979936301D8341D58C36C ``` #### Endorse Change of Owner diff --git a/package-lock.json b/package-lock.json index 386ab528..06d71839 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1963,6 +1963,11 @@ "runtypes": "^6.3.0" } }, + "@govtechsg/token-registry": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@govtechsg/token-registry/-/token-registry-2.6.1.tgz", + "integrity": "sha512-QnAIlYeGD4zHtYPiZ46SIe6hFc3HyMKvyBJIdlsFxHscMR4f8AENIyUS2AKhqJibCcN2beNJy/bPWlCAtHoT7w==" + }, "axios": { "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", @@ -1994,9 +1999,12 @@ } }, "@govtechsg/token-registry": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@govtechsg/token-registry/-/token-registry-2.5.3.tgz", - "integrity": "sha512-0VIeANA2XpaMNBLBAImwXf3SMV9wQcQeU4HxRgPzWaG4X/MpS2Msxv6YyTwk++p+j57s3PNoUQC5JiIAaY7xZw==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@govtechsg/token-registry/-/token-registry-4.1.2.tgz", + "integrity": "sha512-RD/J868KB36QIuQ+sjEO5hIEmLi+Mc7ft/H2WVVBR2bfRjNhH1Q//bw4yvZBuodLgbUWC5ybHOkLk2mvE3fwOg==", + "requires": { + "@typechain/ethers-v5": "~10.0.0" + } }, "@govtechsg/tradetrust-config": { "version": "1.2.3", @@ -3599,6 +3607,15 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@typechain/ethers-v5": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.0.0.tgz", + "integrity": "sha512-Kot7fwAqnH96ZbI8xrRgj5Kpv9yCEdjo7mxRqrH7bYpEgijT5MmuOo8IVsdhOu7Uog4ONg7k/d5UdbAtTKUgsA==", + "requires": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + } + }, "@types/babel__core": { "version": "7.1.15", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz", @@ -16078,6 +16095,11 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, + "ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==" + }, "ts-jest": { "version": "26.5.6", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz", diff --git a/package.json b/package.json index 14c54891..5f3aa94e 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@govtechsg/oa-encryption": "^1.3.3", "@govtechsg/oa-verify": "^7.11.0", "@govtechsg/open-attestation": "^6.4.1", - "@govtechsg/token-registry": "^2.5.3", + "@govtechsg/token-registry": "^4.1.2", "@govtechsg/tradetrust-config": "^1.2.3", "ajv": "^8.4.0", "ajv-formats": "^2.1.0", diff --git a/src/commands/deploy/deploy.types.ts b/src/commands/deploy/deploy.types.ts index 6f3ff6a0..f0b40017 100644 --- a/src/commands/deploy/deploy.types.ts +++ b/src/commands/deploy/deploy.types.ts @@ -14,14 +14,7 @@ export type DeployTokenRegistryCommand = NetworkAndWalletSignerOption & registryName: string; registrySymbol: string; passedOnWallet?: Wallet | ConnectedSigner; - }; - -export type DeployTitleEscrowCreatorCommand = NetworkAndWalletSignerOption & GasOption; - -export type DeployTitleEscrowCommand = NetworkAndWalletSignerOption & - GasOption & { - tokenRegistry: string; - beneficiary: string; - holder: string; - titleEscrowFactory?: string; + factoryAddress?: string; + tokenImplementationAddress?: string; + deployerAddress?: string; }; diff --git a/src/commands/deploy/title-escrow-creator.ts b/src/commands/deploy/title-escrow-creator.ts deleted file mode 100644 index a812a799..00000000 --- a/src/commands/deploy/title-escrow-creator.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Argv } from "yargs"; -import { error, info, success } from "signale"; -import { getLogger } from "../../logger"; -import { deployTitleEscrowCreator } from "../../implementations/deploy/title-escrow-creator"; -import { DeployTitleEscrowCreatorCommand } from "./deploy.types"; -import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; -import { getErrorMessage, getEtherscanAddress } from "../../utils"; - -const { trace } = getLogger("deploy:title-escrow-creator"); - -export const command = "title-escrow-creator [options]"; - -export const describe = "Deploys a (global) title escrow creator on the blockchain"; - -export const builder = (yargs: Argv): Argv => withGasPriceOption(withNetworkAndWalletSignerOption(yargs)); - -export const handler = async (args: DeployTitleEscrowCreatorCommand): Promise => { - trace(`Args: ${JSON.stringify(args, null, 2)}`); - try { - info(`Deploying title escrow creator`); - const titleEscrowCreator = await deployTitleEscrowCreator(args); - success(`Title escrow creator deployed at ${titleEscrowCreator.contractAddress}`); - info( - `Find more details at ${getEtherscanAddress({ network: args.network })}/address/${ - titleEscrowCreator.contractAddress - }` - ); - return titleEscrowCreator.contractAddress; - } catch (e) { - error(getErrorMessage(e)); - } -}; diff --git a/src/commands/deploy/title-escrow.ts b/src/commands/deploy/title-escrow.ts deleted file mode 100644 index f0cc37b9..00000000 --- a/src/commands/deploy/title-escrow.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Argv } from "yargs"; -import { error, info, success } from "signale"; -import { getLogger } from "../../logger"; -import { deployTitleEscrow } from "../../implementations/deploy/title-escrow"; -import { DeployTitleEscrowCommand } from "./deploy.types"; -import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; -import { getErrorMessage, getEtherscanAddress } from "../../utils"; - -const { trace } = getLogger("deploy:title-escrow"); - -export const command = "title-escrow [options]"; - -export const describe = "Deploys a title escrow on the blockchain"; - -export const builder = (yargs: Argv): Argv => - withGasPriceOption( - withNetworkAndWalletSignerOption( - yargs - .option("token-registry", { - alias: "r", - description: "Address of ERC721 contract that the escrow will receive the token from", - type: "string", - normalize: true, - required: true, - }) - .option("beneficiary", { - alias: "b", - description: "Beneficiary address", - type: "string", - normalize: true, - required: true, - }) - .option("holder", { - alias: "h", - description: "Holder address", - type: "string", - normalize: true, - required: true, - }) - .option("title-escrow-factory", { - alias: "c", - description: "Address of title escrow creator/factory", - type: "string", - normalize: true, - }) - ) - ); - -export const handler = async (args: DeployTitleEscrowCommand): Promise => { - trace(`Args: ${JSON.stringify(args, null, 2)}`); - try { - info(`Deploying title escrow`); - const titleEscrow = await deployTitleEscrow(args); - success(`Title escrow deployed at ${titleEscrow.contractAddress}`); - info( - `Find more details at ${getEtherscanAddress({ network: args.network })}/address/${titleEscrow.contractAddress}` - ); - return titleEscrow.contractAddress; - } catch (e) { - error(getErrorMessage(e)); - } -}; diff --git a/src/commands/deploy/token-registry.ts b/src/commands/deploy/token-registry.ts index 603a9650..3d5aecc6 100644 --- a/src/commands/deploy/token-registry.ts +++ b/src/commands/deploy/token-registry.ts @@ -12,6 +12,8 @@ export const command = "token-registry [option export const describe = "Deploys a token registry contract on the blockchain"; +// Refer to @govtechsg/token-registry tasks/deploy-token.ts + export const builder = (yargs: Argv): Argv => withGasPriceOption( withNetworkAndWalletSignerOption( @@ -24,6 +26,18 @@ export const builder = (yargs: Argv): Argv => description: "Symbol of the token (typically 3 characters)", normalize: true, }) + .option("factory-address", { + description: "Address of Token Registry factory (Optional)", + type: "string", + }) + .option("token-implementation-address", { + description: "Address of Token Implementation (Optional)", + type: "string", + }) + .option("deployer-address", { + description: "Address of Deployer (Optional)", + type: "string", + }) ) ); @@ -36,7 +50,7 @@ export const handler = async (args: DeployTokenRegistryCommand): Promise { export type WalletOrSignerOption = Partial | Partial | Partial; export interface GasOption { - gasPriceScale: number; dryRun: boolean; } @@ -58,19 +57,12 @@ export const withNetworkOption = (yargs: Argv): Argv => description: "Ethereum network to deploy to", }); export const withGasPriceOption = (yargs: Argv): Argv => - yargs - .option("gas-price-scale", { - alias: "gps", - type: "number", - default: 1, - description: "Gas price scale to apply to the estimated gas price", - }) - .option("dry-run", { - alias: "dr", - type: "boolean", - default: false, - description: "Dry run", - }); + yargs.option("dry-run", { + alias: "dr", + type: "boolean", + default: false, + description: "Dry run", + }); export const withPrivateKeyOption = (yargs: Argv): Argv => yargs diff --git a/src/commands/title-escrow/change-holder.ts b/src/commands/title-escrow/change-holder.ts index 89e8ae89..c446bba2 100644 --- a/src/commands/title-escrow/change-holder.ts +++ b/src/commands/title-escrow/change-holder.ts @@ -1,8 +1,8 @@ import { Argv } from "yargs"; import { error, info, success, warn } from "signale"; import { getLogger } from "../../logger"; -import { changeHolderOfTitleEscrow } from "../../implementations/title-escrow/changeHolder"; -import { TitleEscrowChangeHolderCommand } from "./title-escrow-command.type"; +import { transferHolder } from "../../implementations/title-escrow/transferHolder"; +import { TitleEscrowTransferHolderCommand } from "./title-escrow-command.type"; import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; import { getErrorMessage, getEtherscanAddress } from "../../utils"; @@ -27,7 +27,8 @@ export const builder = (yargs: Argv): Argv => type: "string", demandOption: true, }) - .option("to", { + .option("newHolder", { + alias: "to", description: "Address of the new holder of the transferable record", type: "string", demandOption: true, @@ -35,18 +36,18 @@ export const builder = (yargs: Argv): Argv => ) ); -export const handler = async (args: TitleEscrowChangeHolderCommand): Promise => { +export const handler = async (args: TitleEscrowTransferHolderCommand): Promise => { trace(`Args: ${JSON.stringify(args, null, 2)}`); try { info( - `Connecting to the registry ${args.tokenRegistry} and attempting to change the holder of the transferable record ${args.tokenId} to ${args.to}` + `Connecting to the registry ${args.tokenRegistry} and attempting to change the holder of the transferable record ${args.tokenId} to ${args.newHolder}` ); warn( `Please note that only current holders can change the holder of the transferable record, otherwise this command will fail.` ); - const { transactionHash } = await changeHolderOfTitleEscrow(args); + const { transactionHash } = await transferHolder(args); success( - `Transferable record with hash ${args.tokenId}'s holder has been successfully changed to holder with address: ${args.to}` + `Transferable record with hash ${args.tokenId}'s holder has been successfully changed to holder with address: ${args.newHolder}` ); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transactionHash}`); } catch (e) { diff --git a/src/commands/title-escrow/endorse-change-of-owner.ts b/src/commands/title-escrow/endorse-change-of-owner.ts index 78f31742..0c8af672 100644 --- a/src/commands/title-escrow/endorse-change-of-owner.ts +++ b/src/commands/title-escrow/endorse-change-of-owner.ts @@ -1,8 +1,8 @@ import { Argv } from "yargs"; import { error, info, success, warn } from "signale"; import { getLogger } from "../../logger"; -import { endorseChangeOfOwner } from "../../implementations/title-escrow/endorseChangeOfOwner"; -import { TitleEscrowEndorseChangeOfOwnerCommand } from "./title-escrow-command.type"; +import { transferOwners } from "../../implementations/title-escrow/transferOwners"; +import { TitleEscrowEndorseTransferOfOwnersCommand } from "./title-escrow-command.type"; import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; import { getErrorMessage, getEtherscanAddress } from "../../utils"; @@ -28,6 +28,7 @@ export const builder = (yargs: Argv): Argv => demandOption: true, }) .option("newOwner", { + alias: "newBeneficiary", description: "Address of the new owner of the transferable record", type: "string", demandOption: true, @@ -40,7 +41,7 @@ export const builder = (yargs: Argv): Argv => ) ); -export const handler = async (args: TitleEscrowEndorseChangeOfOwnerCommand): Promise => { +export const handler = async (args: TitleEscrowEndorseTransferOfOwnersCommand): Promise => { trace(`Args: ${JSON.stringify(args, null, 2)}`); try { info( @@ -49,7 +50,7 @@ export const handler = async (args: TitleEscrowEndorseChangeOfOwnerCommand): Pro warn( `Please note that you have to be both the holder and owner of the transferable record, otherwise this command will fail.` ); - const { transactionHash } = await endorseChangeOfOwner(args); + const { transactionHash } = await transferOwners(args); success( `Transferable record with hash ${args.tokenId}'s holder has been successfully endorsed to new owner with address ${args.newOwner} and new holder with address: ${args.newHolder}` ); diff --git a/src/commands/title-escrow/endorse-transfer-of-owner.ts b/src/commands/title-escrow/endorse-transfer-of-owner.ts index 11d55ca7..5f73c43c 100644 --- a/src/commands/title-escrow/endorse-transfer-of-owner.ts +++ b/src/commands/title-escrow/endorse-transfer-of-owner.ts @@ -1,8 +1,8 @@ import { Argv } from "yargs"; import { error, info, success, warn } from "signale"; import { getLogger } from "../../logger"; -import { endorseTransferOfOwner } from "../../implementations/title-escrow/endorseTransferOfOwner"; -import { BaseTitleEscrowCommand as TitleEscrowEndorseTransferOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { endorseNominatedBeneficiary } from "../../implementations/title-escrow/endorseNominatedBeneficiary"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; import { getErrorMessage, getEtherscanAddress } from "../../utils"; @@ -28,10 +28,16 @@ export const builder = (yargs: Argv): Argv => type: "string", demandOption: true, }) + .option("newBeneficiary", { + alias: ["to", "newOwner"], + description: "Address of the beneficiary of the transferable record", + type: "string", + demandOption: true, + }) ) ); -export const handler = async (args: TitleEscrowEndorseTransferOfOwnerCommand): Promise => { +export const handler = async (args: TitleEscrowNominateBeneficiaryCommand): Promise => { trace(`Args: ${JSON.stringify(args, null, 2)}`); try { info( @@ -40,9 +46,9 @@ export const handler = async (args: TitleEscrowEndorseTransferOfOwnerCommand): P warn( `Please note that if you do not have the correct privileges to the transferable record, then this command will fail.` ); - const { transactionReceipt, approvedHolder, approvedOwner } = await endorseTransferOfOwner(args); + const { transactionReceipt, nominatedBeneficiary } = await endorseNominatedBeneficiary(args); success( - `Transferable record with hash ${args.tokenId}'s holder has been successfully endorsed to approved owner at ${approvedOwner} and approved holder at ${approvedHolder}` + `Transferable record with hash ${args.tokenId}'s holder has been successfully endorsed to approved beneficiary at ${nominatedBeneficiary}` ); info( `Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transactionReceipt.transactionHash}` diff --git a/src/commands/title-escrow/nominate-change-of-owner.ts b/src/commands/title-escrow/nominate-change-of-owner.ts index 654eba4e..8887cedb 100644 --- a/src/commands/title-escrow/nominate-change-of-owner.ts +++ b/src/commands/title-escrow/nominate-change-of-owner.ts @@ -1,8 +1,8 @@ import { Argv } from "yargs"; import { error, info, success, warn } from "signale"; import { getLogger } from "../../logger"; -import { nominateChangeOfOwner } from "../../implementations/title-escrow/nominateChangeOfOwner"; -import { TitleEscrowNominateChangeOfOwnerCommand } from "./title-escrow-command.type"; +import { nominateBeneficiary } from "../../implementations/title-escrow/nominateBeneficiary"; +import { TitleEscrowNominateBeneficiaryCommand } from "./title-escrow-command.type"; import { withGasPriceOption, withNetworkAndWalletSignerOption } from "../shared"; import { getErrorMessage, getEtherscanAddress } from "../../utils"; @@ -27,26 +27,27 @@ export const builder = (yargs: Argv): Argv => type: "string", demandOption: true, }) - .option("newOwner", { - description: "Address of the new owner of the transferable record", + .option("newBeneficiary", { + alias: "newOwner", + description: "Address of the beneficiary of the transferable record", type: "string", demandOption: true, }) ) ); -export const handler = async (args: TitleEscrowNominateChangeOfOwnerCommand): Promise => { +export const handler = async (args: TitleEscrowNominateBeneficiaryCommand): Promise => { trace(`Args: ${JSON.stringify(args, null, 2)}`); try { info( - `Connecting to the registry ${args.tokenRegistry} and attempting to nominate the change of owner of the transferable record ${args.tokenId} to new owner at ${args.newOwner}` + `Connecting to the registry ${args.tokenRegistry} and attempting to nominate the change of owner of the transferable record ${args.tokenId} to new owner at ${args.newBeneficiary}` ); warn( `Please note that if you do not have the correct privileges to the transferable record, then this command will fail.` ); - const { transactionHash } = await nominateChangeOfOwner(args); + const { transactionHash } = await nominateBeneficiary(args); success( - `Transferable record with hash ${args.tokenId}'s holder has been successfully nominated to new owner with address ${args.newOwner}` + `Transferable record with hash ${args.tokenId}'s holder has been successfully nominated to new owner with address ${args.newBeneficiary}` ); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transactionHash}`); } catch (e) { diff --git a/src/commands/title-escrow/title-escrow-command.type.ts b/src/commands/title-escrow/title-escrow-command.type.ts index 67824dbe..3885464c 100644 --- a/src/commands/title-escrow/title-escrow-command.type.ts +++ b/src/commands/title-escrow/title-escrow-command.type.ts @@ -5,15 +5,15 @@ export type BaseTitleEscrowCommand = NetworkAndWalletSignerOption & tokenRegistry: string; tokenId: string; }; -export type TitleEscrowChangeHolderCommand = BaseTitleEscrowCommand & { - to: string; +export type TitleEscrowTransferHolderCommand = BaseTitleEscrowCommand & { + newHolder: string; }; -export type TitleEscrowEndorseChangeOfOwnerCommand = BaseTitleEscrowCommand & { +export type TitleEscrowEndorseTransferOfOwnersCommand = BaseTitleEscrowCommand & { newHolder: string; newOwner: string; }; -export type TitleEscrowNominateChangeOfOwnerCommand = BaseTitleEscrowCommand & { - newOwner: string; +export type TitleEscrowNominateBeneficiaryCommand = BaseTitleEscrowCommand & { + newBeneficiary: string; }; diff --git a/src/commands/token-registry/issue.ts b/src/commands/token-registry/issue.ts index 6eac80f7..2d527285 100644 --- a/src/commands/token-registry/issue.ts +++ b/src/commands/token-registry/issue.ts @@ -27,8 +27,13 @@ export const builder = (yargs: Argv): Argv => type: "string", demandOption: true, }) - .option("to", { - description: "Initial recipient of the tokenId", + .option("beneficiary", { + description: "Initial beneficiary of the tokenId", + type: "string", + demandOption: true, + }) + .option("holder", { + description: "Initial holder of the tokenId", type: "string", demandOption: true, }) @@ -38,13 +43,15 @@ export const builder = (yargs: Argv): Argv => export const handler = async (args: TokenRegistryIssueCommand): Promise => { trace(`Args: ${JSON.stringify(args, null, 2)}`); try { - info(`Issuing ${args.tokenId} to the initial recipient ${args.to} in the registry ${args.address}`); + info( + `Issuing ${args.tokenId} to the initial recipient ${args.beneficiary} and initial holder ${args.holder} in the registry ${args.address}` + ); const { transactionHash } = await issueToTokenRegistry({ ...args, tokenId: addAddressPrefix(args.tokenId), }); success( - `Token with hash ${args.tokenId} has been issued on ${args.address} with the initial recipient being ${args.to}` + `Token with hash ${args.tokenId} has been issued on ${args.address} with the initial recipient being ${args.beneficiary} and initial holder ${args.holder}` ); info(`Find more details at ${getEtherscanAddress({ network: args.network })}/tx/${transactionHash}`); return args.address; diff --git a/src/commands/token-registry/token-registry-command.type.ts b/src/commands/token-registry/token-registry-command.type.ts index b6dc6fcf..24f212d2 100644 --- a/src/commands/token-registry/token-registry-command.type.ts +++ b/src/commands/token-registry/token-registry-command.type.ts @@ -3,6 +3,7 @@ import { GasOption, NetworkAndWalletSignerOption } from "../shared"; export type TokenRegistryIssueCommand = NetworkAndWalletSignerOption & GasOption & { address: string; - to: string; + beneficiary: string; + holder: string; tokenId: string; }; diff --git a/src/implementations/config/helpers.ts b/src/implementations/config/helpers.ts index a4a77ffa..dd5d317e 100644 --- a/src/implementations/config/helpers.ts +++ b/src/implementations/config/helpers.ts @@ -118,7 +118,6 @@ export const getTokenRegistryAddress = async ( encryptedWalletPath, passedOnWallet, network, - gasPriceScale: 1, dryRun: false, registryName: "Token Registry", registrySymbol: "TR", @@ -137,7 +136,6 @@ export const getDocumentStoreAddress = async ( encryptedWalletPath, passedOnWallet, network, - gasPriceScale: 1, dryRun: false, storeName: "Document Store", }); diff --git a/src/implementations/deploy/document-store/document-store.test.ts b/src/implementations/deploy/document-store/document-store.test.ts index 4b210c2f..79a51cff 100644 --- a/src/implementations/deploy/document-store/document-store.test.ts +++ b/src/implementations/deploy/document-store/document-store.test.ts @@ -11,7 +11,6 @@ const deployParams: DeployDocumentStoreCommand = { owner: "0x1234", network: "goerli", key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, dryRun: false, }; @@ -38,7 +37,6 @@ describe("document-store", () => { await deployDocumentStore({ storeName: "Test", network: "goerli", - gasPriceScale: 1, dryRun: false, }); @@ -51,7 +49,6 @@ describe("document-store", () => { storeName: "Test", network: "goerli", keyFile: join(__dirname, "..", "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, dryRun: false, }); @@ -68,7 +65,6 @@ describe("document-store", () => { expect(mockedDeploy.mock.calls[0][0]).toStrictEqual(deployParams.storeName); expect(mockedDeploy.mock.calls[0][1]).toStrictEqual(deployParams.owner); // price should be any length string of digits - expect(mockedDeploy.mock.calls[0][2].gasPrice.toString()).toStrictEqual(expect.stringMatching(/\d+/)); expect(instance.contractAddress).toBe("contractAddress"); }); @@ -82,7 +78,6 @@ describe("document-store", () => { deployDocumentStore({ storeName: "Test", network: "goerli", - gasPriceScale: 1, dryRun: false, }) ).rejects.toThrow( @@ -96,7 +91,6 @@ describe("document-store", () => { await deployDocumentStore({ storeName: "Test", network: "goerli", - gasPriceScale: 1, dryRun: false, }); diff --git a/src/implementations/deploy/document-store/document-store.ts b/src/implementations/deploy/document-store/document-store.ts index ddbc38bd..4a00d1e6 100644 --- a/src/implementations/deploy/document-store/document-store.ts +++ b/src/implementations/deploy/document-store/document-store.ts @@ -11,27 +11,22 @@ export const deployDocumentStore = async ({ storeName, owner, network, - gasPriceScale, dryRun, passedOnWallet, // passedOnWallet variable will only be used if we are calling it from create. ...rest }: DeployDocumentStoreCommand): Promise<{ contractAddress: string }> => { const wallet = passedOnWallet ? passedOnWallet : await getWalletOrSigner({ network, ...rest }); const ownerAddress = owner ?? (await wallet.getAddress()); - if (dryRun) { await dryRunMode({ - gasPriceScale: gasPriceScale, transaction: new DocumentStoreFactory().getDeployTransaction(storeName, ownerAddress), network, }); process.exit(0); } - - const gasPrice = await wallet.provider.getGasPrice(); const factory = new DocumentStoreFactory(wallet); signale.await(`Sending transaction to pool`); - const transaction = await factory.deploy(storeName, ownerAddress, { gasPrice: gasPrice.mul(gasPriceScale) }); + const transaction = await factory.deploy(storeName, ownerAddress); trace(`Tx hash: ${transaction.deployTransaction.hash}`); trace(`Block Number: ${transaction.deployTransaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.deployTransaction.hash} to be mined`); diff --git a/src/implementations/deploy/title-escrow-creator/index.ts b/src/implementations/deploy/title-escrow-creator/index.ts deleted file mode 100644 index 02b22336..00000000 --- a/src/implementations/deploy/title-escrow-creator/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./title-escrow-creator"; diff --git a/src/implementations/deploy/title-escrow-creator/title-escrow-creator.test.ts b/src/implementations/deploy/title-escrow-creator/title-escrow-creator.test.ts deleted file mode 100644 index 1e9a3fe3..00000000 --- a/src/implementations/deploy/title-escrow-creator/title-escrow-creator.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { deployTitleEscrowCreator } from "./title-escrow-creator"; -import { join } from "path"; -import { TitleEscrowCreatorFactory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; -import { DeployTitleEscrowCreatorCommand } from "../../../commands/deploy/deploy.types"; - -jest.mock("@govtechsg/token-registry"); - -const deployParams: DeployTitleEscrowCreatorCommand = { - network: "goerli", - key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, - dryRun: false, -}; - -describe("token-registry", () => { - describe("deployTitleEscrowCreator", () => { - const tokenFactory: any = TitleEscrowCreatorFactory; - const mockedTokenFactory: jest.Mock = tokenFactory; - const mockedDeploy: jest.Mock = mockedTokenFactory.prototype.deploy; - // increase timeout because ethers is throttling - jest.setTimeout(30000); - - beforeEach(() => { - mockedTokenFactory.mockReset(); - mockedDeploy.mockReset(); - mockedDeploy.mockResolvedValue({ - deployTransaction: { hash: "hash", wait: () => Promise.resolve({ contractAddress: "contractAddress" }) }, - }); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await deployTitleEscrowCreator({ - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await deployTitleEscrowCreator({ - network: "goerli", - keyFile: join(__dirname, "..", "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and return the deployed instance", async () => { - const instance = await deployTitleEscrowCreator(deployParams); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - // price should be any length string of digits - expect(mockedDeploy.mock.calls[0][0].gasPrice.toString()).toStrictEqual(expect.stringMatching(/\d+/)); - expect(instance.contractAddress).toBe("contractAddress"); - }); - - it("should allow errors to bubble up", async () => { - mockedDeploy.mockRejectedValue(new Error("An Error")); - await expect(deployTitleEscrowCreator(deployParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - delete process.env.OA_PRIVATE_KEY; - await expect( - deployTitleEscrowCreator({ - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }) - ).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); - }); - }); -}); diff --git a/src/implementations/deploy/title-escrow-creator/title-escrow-creator.ts b/src/implementations/deploy/title-escrow-creator/title-escrow-creator.ts deleted file mode 100644 index a2a99a58..00000000 --- a/src/implementations/deploy/title-escrow-creator/title-escrow-creator.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TitleEscrowCreatorFactory } from "@govtechsg/token-registry"; -import { getWalletOrSigner } from "../../utils/wallet"; -import signale from "signale"; -import { getLogger } from "../../../logger"; -import { TransactionReceipt } from "@ethersproject/providers"; -import { DeployTitleEscrowCreatorCommand } from "../../../commands/deploy/deploy.types"; -import { dryRunMode } from "../../utils/dryRun"; - -const { trace } = getLogger("deploy:title-escrow-creator"); - -export const deployTitleEscrowCreator = async ({ - network, - gasPriceScale, - dryRun, - ...rest -}: DeployTitleEscrowCreatorCommand): Promise => { - if (dryRun) { - const factory = new TitleEscrowCreatorFactory(); - await dryRunMode({ - network, - gasPriceScale: gasPriceScale, - transaction: factory.getDeployTransaction(), - }); - process.exit(0); - } - const wallet = await getWalletOrSigner({ network, ...rest }); - const gasPrice = await wallet.provider.getGasPrice(); - const factory = new TitleEscrowCreatorFactory(wallet); - signale.await(`Sending transaction to pool`); - const transaction = await factory.deploy({ gasPrice: gasPrice.mul(gasPriceScale) }); - trace(`Tx hash: ${transaction.deployTransaction.hash}`); - trace(`Block Number: ${transaction.deployTransaction.blockNumber}`); - signale.await(`Waiting for transaction ${transaction.deployTransaction.hash} to be mined`); - return transaction.deployTransaction.wait(); -}; diff --git a/src/implementations/deploy/title-escrow/index.ts b/src/implementations/deploy/title-escrow/index.ts deleted file mode 100644 index 5363eeee..00000000 --- a/src/implementations/deploy/title-escrow/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./title-escrow"; diff --git a/src/implementations/deploy/title-escrow/title-escrow.test.ts b/src/implementations/deploy/title-escrow/title-escrow.test.ts deleted file mode 100644 index f8891dc7..00000000 --- a/src/implementations/deploy/title-escrow/title-escrow.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { deployTitleEscrow } from "./title-escrow"; -import { join } from "path"; -import { TitleEscrowFactory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; -import { DeployTitleEscrowCommand } from "../../../commands/deploy/deploy.types"; - -jest.mock("@govtechsg/token-registry"); - -const deployParams: DeployTitleEscrowCommand = { - tokenRegistry: "0x0000000000000000000000000000000000000000", - beneficiary: "0x0000000000000000000000000000000000000001", - holder: "0x0000000000000000000000000000000000000002", - titleEscrowFactory: "0x0000000000000000000000000000000000000003", - network: "goerli", - key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, - dryRun: false, -}; - -describe("token-registry", () => { - describe("deployTitleEscrow", () => { - const tokenFactory: any = TitleEscrowFactory; - const mockedTokenFactory: jest.Mock = tokenFactory; - const mockedDeploy: jest.Mock = mockedTokenFactory.prototype.deploy; - // increase timeout because ethers is throttling - jest.setTimeout(30000); - - beforeEach(() => { - mockedTokenFactory.mockReset(); - mockedDeploy.mockReset(); - mockedDeploy.mockResolvedValue({ - deployTransaction: { hash: "hash", wait: () => Promise.resolve({ contractAddress: "contractAddress" }) }, - }); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await deployTitleEscrow({ - tokenRegistry: "0x0000000000000000000000000000000000000000", - beneficiary: "0x0000000000000000000000000000000000000001", - holder: "0x0000000000000000000000000000000000000002", - titleEscrowFactory: "0x0000000000000000000000000000000000000003", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await deployTitleEscrow({ - network: "goerli", - tokenRegistry: "0x0000000000000000000000000000000000000000", - beneficiary: "0x0000000000000000000000000000000000000001", - holder: "0x0000000000000000000000000000000000000002", - titleEscrowFactory: "0x0000000000000000000000000000000000000003", - keyFile: join(__dirname, "..", "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and return the deployed instance", async () => { - const instance = await deployTitleEscrow(deployParams); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedDeploy.mock.calls[0][0]).toEqual("0x0000000000000000000000000000000000000000"); - expect(mockedDeploy.mock.calls[0][1]).toEqual("0x0000000000000000000000000000000000000001"); - expect(mockedDeploy.mock.calls[0][2]).toEqual("0x0000000000000000000000000000000000000002"); - expect(mockedDeploy.mock.calls[0][3]).toEqual("0x0000000000000000000000000000000000000003"); - // price should be any length string of digits - expect(mockedDeploy.mock.calls[0][4].gasPrice.toString()).toStrictEqual(expect.stringMatching(/\d+/)); - expect(instance.contractAddress).toBe("contractAddress"); - }); - - it("should allow errors to bubble up", async () => { - mockedDeploy.mockRejectedValue(new Error("An Error")); - await expect(deployTitleEscrow(deployParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - delete process.env.OA_PRIVATE_KEY; - await expect( - deployTitleEscrow({ - network: "goerli", - tokenRegistry: "0x0000000000000000000000000000000000000000", - beneficiary: "0x0000000000000000000000000000000000000001", - holder: "0x0000000000000000000000000000000000000002", - titleEscrowFactory: "0x0000000000000000000000000000000000000003", - gasPriceScale: 1, - dryRun: false, - }) - ).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); - }); - }); -}); diff --git a/src/implementations/deploy/title-escrow/title-escrow.ts b/src/implementations/deploy/title-escrow/title-escrow.ts deleted file mode 100644 index c697df31..00000000 --- a/src/implementations/deploy/title-escrow/title-escrow.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { TransactionReceipt } from "@ethersproject/providers"; -import { TitleEscrowFactory } from "@govtechsg/token-registry"; -import signale from "signale"; -import { DeployTitleEscrowCommand } from "../../../commands/deploy/deploy.types"; -import { getLogger } from "../../../logger"; -import { dryRunMode } from "../../utils/dryRun"; -import { validateAddress } from "../../utils/validation"; -import { getWalletOrSigner } from "../../utils/wallet"; - -const { trace } = getLogger("deploy:title-escrow"); - -const CREATOR_CONTRACTS: { [network: string]: string } = { - mainnet: "0x907A4D491A09D59Bcb5dC38eeb9d121ac47237F1", - goerli: "0x3906daFc722089A8eb3D07D833CDE3C84629FF52", - sepolia: "0xebcFFcDDf84BA6C66C83aE377E41611A43b30c34", - mumbai: "0xc60E5d2f8ca962f7803B28487fa7cB507daDefE9", -}; - -export const getDefaultEscrowFactory = (network: string): string => { - const address = CREATOR_CONTRACTS[network]; - if (!address) - throw new Error( - "Title escrow creator not found on this network, please deploy one onto this network and specify the address with the -c flag" - ); - return address; -}; - -export const deployTitleEscrow = async ({ - tokenRegistry, - beneficiary, - holder, - titleEscrowFactory, - network, - gasPriceScale, - dryRun, - ...rest -}: DeployTitleEscrowCommand): Promise => { - const titleEscrowFactoryAddress = titleEscrowFactory || getDefaultEscrowFactory(network); - validateAddress(tokenRegistry); - validateAddress(beneficiary); - validateAddress(holder); - validateAddress(titleEscrowFactoryAddress); - - if (dryRun) { - const factory = new TitleEscrowFactory(); - await dryRunMode({ - network, - gasPriceScale: gasPriceScale, - transaction: factory.getDeployTransaction(tokenRegistry, beneficiary, holder, titleEscrowFactoryAddress), - }); - process.exit(0); - } - - const wallet = await getWalletOrSigner({ network, ...rest }); - const gasPrice = await wallet.provider.getGasPrice(); - - const factory = new TitleEscrowFactory(wallet); - signale.await(`Sending transaction to pool`); - const transaction = await factory.deploy(tokenRegistry, beneficiary, holder, titleEscrowFactoryAddress, { - gasPrice: gasPrice.mul(gasPriceScale), - }); - trace(`Tx hash: ${transaction.deployTransaction.hash}`); - trace(`Block Number: ${transaction.deployTransaction.blockNumber}`); - signale.await(`Waiting for transaction ${transaction.deployTransaction.hash} to be mined`); - return transaction.deployTransaction.wait(); -}; diff --git a/src/implementations/deploy/token-registry/helpers.test.ts b/src/implementations/deploy/token-registry/helpers.test.ts new file mode 100644 index 00000000..e892837a --- /dev/null +++ b/src/implementations/deploy/token-registry/helpers.test.ts @@ -0,0 +1,44 @@ +import { encodeInitParams, retrieveFactoryAddress } from "./helpers"; +import { isAddress } from "ethers/lib/utils"; + +describe("valid Token Registry Factory Address", () => { + it("should return deployer address", () => { + const address = retrieveFactoryAddress(5, undefined); + + expect(isAddress(address.titleEscrowFactory)).toBe(true); + expect(isAddress(address.tokenImplementation)).toBe(true); + expect(isAddress(address.deployer)).toBe(true); + }); + + it("should return provided deployer address", () => { + const suppliedAddress = { + titleEscrowFactory: "0xd6C249d0756059E21Ef4Aef4711B69b76927BEA7", + tokenImplementation: "", + deployer: "", + }; + const address = retrieveFactoryAddress(5, suppliedAddress); + + expect(address.titleEscrowFactory).toBe(suppliedAddress.titleEscrowFactory); + expect(isAddress(address.tokenImplementation)).toBe(true); + expect(isAddress(address.deployer)).toBe(true); + }); + + it("should reject invalid chainId", () => { + const chainId = 2022; + expect(() => { + retrieveFactoryAddress(chainId); + }).toThrow(`ChainId ${chainId} currently is not supported. Use token-registry to deploy.`); + }); +}); +describe("valid encodeInit parameters", () => { + it("should encode parameters correctly", () => { + const params = encodeInitParams({ + name: "Token Registry Factory", + symbol: "TCR", + deployer: "0xd6C249d0756059E21Ef4Aef4711B69b76927BEA7", + }); + expect(params).toContain( + "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000d6c249d0756059e21ef4aef4711b69b76927bea70000000000000000000000000000000000000000000000000000000000000016546f6b656e20526567697374727920466163" + ); + }); +}); diff --git a/src/implementations/deploy/token-registry/helpers.ts b/src/implementations/deploy/token-registry/helpers.ts new file mode 100644 index 00000000..68393d93 --- /dev/null +++ b/src/implementations/deploy/token-registry/helpers.ts @@ -0,0 +1,48 @@ +import { ethers } from "ethers"; +import { constants } from "@govtechsg/token-registry"; +import { isAddress } from "ethers/lib/utils"; + +export interface DeployContractAddress { + titleEscrowFactory: string; + tokenImplementation: string; + deployer: string; +} + +export interface Params { + name: string; + symbol: string; + deployer: string; +} + +export const encodeInitParams = ({ name, symbol, deployer }: Params): string => { + return ethers.utils.defaultAbiCoder.encode(["string", "string", "address"], [name, symbol, deployer]); +}; + +export const retrieveFactoryAddress = ( + chainId: number, + inputAddress?: DeployContractAddress +): DeployContractAddress => { + const { contractAddress } = constants; + + if (!chainId) { + throw new Error(`Invalid chain ID: ${chainId}`); + } + + const titleEscrowFactory = inputAddress?.titleEscrowFactory || contractAddress.TitleEscrowFactory[chainId] || ""; + const tokenImplementation = inputAddress?.tokenImplementation || contractAddress.TokenImplementation[chainId] || ""; + const deployer = inputAddress?.deployer || contractAddress.Deployer[chainId] || ""; + + if (!isAddress(tokenImplementation) || !isAddress(deployer)) { + throw new Error(`ChainId ${chainId} currently is not supported. Use token-registry to deploy.`); + } + + if (!isAddress(titleEscrowFactory)) { + throw new Error(`ChainId ${chainId} currently is not supported. Supply a factory address.`); + } + + return { + titleEscrowFactory, + tokenImplementation, + deployer, + } as DeployContractAddress; +}; diff --git a/src/implementations/deploy/token-registry/token-registry.test.ts b/src/implementations/deploy/token-registry/token-registry.test.ts index b354315e..a5db0946 100644 --- a/src/implementations/deploy/token-registry/token-registry.test.ts +++ b/src/implementations/deploy/token-registry/token-registry.test.ts @@ -1,96 +1,88 @@ import { deployTokenRegistry } from "./token-registry"; -import { join } from "path"; -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; +import { encodeInitParams } from "./helpers"; +import { Contract } from "ethers"; import { DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; - -jest.mock("@govtechsg/token-registry"); +import { DeploymentEvent } from "@govtechsg/token-registry/dist/contracts/contracts/utils/TDocDeployer"; const deployParams: DeployTokenRegistryCommand = { registryName: "Test", registrySymbol: "Tst", network: "goerli", key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, dryRun: false, }; -describe("token-registry", () => { - describe("deployTokenRegistry", () => { - const tokenFactory: any = TradeTrustErc721Factory; - const mockedTokenFactory: jest.Mock = tokenFactory; - const mockedDeploy: jest.Mock = mockedTokenFactory.prototype.deploy; - // increase timeout because ethers is throttling - jest.setTimeout(30000); - - beforeEach(() => { - mockedTokenFactory.mockReset(); - mockedDeploy.mockReset(); - mockedDeploy.mockResolvedValue({ - deployTransaction: { hash: "hash", wait: () => Promise.resolve({ contractAddress: "contractAddress" }) }, - }); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; +describe("deploy Token Registry", () => { + const mockedEthersContract: jest.Mock = Contract as any; + // eslint-disable-next-line jest/prefer-spy-on + mockedEthersContract.prototype.deploy = jest.fn(); + const mockedDeploy: jest.Mock = mockedEthersContract.prototype.deploy; - await deployTokenRegistry({ - registryName: "Test", - registrySymbol: "Tst", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }); + // increase timeout because ethers is throttling + jest.setTimeout(30000); - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + beforeEach(() => { + mockedDeploy.mockReset(); + mockedDeploy.mockResolvedValue({ + hash: "hash", + blockNumber: "blockNumber", + wait: () => + Promise.resolve({ + events: [ + { + topics: [ + "0x3588ebb5c75fdf91927f8472318f41513ee567c2612a5ce52ac840dcf6f162f5", // deployment + "0x000000000000000000000000426c58c2b29111eafc53bdcb9c99dc7714fdb262", + "0x000000000000000000000000e5c75026d5f636c89cc77583b6bce7c99f512763", + "0x0000000000000000000000008d366250a96debe81c8619459a503a0eebe33ca6", + ], + data: "0x000000000000000000000000878a327daa390bc602ae259d3a374610356b6485000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000008d366250a96debe81c8619459a503a0eebe33ca60000000000000000000000000000000000000000000000000000000000000011563420546f6b656e20526567697374727900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034d54540000000000000000000000000000000000000000000000000000000000", + args: [ + "0xd6C249d0756059E21Ef4Aef4711B69b76927BEA7", + "0xC78BA1a49663Ef8b920F36B036E91Ab40D8F26D6", + "0x8d366250A96deBE81C8619459a503a0eEBE33ca6", + "0x878A327daA390Bc602Ae259D3A374610356b6485", + "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000008d366250a96debe81c8619459a503a0eebe33ca60000000000000000000000000000000000000000000000000000000000000011563420546f6b656e20526567697374727900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034d54540000000000000000000000000000000000000000000000000000000000", + ] as unknown as DeploymentEvent, + }, + ], + }), }); + }); - it("should take in the key from key file", async () => { - await deployTokenRegistry({ - registryName: "Test", - registrySymbol: "Tst", - network: "goerli", - keyFile: join(__dirname, "..", "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, - dryRun: false, - }); + it("should pass in the correct params and return the deployed instance", async () => { + await deployTokenRegistry(deployParams); - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); + const expectedInitParams = encodeInitParams({ + name: deployParams.registryName, + symbol: deployParams.registrySymbol, + deployer: "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf", }); - it("should pass in the correct params and return the deployed instance", async () => { - const instance = await deployTokenRegistry(deployParams); - - const passedSigner: Wallet = mockedTokenFactory.mock.calls[0][0]; + expect(mockedDeploy.mock.calls[0][0]).toEqual("0xC78BA1a49663Ef8b920F36B036E91Ab40D8F26D6"); + expect(mockedDeploy.mock.calls[0][1]).toEqual(expectedInitParams); - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedDeploy.mock.calls[0][0]).toEqual(deployParams.registryName); - expect(mockedDeploy.mock.calls[0][1]).toEqual(deployParams.registrySymbol); - // price should be any length string of digits - expect(mockedDeploy.mock.calls[0][2].gasPrice.toString()).toStrictEqual(expect.stringMatching(/\d+/)); - expect(instance.contractAddress).toBe("contractAddress"); - }); + // price should be any length string of digits + // expect(mockedDeploy.mock.calls[0][2].gasPrice.toString()).toStrictEqual(expect.stringMatching(/\d+/)); + // expect(instance.contractAddress).toBe("contractAddress"); // TODO + }); - it("should allow errors to bubble up", async () => { - mockedDeploy.mockRejectedValue(new Error("An Error")); - await expect(deployTokenRegistry(deployParams)).rejects.toThrow("An Error"); - }); + it("should allow errors to bubble up", async () => { + mockedDeploy.mockRejectedValue(new Error("An Error")); + await expect(deployTokenRegistry(deployParams)).rejects.toThrow("An Error"); + }); - it("should throw when keys are not found anywhere", async () => { - delete process.env.OA_PRIVATE_KEY; - await expect( - deployTokenRegistry({ - registryName: "Test", - registrySymbol: "Tst", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }) - ).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); - }); + it("should throw when keys are not found anywhere", async () => { + delete process.env.OA_PRIVATE_KEY; + await expect( + deployTokenRegistry({ + registryName: "Test", + registrySymbol: "Tst", + network: "goerli", + dryRun: false, + }) + ).rejects.toThrow( + "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" + ); }); }); diff --git a/src/implementations/deploy/token-registry/token-registry.ts b/src/implementations/deploy/token-registry/token-registry.ts index 5fa6197d..e243113e 100644 --- a/src/implementations/deploy/token-registry/token-registry.ts +++ b/src/implementations/deploy/token-registry/token-registry.ts @@ -1,9 +1,11 @@ -import { TransactionReceipt } from "@ethersproject/providers"; -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TDocDeployer, TDocDeployer__factory } from "@govtechsg/token-registry/contracts"; +import { BigNumber, ethers } from "ethers"; +import { DeploymentEvent } from "@govtechsg/token-registry/dist/contracts/contracts/utils/TDocDeployer"; +import { utils } from "@govtechsg/token-registry"; +import { DeployContractAddress, encodeInitParams, retrieveFactoryAddress } from "./helpers"; import signale from "signale"; import { DeployTokenRegistryCommand } from "../../../commands/deploy/deploy.types"; import { getLogger } from "../../../logger"; -import { dryRunMode } from "../../utils/dryRun"; import { getWalletOrSigner } from "../../utils/wallet"; const { trace } = getLogger("deploy:token-registry"); @@ -11,29 +13,56 @@ const { trace } = getLogger("deploy:token-registry"); export const deployTokenRegistry = async ({ registryName, registrySymbol, + factoryAddress, + tokenImplementationAddress, + deployerAddress, network, - gasPriceScale, dryRun, passedOnWallet, // passedOnWallet variable will only be used if we are calling it from create. ...rest -}: DeployTokenRegistryCommand): Promise => { +}: DeployTokenRegistryCommand): Promise<{ contractAddress: string }> => { const wallet = passedOnWallet ? passedOnWallet : await getWalletOrSigner({ network, ...rest }); - const factory = new TradeTrustErc721Factory(wallet); + const chainId = await wallet.getChainId(); + const deployContractAddressInput: DeployContractAddress = { + titleEscrowFactory: factoryAddress || "", + tokenImplementation: tokenImplementationAddress || "", + deployer: deployerAddress || "", + }; + const deployContractAddress: DeployContractAddress = retrieveFactoryAddress(chainId, deployContractAddressInput); + + const factory = new ethers.Contract( + deployContractAddress.deployer, + TDocDeployer__factory.createInterface(), + wallet + ) as TDocDeployer; + signale.info(`Using ${deployContractAddress.titleEscrowFactory} as Title Escrow factory.`); + + const initParam = encodeInitParams({ + name: registryName, + symbol: registrySymbol, + deployer: await wallet.getAddress(), + }); + if (dryRun) { - const unsignedTx = factory.getDeployTransaction(registryName, registrySymbol, {}); - const tx = await wallet.populateTransaction(unsignedTx); - await dryRunMode({ - network, - gasPriceScale: gasPriceScale, - transaction: tx, - }); + const estimatedGas: BigNumber = await factory.estimateGas.deploy( + deployContractAddress.tokenImplementation, + initParam + ); + signale.info("Dry Run is unavailable for token registry deploy"); + signale.info(`Estimated Gas Required: ${estimatedGas.toString()}`); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - const transaction = await factory.deploy(registryName, registrySymbol, { gasPrice: gasPrice.mul(gasPriceScale) }); - trace(`Tx hash: ${transaction.deployTransaction.hash}`); - trace(`Block Number: ${transaction.deployTransaction.blockNumber}`); - signale.await(`Waiting for transaction ${transaction.deployTransaction.hash} to be mined`); - return transaction.deployTransaction.wait(); + + const transaction = await factory.deploy(deployContractAddress.tokenImplementation, initParam); + trace(`Tx hash: ${transaction.hash}`); + trace(`Block Number: ${transaction.blockNumber}`); + signale.await(`Waiting for transaction ${transaction.hash} to be mined`); + const receipt = await transaction.wait(); + const registryAddress = utils.getEventFromReceipt( + receipt, + factory.interface.getEventTopic("Deployment") + ).args.deployed; + return { contractAddress: registryAddress }; }; diff --git a/src/implementations/document-store/issue.test.ts b/src/implementations/document-store/issue.test.ts index f1b49397..dffc6c3c 100644 --- a/src/implementations/document-store/issue.test.ts +++ b/src/implementations/document-store/issue.test.ts @@ -1,9 +1,9 @@ import { issueToDocumentStore } from "./issue"; -import { join } from "path"; import { Wallet } from "ethers"; import { DocumentStoreFactory } from "@govtechsg/document-store"; import { DocumentStoreIssueCommand } from "../../commands/document-store/document-store-command.type"; import { addAddressPrefix } from "../../utils"; +import { join } from "path"; jest.mock("@govtechsg/document-store"); @@ -12,112 +12,106 @@ const deployParams: DocumentStoreIssueCommand = { address: "0x1234", network: "goerli", key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, dryRun: false, }; // TODO the following test is very fragile and might break on every interface change of DocumentStoreFactory // ideally must setup ganache, and run the function over it -describe("document-store", () => { +describe("issue document-store", () => { // increase timeout because ethers is throttling jest.setTimeout(30000); - describe("issueDocumentStore", () => { - const mockedDocumentStoreFactory: jest.Mock = DocumentStoreFactory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnect: jest.Mock = mockedDocumentStoreFactory.connect; - const mockedIssue = jest.fn(); - const mockCallStaticIssue = jest.fn().mockResolvedValue(undefined); - - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedDocumentStoreFactory.mockReset(); - mockedConnect.mockReset(); - mockCallStaticIssue.mockClear(); - mockedConnect.mockReturnValue({ - issue: mockedIssue, - callStatic: { - issue: mockCallStaticIssue, - }, - }); - mockedIssue.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); + const mockedDocumentStoreFactory: jest.Mock = DocumentStoreFactory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnect: jest.Mock = mockedDocumentStoreFactory.connect; + const mockedIssue = jest.fn(); + const mockCallStaticIssue = jest.fn().mockResolvedValue(undefined); + + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedDocumentStoreFactory.mockReset(); + mockedConnect.mockReset(); + mockCallStaticIssue.mockClear(); + mockedConnect.mockReturnValue({ + issue: mockedIssue, + callStatic: { + issue: mockCallStaticIssue, + }, }); + mockedIssue.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); + }); - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; + it("should take in the key from environment variable", async () => { + process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - await issueToDocumentStore({ - hash: "0xabcd", - address: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + await issueToDocumentStore({ + hash: "0xabcd", + address: "0x1234", + network: "goerli", + dryRun: false, }); - it("should take in the key from key file", async () => { - await issueToDocumentStore({ - hash: "0xabcd", - address: "0x1234", - network: "goerli", - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, - dryRun: false, - }); + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + }); - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); + it("should pass in the correct params and return the deployed instance", async () => { + const instance = await issueToDocumentStore(deployParams); - it("should pass in the correct params and return the deployed instance", async () => { - const instance = await issueToDocumentStore(deployParams); + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); + expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockCallStaticIssue).toHaveBeenCalledTimes(1); + expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.hash); + expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); + }); - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockCallStaticIssue).toHaveBeenCalledTimes(1); - expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.hash); - expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); + it("should take in the key from key file", async () => { + await issueToDocumentStore({ + hash: "0xabcd", + address: "0x1234", + network: "goerli", + keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), + dryRun: false, }); - it("should accept hash without 0x prefix and return deployed instance", async () => { - const instance = await issueToDocumentStore({ ...deployParams, hash: addAddressPrefix("abcd") }); + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); + }); - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + it("should accept hash without 0x prefix and return deployed instance", async () => { + const instance = await issueToDocumentStore({ ...deployParams, hash: addAddressPrefix("abcd") }); - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockCallStaticIssue).toHaveBeenCalledTimes(1); - expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.hash); - expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); - }); + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - it("should allow errors to bubble up", async () => { - mockedConnect.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(issueToDocumentStore(deployParams)).rejects.toThrow("An Error"); - }); + expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); + expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockCallStaticIssue).toHaveBeenCalledTimes(1); + expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.hash); + expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); + }); - it("should throw when keys are not found anywhere", async () => { - await expect( - issueToDocumentStore({ - hash: "0xabcd", - address: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }) - ).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); + it("should allow errors to bubble up", async () => { + mockedConnect.mockImplementation(() => { + throw new Error("An Error"); }); + await expect(issueToDocumentStore(deployParams)).rejects.toThrow("An Error"); + }); + + it("should throw when keys are not found anywhere", async () => { + await expect( + issueToDocumentStore({ + hash: "0xabcd", + address: "0x1234", + network: "goerli", + dryRun: false, + }) + ).rejects.toThrow( + "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" + ); }); }); diff --git a/src/implementations/document-store/issue.ts b/src/implementations/document-store/issue.ts index 7387ef8c..76172451 100644 --- a/src/implementations/document-store/issue.ts +++ b/src/implementations/document-store/issue.ts @@ -12,7 +12,6 @@ export const issueToDocumentStore = async ({ address, hash, network, - gasPriceScale, dryRun, ...rest }: DocumentStoreIssueCommand): Promise => { @@ -20,21 +19,17 @@ export const issueToDocumentStore = async ({ if (dryRun) { const documentStore = await DocumentStoreFactory.connect(address, wallet); await dryRunMode({ - gasPriceScale: gasPriceScale, estimatedGas: await documentStore.estimateGas.issue(hash), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); signale.await(`Sending transaction to pool`); const documentStore = await DocumentStoreFactory.connect(address, wallet); await documentStore.callStatic.issue(hash); - const transaction = await documentStore.issue(hash, { - gasPrice: gasPrice.mul(gasPriceScale), - }); + const transaction = await documentStore.issue(hash); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/document-store/revoke.test.ts b/src/implementations/document-store/revoke.test.ts index 60ccc278..74989a13 100644 --- a/src/implementations/document-store/revoke.test.ts +++ b/src/implementations/document-store/revoke.test.ts @@ -1,9 +1,10 @@ import { revokeToDocumentStore } from "./revoke"; -import { join } from "path"; + import { Wallet } from "ethers"; import { DocumentStoreFactory } from "@govtechsg/document-store"; import { DocumentStoreRevokeCommand } from "../../commands/document-store/document-store-command.type"; import { addAddressPrefix } from "../../utils"; +import { join } from "path"; jest.mock("@govtechsg/document-store"); @@ -12,7 +13,6 @@ const deployParams: DocumentStoreRevokeCommand = { address: "0x1234", network: "goerli", key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, dryRun: false, }; @@ -45,15 +45,36 @@ describe("document-store", () => { wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); }); + it("should pass in the correct params and return the deployed instance", async () => { + const instance = await revokeToDocumentStore(deployParams); + + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); + expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockCallStaticRevoke).toHaveBeenCalledTimes(1); + expect(mockedRevoke.mock.calls[0][0]).toEqual(deployParams.hash); + expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); + }); + + it("should accept hash without 0x prefix and return deployed instance", async () => { + const instance = await revokeToDocumentStore({ ...deployParams, hash: addAddressPrefix("abcd") }); + + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); + expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockCallStaticRevoke).toHaveBeenCalledTimes(1); + expect(mockedRevoke.mock.calls[0][0]).toEqual(deployParams.hash); + expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); + }); it("should take in the key from environment variable", async () => { process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - await revokeToDocumentStore({ hash: "0xabcd", address: "0x1234", network: "goerli", - gasPriceScale: 1, dryRun: false, }); @@ -67,7 +88,6 @@ describe("document-store", () => { address: "0x1234", network: "goerli", keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, dryRun: false, }); @@ -75,30 +95,6 @@ describe("document-store", () => { expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); }); - it("should pass in the correct params and return the deployed instance", async () => { - const instance = await revokeToDocumentStore(deployParams); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockCallStaticRevoke).toHaveBeenCalledTimes(1); - expect(mockedRevoke.mock.calls[0][0]).toEqual(deployParams.hash); - expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); - }); - - it("should accept hash without 0x prefix and return deployed instance", async () => { - const instance = await revokeToDocumentStore({ ...deployParams, hash: addAddressPrefix("abcd") }); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${deployParams.key}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockCallStaticRevoke).toHaveBeenCalledTimes(1); - expect(mockedRevoke.mock.calls[0][0]).toEqual(deployParams.hash); - expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); - }); - it("should allow errors to bubble up", async () => { mockedConnect.mockImplementation(() => { throw new Error("An Error"); @@ -112,7 +108,6 @@ describe("document-store", () => { hash: "0xabcd", address: "0x1234", network: "goerli", - gasPriceScale: 1, dryRun: false, }) ).rejects.toThrow( diff --git a/src/implementations/document-store/revoke.ts b/src/implementations/document-store/revoke.ts index c5e2aceb..72d1227a 100644 --- a/src/implementations/document-store/revoke.ts +++ b/src/implementations/document-store/revoke.ts @@ -11,7 +11,6 @@ export const revokeToDocumentStore = async ({ address, hash, network, - gasPriceScale, dryRun, ...rest }: DocumentStoreRevokeCommand): Promise<{ transactionHash: string }> => { @@ -19,19 +18,16 @@ export const revokeToDocumentStore = async ({ if (dryRun) { const documentStore = await DocumentStoreFactory.connect(address, wallet); await dryRunMode({ - gasPriceScale: gasPriceScale, estimatedGas: await documentStore.estimateGas.revoke(hash), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); const documentStore = await DocumentStoreFactory.connect(address, wallet); await documentStore.callStatic.revoke(hash); - const transaction = await documentStore.revoke(hash, { - gasPrice: gasPrice.mul(gasPriceScale), - }); + const transaction = await documentStore.revoke(hash); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/document-store/transfer-ownership.test.ts b/src/implementations/document-store/transfer-ownership.test.ts index 35090dc1..f07f36ad 100644 --- a/src/implementations/document-store/transfer-ownership.test.ts +++ b/src/implementations/document-store/transfer-ownership.test.ts @@ -1,9 +1,10 @@ import { transferDocumentStoreOwnershipToWallet } from "./transfer-ownership"; -import { join } from "path"; + import { Wallet } from "ethers"; import { DocumentStoreFactory } from "@govtechsg/document-store"; import { DocumentStoreTransferOwnershipCommand } from "../../commands/document-store/document-store-command.type"; import { addAddressPrefix } from "../../utils"; +import { join } from "path"; jest.mock("@govtechsg/document-store"); @@ -12,7 +13,6 @@ const deployParams: DocumentStoreTransferOwnershipCommand = { address: "0x1234", network: "goerli", key: "0000000000000000000000000000000000000000000000000000000000000001", - gasPriceScale: 1, dryRun: false, }; @@ -45,36 +45,6 @@ describe("document-store", () => { wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await transferDocumentStoreOwnershipToWallet({ - newOwner: "0xabcd", - address: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await transferDocumentStoreOwnershipToWallet({ - newOwner: "0xabcd", - address: "0x1234", - network: "goerli", - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - gasPriceScale: 1, - dryRun: false, - }); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - it("should pass in the correct params and return the deployed instance", async () => { const instance = await transferDocumentStoreOwnershipToWallet(deployParams); @@ -102,25 +72,29 @@ describe("document-store", () => { expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); }); - it("should allow errors to bubble up", async () => { - mockedConnect.mockImplementation(() => { - throw new Error("An Error"); + it("should take in the key from environment variable", async () => { + process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; + await transferDocumentStoreOwnershipToWallet({ + newOwner: "0xabcd", + address: "0x1234", + network: "goerli", + dryRun: false, }); - await expect(transferDocumentStoreOwnershipToWallet(deployParams)).rejects.toThrow("An Error"); + + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); }); + it("should take in the key from key file", async () => { + await transferDocumentStoreOwnershipToWallet({ + newOwner: "0xabcd", + address: "0x1234", + network: "goerli", + keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), + dryRun: false, + }); - it("should throw when keys are not found anywhere", async () => { - await expect( - transferDocumentStoreOwnershipToWallet({ - newOwner: "0xabcd", - address: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, - }) - ).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); + const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; + expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); }); }); }); diff --git a/src/implementations/document-store/transfer-ownership.ts b/src/implementations/document-store/transfer-ownership.ts index dd49230c..a9200ef5 100644 --- a/src/implementations/document-store/transfer-ownership.ts +++ b/src/implementations/document-store/transfer-ownership.ts @@ -12,7 +12,6 @@ export const transferDocumentStoreOwnershipToWallet = async ({ address, newOwner, network, - gasPriceScale, dryRun, ...rest }: DocumentStoreTransferOwnershipCommand): Promise => { @@ -20,20 +19,16 @@ export const transferDocumentStoreOwnershipToWallet = async ({ if (dryRun) { const documentStore = await DocumentStoreFactory.connect(address, wallet); await dryRunMode({ - gasPriceScale: gasPriceScale, estimatedGas: await documentStore.estimateGas.transferOwnership(newOwner), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); signale.await(`Sending transaction to pool`); const documentStore = await DocumentStoreFactory.connect(address, wallet); await documentStore.callStatic.transferOwnership(newOwner); - const transaction = await documentStore.transferOwnership(newOwner, { - gasPrice: gasPrice.mul(gasPriceScale), - }); + const transaction = await documentStore.transferOwnership(newOwner); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/acceptSurrendered.test.ts b/src/implementations/title-escrow/acceptSurrendered.test.ts index a0c1923c..6ec16afb 100644 --- a/src/implementations/title-escrow/acceptSurrendered.test.ts +++ b/src/implementations/title-escrow/acceptSurrendered.test.ts @@ -1,69 +1,46 @@ -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; -import { join } from "path"; + import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { acceptSurrendered } from "./acceptSurrendered"; -jest.mock("@govtechsg/token-registry"); +jest.mock("@govtechsg/token-registry/contracts"); const acceptSurrenderedDocumentParams: TitleEscrowSurrenderDocumentCommand = { tokenRegistry: "0x1122", tokenId: "0x12345", network: "goerli", - gasPriceScale: 1, dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it describe("title-escrow", () => { describe("accepts surrendered transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockDestroyToken = jest.fn(); - const mockCallStaticDestroyToken = jest.fn().mockResolvedValue(undefined); + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockBurnToken = jest.fn(); + const mockCallStaticBurnToken = jest.fn().mockResolvedValue(undefined); beforeEach(() => { delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockReset(); + mockedTradeTrustTokenFactory.mockReset(); mockedConnectERC721.mockReset(); - mockDestroyToken.mockReturnValue({ + mockBurnToken.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); mockedConnectERC721.mockReturnValue({ - destroyToken: mockDestroyToken, + burn: mockBurnToken, callStatic: { - destroyToken: mockCallStaticDestroyToken, + burn: mockCallStaticBurnToken, }, }); - mockDestroyToken.mockClear(); - mockCallStaticDestroyToken.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await acceptSurrendered(acceptSurrenderedDocumentParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + mockBurnToken.mockClear(); + mockCallStaticBurnToken.mockClear(); }); - - it("should take in the key from key file", async () => { - await acceptSurrendered({ - ...acceptSurrenderedDocumentParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - it("should pass in the correct params and successfully accepts a surrendered transferable record", async () => { const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; await acceptSurrendered({ @@ -75,22 +52,8 @@ describe("title-escrow", () => { expect(passedSigner.privateKey).toBe(`0x${privateKey}`); expect(mockedConnectERC721).toHaveBeenCalledWith(acceptSurrenderedDocumentParams.tokenRegistry, passedSigner); - expect(mockCallStaticDestroyToken).toHaveBeenCalledTimes(1); - expect(mockDestroyToken).toHaveBeenCalledTimes(1); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(acceptSurrendered(acceptSurrenderedDocumentParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(acceptSurrendered(acceptSurrenderedDocumentParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); + expect(mockCallStaticBurnToken).toHaveBeenCalledTimes(1); + expect(mockBurnToken).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/implementations/title-escrow/acceptSurrendered.ts b/src/implementations/title-escrow/acceptSurrendered.ts index 7a33e0ef..010805f8 100644 --- a/src/implementations/title-escrow/acceptSurrendered.ts +++ b/src/implementations/title-escrow/acceptSurrendered.ts @@ -1,4 +1,3 @@ -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; @@ -6,6 +5,7 @@ import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from ". import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; +import { TradeTrustToken__factory } from "@govtechsg/token-registry/dist/contracts"; const { trace } = getLogger("title-escrow:acceptSurrendered"); @@ -13,24 +13,22 @@ export const acceptSurrendered = async ({ tokenRegistry: address, tokenId, network, - gasPriceScale, dryRun, ...rest }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); - const tokenRegistryInstance = await TradeTrustErc721Factory.connect(address, wallet); + const tokenRegistryInstance = await TradeTrustToken__factory.connect(address, wallet); if (dryRun) { await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await tokenRegistryInstance.estimateGas.destroyToken(tokenId), + estimatedGas: await tokenRegistryInstance.estimateGas.burn(tokenId), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - await tokenRegistryInstance.callStatic.destroyToken(tokenId); - const transaction = await tokenRegistryInstance.destroyToken(tokenId, { gasPrice: gasPrice.mul(gasPriceScale) }); + await tokenRegistryInstance.callStatic.burn(tokenId); + const transaction = await tokenRegistryInstance.burn(tokenId); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/changeHolder.test.ts b/src/implementations/title-escrow/changeHolder.test.ts deleted file mode 100644 index 6120e075..00000000 --- a/src/implementations/title-escrow/changeHolder.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; -import { join } from "path"; -import { TitleEscrowChangeHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { changeHolderOfTitleEscrow } from "./changeHolder"; - -jest.mock("@govtechsg/token-registry"); - -const changeHolderParams: TitleEscrowChangeHolderCommand = { - to: "0xabcd", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, -}; - -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it -describe("title-escrow", () => { - describe("change holder of transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTokenFactory: jest.Mock = TitleEscrowFactory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); - const mockChangeHolder = jest.fn(); - const mockCallStaticChangeHolder = jest.fn().mockResolvedValue(undefined); - const mockedTitleEscrowAddress = "0x2133"; - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); - mockChangeHolder.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - }); - mockedConnectTokenFactory.mockReturnValue({ - changeHolder: mockChangeHolder, - callStatic: { - changeHolder: mockCallStaticChangeHolder, - }, - }); - - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockClear(); - mockedConnectERC721.mockClear(); - mockedTokenFactory.mockClear(); - mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); - mockChangeHolder.mockClear(); - mockCallStaticChangeHolder.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await changeHolderOfTitleEscrow(changeHolderParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await changeHolderOfTitleEscrow({ - ...changeHolderParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and call the following procedures to invoke a change in holder of a transferable record", async () => { - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await changeHolderOfTitleEscrow({ - ...changeHolderParams, - key: privateKey, - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnectERC721).toHaveBeenCalledWith(changeHolderParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(changeHolderParams.tokenId); - expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockCallStaticChangeHolder).toHaveBeenCalledTimes(1); - expect(mockChangeHolder).toHaveBeenCalledTimes(1); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(changeHolderOfTitleEscrow(changeHolderParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(changeHolderOfTitleEscrow(changeHolderParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); - }); - }); -}); diff --git a/src/implementations/title-escrow/endorseChangeOfOwner.test.ts b/src/implementations/title-escrow/endorseChangeOfOwner.test.ts deleted file mode 100644 index 109fd423..00000000 --- a/src/implementations/title-escrow/endorseChangeOfOwner.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; -import { join } from "path"; -import { TitleEscrowEndorseChangeOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { endorseChangeOfOwner } from "./endorseChangeOfOwner"; - -jest.mock("@govtechsg/token-registry"); - -const endorseChangeOwnerParams: TitleEscrowEndorseChangeOfOwnerCommand = { - newHolder: "0xabcd", - newOwner: "0fosui", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, -}; - -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it -describe("title-escrow", () => { - describe("endorse change of owner of transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTokenFactory: jest.Mock = TitleEscrowFactory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); - const mockTransferToNewEscrow = jest.fn(); - const mockCallStaticTransferToNewEscrow = jest.fn().mockResolvedValue(undefined); - const mockedTitleEscrowAddress = "0x2133"; - const mockedBeneficiary = "0xdssfs"; - const mockedHolder = "0xdsfls"; - const mockGetBeneficiary = jest.fn(); - const mockGetHolder = jest.fn(); - mockGetBeneficiary.mockReturnValue(mockedBeneficiary); - mockGetHolder.mockReturnValue(mockedHolder); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - }); - mockedConnectTokenFactory.mockReturnValue({ - transferToNewEscrow: mockTransferToNewEscrow, - beneficiary: mockGetBeneficiary, - holder: mockGetHolder, - callStatic: { - transferToNewEscrow: mockCallStaticTransferToNewEscrow, - }, - }); - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); - mockTransferToNewEscrow.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockClear(); - mockedConnectERC721.mockClear(); - mockedTokenFactory.mockClear(); - mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); - mockTransferToNewEscrow.mockClear(); - mockGetBeneficiary.mockClear(); - mockGetHolder.mockClear(); - mockCallStaticTransferToNewEscrow.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await endorseChangeOfOwner(endorseChangeOwnerParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await endorseChangeOfOwner({ - ...endorseChangeOwnerParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and call the following procedures to invoke an endorsement of change of owner of a transferable record", async () => { - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await endorseChangeOfOwner({ - ...endorseChangeOwnerParams, - key: privateKey, - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnectERC721).toHaveBeenCalledWith(endorseChangeOwnerParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(endorseChangeOwnerParams.tokenId); - expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockGetBeneficiary).toHaveBeenCalledTimes(1); - expect(mockGetHolder).toHaveBeenCalledTimes(1); - expect(mockCallStaticTransferToNewEscrow).toHaveBeenCalledTimes(1); - expect(mockTransferToNewEscrow).toHaveBeenCalledTimes(1); - }); - - it("should throw an error if new owner and new holder addresses are the same as current owner and holder addressses", async () => { - mockGetBeneficiary.mockReturnValue(endorseChangeOwnerParams.newOwner); - mockGetHolder.mockReturnValue(endorseChangeOwnerParams.newHolder); - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await expect( - endorseChangeOfOwner({ - ...endorseChangeOwnerParams, - key: privateKey, - }) - ).rejects.toThrow("new owner and new holder addresses are the same as the current owner and holder addresses"); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(endorseChangeOfOwner(endorseChangeOwnerParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(endorseChangeOfOwner(endorseChangeOwnerParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); - }); - }); -}); diff --git a/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts new file mode 100644 index 00000000..ee1ef662 --- /dev/null +++ b/src/implementations/title-escrow/endorseNominatedBeneficiary.test.ts @@ -0,0 +1,94 @@ +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { Wallet } from "ethers"; + +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { endorseNominatedBeneficiary } from "./endorseNominatedBeneficiary"; + +jest.mock("@govtechsg/token-registry/contracts"); + +const endorseNominatedBeneficiaryParams: TitleEscrowNominateBeneficiaryCommand = { + tokenId: "0xzyxw", + tokenRegistry: "0x1234", + newBeneficiary: "0x1232", + network: "goerli", + dryRun: false, +}; + +describe("title-escrow", () => { + describe("endorse transfer of owner of transferable record", () => { + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; + + const mockedTitleEscrowAddress = "0x2133"; + const mockedOwnerOf = jest.fn(); + mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + + const mockTransferOwners = jest.fn(); + const mockCallStaticTransferOwners = jest.fn().mockResolvedValue(undefined); + + const mockedBeneficiary = "0xdssfs"; + const mockGetBeneficiary = jest.fn(); + mockGetBeneficiary.mockReturnValue(mockedBeneficiary); + + mockedConnectERC721.mockReturnValue({ + ownerOf: mockedOwnerOf, + }); + + mockedConnectTokenFactory.mockReturnValue({ + transferBeneficiary: mockTransferOwners, + beneficiary: mockGetBeneficiary, + callStatic: { + transferBeneficiary: mockCallStaticTransferOwners, + }, + }); + mockTransferOwners.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); + + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedTradeTrustTokenFactory.mockClear(); + mockedConnectERC721.mockClear(); + mockedTokenFactory.mockClear(); + mockedConnectTokenFactory.mockClear(); + mockedOwnerOf.mockClear(); + mockTransferOwners.mockClear(); + mockCallStaticTransferOwners.mockClear(); + }); + + it("should pass in the correct params and call the following procedures to invoke an endorsement of transfer of owner of a transferable record", async () => { + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await endorseNominatedBeneficiary({ + ...endorseNominatedBeneficiaryParams, + key: privateKey, + }); + + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${privateKey}`); + expect(mockedConnectERC721).toHaveBeenCalledWith(endorseNominatedBeneficiaryParams.tokenRegistry, passedSigner); + expect(mockedOwnerOf).toHaveBeenCalledWith(endorseNominatedBeneficiaryParams.tokenId); + expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); + expect(mockCallStaticTransferOwners).toHaveBeenCalledTimes(1); + expect(mockTransferOwners).toHaveBeenCalledTimes(1); + }); + + it("should throw an error if nominee is the owner address", async () => { + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await expect( + endorseNominatedBeneficiary({ + ...endorseNominatedBeneficiaryParams, + newBeneficiary: "0xdssfs", + key: privateKey, + }) + ).rejects.toThrow(`new beneficiary address is the same as the current beneficiary address`); + }); + }); +}); diff --git a/src/implementations/title-escrow/endorseNominatedBeneficiary.ts b/src/implementations/title-escrow/endorseNominatedBeneficiary.ts new file mode 100644 index 00000000..df952f28 --- /dev/null +++ b/src/implementations/title-escrow/endorseNominatedBeneficiary.ts @@ -0,0 +1,46 @@ +import signale from "signale"; +import { getLogger } from "../../logger"; +import { getWalletOrSigner } from "../utils/wallet"; +import { connectToTitleEscrow, validateNominateBeneficiary } from "./helpers"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; + +import { dryRunMode } from "../utils/dryRun"; +import { TransactionReceipt } from "@ethersproject/providers"; + +const { trace } = getLogger("title-escrow:endorseTransferOfOwner"); + +export const endorseNominatedBeneficiary = async ({ + tokenRegistry: address, + tokenId, + newBeneficiary, + network, + dryRun, + ...rest +}: TitleEscrowNominateBeneficiaryCommand): Promise<{ + transactionReceipt: TransactionReceipt; + nominatedBeneficiary: string; +}> => { + const wallet = await getWalletOrSigner({ network, ...rest }); + const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + const nominatedBeneficiary = newBeneficiary; + await validateNominateBeneficiary({ beneficiaryNominee: nominatedBeneficiary, titleEscrow }); + if (dryRun) { + await dryRunMode({ + estimatedGas: await titleEscrow.estimateGas.transferBeneficiary(nominatedBeneficiary), + network, + }); + process.exit(0); + } + + signale.await(`Sending transaction to pool`); + await titleEscrow.callStatic.transferBeneficiary(nominatedBeneficiary); + const transaction = await titleEscrow.transferBeneficiary(nominatedBeneficiary); + trace(`Tx hash: ${transaction.hash}`); + trace(`Block Number: ${transaction.blockNumber}`); + signale.await(`Waiting for transaction ${transaction.hash} to be mined`); + const transactionReceipt = await transaction.wait(); + return { + transactionReceipt, + nominatedBeneficiary: nominatedBeneficiary, + }; +}; diff --git a/src/implementations/title-escrow/endorseTransferOfOwner.test.ts b/src/implementations/title-escrow/endorseTransferOfOwner.test.ts deleted file mode 100644 index 1c44dbe5..00000000 --- a/src/implementations/title-escrow/endorseTransferOfOwner.test.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; -import { Wallet, constants } from "ethers"; -import { join } from "path"; -import { BaseTitleEscrowCommand as TitleEscrowEndorseTransferOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { endorseTransferOfOwner } from "./endorseTransferOfOwner"; - -jest.mock("@govtechsg/token-registry"); - -const endorseTransferOfOwnerParams: TitleEscrowEndorseTransferOfOwnerCommand = { - tokenId: "0xzyxw", - tokenRegistry: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, -}; -const GENESIS_ADDRESS = constants.AddressZero; - -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it -describe("title-escrow", () => { - describe("endorse transfer of owner of transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTokenFactory: jest.Mock = TitleEscrowFactory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); - const mockTransferToNewEscrow = jest.fn(); - const mockedTitleEscrowAddress = "0x2133"; - const mockedApprovedBeneficiary = "0xdssfs"; - const mockedApprovedHolder = "0xdsfls"; - const mockGetApprovedBeneficiary = jest.fn(); - const mockGetApprovedHolder = jest.fn(); - const mockCallStaticTransferToNewEscrow = jest.fn().mockResolvedValue(undefined); - mockGetApprovedBeneficiary.mockReturnValue(mockedApprovedBeneficiary); - mockGetApprovedHolder.mockReturnValue(mockedApprovedHolder); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - }); - mockedConnectTokenFactory.mockReturnValue({ - transferToNewEscrow: mockTransferToNewEscrow, - approvedBeneficiary: mockGetApprovedBeneficiary, - approvedHolder: mockGetApprovedHolder, - callStatic: { - transferToNewEscrow: mockCallStaticTransferToNewEscrow, - }, - }); - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); - mockTransferToNewEscrow.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockClear(); - mockedConnectERC721.mockClear(); - mockedTokenFactory.mockClear(); - mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); - mockTransferToNewEscrow.mockClear(); - mockGetApprovedBeneficiary.mockClear(); - mockGetApprovedHolder.mockClear(); - mockCallStaticTransferToNewEscrow.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await endorseTransferOfOwner(endorseTransferOfOwnerParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await endorseTransferOfOwner({ - ...endorseTransferOfOwnerParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and call the following procedures to invoke an endorsement of transfer of owner of a transferable record", async () => { - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await endorseTransferOfOwner({ - ...endorseTransferOfOwnerParams, - key: privateKey, - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnectERC721).toHaveBeenCalledWith(endorseTransferOfOwnerParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(endorseTransferOfOwnerParams.tokenId); - expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockGetApprovedBeneficiary).toHaveBeenCalledTimes(1); - expect(mockGetApprovedHolder).toHaveBeenCalledTimes(1); - expect(mockCallStaticTransferToNewEscrow).toHaveBeenCalledTimes(1); - expect(mockTransferToNewEscrow).toHaveBeenCalledTimes(1); - }); - - it("should throw an error if approved owner or approved holder addresses is the Genesis address", async () => { - mockGetApprovedBeneficiary.mockReturnValue(GENESIS_ADDRESS); - mockGetApprovedHolder.mockReturnValue(GENESIS_ADDRESS); - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await expect( - endorseTransferOfOwner({ - ...endorseTransferOfOwnerParams, - key: privateKey, - }) - ).rejects.toThrow( - `there is no approved owner or holder or the approved owner or holder is equal to the genesis address: ${GENESIS_ADDRESS}` - ); - }); - - it("should throw an error if approved owner or approved holder addresses does not exist", async () => { - mockGetApprovedBeneficiary.mockReturnValue(""); - mockGetApprovedHolder.mockReturnValue(""); - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await expect( - endorseTransferOfOwner({ - ...endorseTransferOfOwnerParams, - key: privateKey, - }) - ).rejects.toThrow( - `there is no approved owner or holder or the approved owner or holder is equal to the genesis address: ${GENESIS_ADDRESS}` - ); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(endorseTransferOfOwner(endorseTransferOfOwnerParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(endorseTransferOfOwner(endorseTransferOfOwnerParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); - }); - }); -}); diff --git a/src/implementations/title-escrow/endorseTransferOfOwner.ts b/src/implementations/title-escrow/endorseTransferOfOwner.ts deleted file mode 100644 index 4e13dc02..00000000 --- a/src/implementations/title-escrow/endorseTransferOfOwner.ts +++ /dev/null @@ -1,56 +0,0 @@ -import signale from "signale"; -import { getLogger } from "../../logger"; -import { getWalletOrSigner } from "../utils/wallet"; -import { connectToTitleEscrow, validateEndorseTransferOwner } from "./helpers"; -import { BaseTitleEscrowCommand as TitleEscrowEndorseTransferOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; - -import { dryRunMode } from "../utils/dryRun"; -import { TransactionReceipt } from "@ethersproject/providers"; - -const { trace } = getLogger("title-escrow:endorseTransferOfOwner"); - -export const endorseTransferOfOwner = async ({ - tokenRegistry: address, - tokenId, - network, - gasPriceScale, - dryRun, - ...rest -}: TitleEscrowEndorseTransferOfOwnerCommand): Promise<{ - transactionReceipt: TransactionReceipt; - approvedOwner: string; - approvedHolder: string; -}> => { - const wallet = await getWalletOrSigner({ network, ...rest }); - if (dryRun) { - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - const approvedBeneficiary = await titleEscrow.approvedBeneficiary(); - const approvedHolder = await titleEscrow.approvedHolder(); - await validateEndorseTransferOwner({ approvedOwner: approvedBeneficiary, approvedHolder }); - await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await titleEscrow.estimateGas.transferToNewEscrow(approvedBeneficiary, approvedHolder), - network, - }); - process.exit(0); - } - const gasPrice = await wallet.provider.getGasPrice(); - signale.await(`Sending transaction to pool`); - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - const approvedBeneficiary = await titleEscrow.approvedBeneficiary(); - const approvedHolder = await titleEscrow.approvedHolder(); - await validateEndorseTransferOwner({ approvedOwner: approvedBeneficiary, approvedHolder }); - await titleEscrow.callStatic.transferToNewEscrow(approvedBeneficiary, approvedHolder); - const transaction = await titleEscrow.transferToNewEscrow(approvedBeneficiary, approvedHolder, { - gasPrice: gasPrice.mul(gasPriceScale), - }); - trace(`Tx hash: ${transaction.hash}`); - trace(`Block Number: ${transaction.blockNumber}`); - signale.await(`Waiting for transaction ${transaction.hash} to be mined`); - const transactionReceipt = await transaction.wait(); - return { - transactionReceipt, - approvedOwner: approvedBeneficiary, - approvedHolder, - }; -}; diff --git a/src/implementations/title-escrow/helpers.ts b/src/implementations/title-escrow/helpers.ts index 9809beff..41593019 100644 --- a/src/implementations/title-escrow/helpers.ts +++ b/src/implementations/title-escrow/helpers.ts @@ -1,4 +1,9 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { + TitleEscrow, + TitleEscrow__factory, + TradeTrustToken, + TradeTrustToken__factory, +} from "@govtechsg/token-registry/contracts"; import { Wallet, constants } from "ethers"; import signale from "signale"; import { ConnectedSigner } from "../utils/wallet"; @@ -9,23 +14,20 @@ interface ConnectToTitleEscrowArgs { wallet: Wallet | ConnectedSigner; } -type TitleEscrowInstanceType = ReturnType; - export const connectToTitleEscrow = async ({ tokenId, address, wallet, -}: ConnectToTitleEscrowArgs): Promise => { - const tokenRegistry = await TradeTrustErc721Factory.connect(address, wallet); +}: ConnectToTitleEscrowArgs): Promise => { + const tokenRegistry: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); const titleEscrowAddress = await tokenRegistry.ownerOf(tokenId); - const titleEscrow = await TitleEscrowFactory.connect(titleEscrowAddress, wallet); - return titleEscrow; + return await TitleEscrow__factory.connect(titleEscrowAddress, wallet); }; interface validateEndorseChangeOwnerArgs { newHolder: string; newOwner: string; - titleEscrow: TitleEscrowInstanceType; + titleEscrow: TitleEscrow; } export const validateEndorseChangeOwner = async ({ newHolder, @@ -41,17 +43,17 @@ export const validateEndorseChangeOwner = async ({ } }; -interface validateNominateChangeOwnerArgs { - newOwner: string; - titleEscrow: TitleEscrowInstanceType; +interface validateNominateBeneficiaryArgs { + beneficiaryNominee: string; + titleEscrow: TitleEscrow; } -export const validateNominateChangeOwner = async ({ - newOwner, +export const validateNominateBeneficiary = async ({ + beneficiaryNominee, titleEscrow, -}: validateNominateChangeOwnerArgs): Promise => { +}: validateNominateBeneficiaryArgs): Promise => { const beneficiary = await titleEscrow.beneficiary(); - if (newOwner === beneficiary) { - const error = "new owner address is the same as the current owner address"; + if (beneficiaryNominee === beneficiary) { + const error = "new beneficiary address is the same as the current beneficiary address"; signale.error(error); throw new Error(error); } diff --git a/src/implementations/title-escrow/nominateBeneficiary.test.ts b/src/implementations/title-escrow/nominateBeneficiary.test.ts new file mode 100644 index 00000000..eb755c91 --- /dev/null +++ b/src/implementations/title-escrow/nominateBeneficiary.test.ts @@ -0,0 +1,95 @@ +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { Wallet } from "ethers"; + +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { nominateBeneficiary } from "./nominateBeneficiary"; + +jest.mock("@govtechsg/token-registry/contracts"); + +const nominateBeneficiaryParams: TitleEscrowNominateBeneficiaryCommand = { + newBeneficiary: "0fosui", + tokenId: "0xzyxw", + tokenRegistry: "0x1234", + network: "goerli", + dryRun: false, +}; + +describe("title-escrow", () => { + describe("nominate change of owner of transferable record", () => { + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; + const mockedOwnerOf = jest.fn(); + const mockNominateBeneficiary = jest.fn(); + const mockedTitleEscrowAddress = "0x2133"; + const mockedBeneficiary = "0xdssfs"; + const mockedHolder = "0xdsfls"; + const mockGetBeneficiary = jest.fn(); + const mockGetHolder = jest.fn(); + const mockCallStaticNominateBeneficiary = jest.fn().mockResolvedValue(undefined); + mockGetBeneficiary.mockResolvedValue(mockedBeneficiary); + mockGetHolder.mockResolvedValue(mockedHolder); + mockedConnectERC721.mockReturnValue({ + ownerOf: mockedOwnerOf, + }); + mockedConnectTokenFactory.mockReturnValue({ + nominate: mockNominateBeneficiary, + beneficiary: mockGetBeneficiary, + holder: mockGetHolder, + callStatic: { + nominate: mockCallStaticNominateBeneficiary, + }, + }); + mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + mockNominateBeneficiary.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); + + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedTradeTrustTokenFactory.mockClear(); + mockedConnectERC721.mockClear(); + mockedTokenFactory.mockClear(); + mockedConnectTokenFactory.mockClear(); + mockedOwnerOf.mockClear(); + mockNominateBeneficiary.mockClear(); + mockGetBeneficiary.mockClear(); + mockGetHolder.mockClear(); + mockCallStaticNominateBeneficiary.mockClear(); + }); + + it("should pass in the correct params and call the following procedures to invoke an nomination of change of owner of a transferable record", async () => { + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await nominateBeneficiary({ + ...nominateBeneficiaryParams, + key: privateKey, + }); + + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${privateKey}`); + expect(mockedConnectERC721).toHaveBeenCalledWith(nominateBeneficiaryParams.tokenRegistry, passedSigner); + expect(mockedOwnerOf).toHaveBeenCalledWith(nominateBeneficiaryParams.tokenId); + expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); + expect(mockCallStaticNominateBeneficiary).toHaveBeenCalledTimes(1); + expect(mockNominateBeneficiary).toHaveBeenCalledTimes(1); + }); + + it("should throw an error if new owner addresses is the same as current owner", async () => { + mockGetBeneficiary.mockReturnValue(nominateBeneficiaryParams.newBeneficiary); + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await expect( + nominateBeneficiary({ + ...nominateBeneficiaryParams, + key: privateKey, + }) + ).rejects.toThrow("new beneficiary address is the same as the current beneficiary address"); + }); + }); +}); diff --git a/src/implementations/title-escrow/nominateBeneficiary.ts b/src/implementations/title-escrow/nominateBeneficiary.ts new file mode 100644 index 00000000..7bddd11b --- /dev/null +++ b/src/implementations/title-escrow/nominateBeneficiary.ts @@ -0,0 +1,39 @@ +import signale from "signale"; +import { getLogger } from "../../logger"; +import { getWalletOrSigner } from "../utils/wallet"; +import { connectToTitleEscrow, validateNominateBeneficiary } from "./helpers"; +import { TitleEscrowNominateBeneficiaryCommand } from "../../commands/title-escrow/title-escrow-command.type"; + +import { dryRunMode } from "../utils/dryRun"; +import { TransactionReceipt } from "@ethersproject/providers"; + +const { trace } = getLogger("title-escrow:nominateChangeOfOwner"); + +export const nominateBeneficiary = async ({ + tokenRegistry: address, + tokenId, + newBeneficiary, + network, + dryRun, + ...rest +}: TitleEscrowNominateBeneficiaryCommand): Promise => { + const wallet = await getWalletOrSigner({ network, ...rest }); + const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + if (dryRun) { + await validateNominateBeneficiary({ beneficiaryNominee: newBeneficiary, titleEscrow }); + await dryRunMode({ + estimatedGas: await titleEscrow.estimateGas.nominate(newBeneficiary), + network, + }); + process.exit(0); + } + + signale.await(`Sending transaction to pool`); + await validateNominateBeneficiary({ beneficiaryNominee: newBeneficiary, titleEscrow }); + await titleEscrow.callStatic.nominate(newBeneficiary); + const transaction = await titleEscrow.nominate(newBeneficiary); + trace(`Tx hash: ${transaction.hash}`); + trace(`Block Number: ${transaction.blockNumber}`); + signale.await(`Waiting for transaction ${transaction.hash} to be mined`); + return transaction.wait(); +}; diff --git a/src/implementations/title-escrow/nominateChangeOfOwner.test.ts b/src/implementations/title-escrow/nominateChangeOfOwner.test.ts deleted file mode 100644 index 6e71f368..00000000 --- a/src/implementations/title-escrow/nominateChangeOfOwner.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; -import { Wallet } from "ethers"; -import { join } from "path"; -import { TitleEscrowNominateChangeOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; -import { nominateChangeOfOwner } from "./nominateChangeOfOwner"; - -jest.mock("@govtechsg/token-registry"); - -const nominateChangeOfOwnerParams: TitleEscrowNominateChangeOfOwnerCommand = { - newOwner: "0fosui", - tokenId: "0xzyxw", - tokenRegistry: "0x1234", - network: "goerli", - gasPriceScale: 1, - dryRun: false, -}; - -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it -describe("title-escrow", () => { - describe("nominate change of owner of transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTokenFactory: jest.Mock = TitleEscrowFactory as any; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore mock static method - const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; - const mockedOwnerOf = jest.fn(); - const mockApproveNewTransferTargets = jest.fn(); - const mockedTitleEscrowAddress = "0x2133"; - const mockedBeneficiary = "0xdssfs"; - const mockedHolder = "0xdsfls"; - const mockGetBeneficiary = jest.fn(); - const mockGetHolder = jest.fn(); - const mockCallStaticApproveNewTransferTargets = jest.fn().mockResolvedValue(undefined); - mockGetBeneficiary.mockResolvedValue(mockedBeneficiary); - mockGetHolder.mockResolvedValue(mockedHolder); - mockedConnectERC721.mockReturnValue({ - ownerOf: mockedOwnerOf, - }); - mockedConnectTokenFactory.mockReturnValue({ - approveNewTransferTargets: mockApproveNewTransferTargets, - beneficiary: mockGetBeneficiary, - holder: mockGetHolder, - callStatic: { - approveNewTransferTargets: mockCallStaticApproveNewTransferTargets, - }, - }); - mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); - mockApproveNewTransferTargets.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockClear(); - mockedConnectERC721.mockClear(); - mockedTokenFactory.mockClear(); - mockedConnectTokenFactory.mockClear(); - mockedOwnerOf.mockClear(); - mockApproveNewTransferTargets.mockClear(); - mockGetBeneficiary.mockClear(); - mockGetHolder.mockClear(); - mockCallStaticApproveNewTransferTargets.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await nominateChangeOfOwner(nominateChangeOfOwnerParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await nominateChangeOfOwner({ - ...nominateChangeOfOwnerParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - - it("should pass in the correct params and call the following procedures to invoke an nomination of change of owner of a transferable record", async () => { - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await nominateChangeOfOwner({ - ...nominateChangeOfOwnerParams, - key: privateKey, - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - - expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnectERC721).toHaveBeenCalledWith(nominateChangeOfOwnerParams.tokenRegistry, passedSigner); - expect(mockedOwnerOf).toHaveBeenCalledWith(nominateChangeOfOwnerParams.tokenId); - expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockGetHolder).toHaveBeenCalledTimes(1); - expect(mockGetBeneficiary).toHaveBeenCalledTimes(1); - expect(mockCallStaticApproveNewTransferTargets).toHaveBeenCalledTimes(1); - expect(mockApproveNewTransferTargets).toHaveBeenCalledTimes(1); - }); - - it("should throw an error if new owner addresses is the same as current owner", async () => { - mockGetBeneficiary.mockReturnValue(nominateChangeOfOwnerParams.newOwner); - const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; - await expect( - nominateChangeOfOwner({ - ...nominateChangeOfOwnerParams, - key: privateKey, - }) - ).rejects.toThrow("new owner address is the same as the current owner address"); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(nominateChangeOfOwner(nominateChangeOfOwnerParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(nominateChangeOfOwner(nominateChangeOfOwnerParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); - }); - }); -}); diff --git a/src/implementations/title-escrow/nominateChangeOfOwner.ts b/src/implementations/title-escrow/nominateChangeOfOwner.ts deleted file mode 100644 index f470ac8f..00000000 --- a/src/implementations/title-escrow/nominateChangeOfOwner.ts +++ /dev/null @@ -1,46 +0,0 @@ -import signale from "signale"; -import { getLogger } from "../../logger"; -import { getWalletOrSigner } from "../utils/wallet"; -import { connectToTitleEscrow, validateNominateChangeOwner } from "./helpers"; -import { TitleEscrowNominateChangeOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; - -import { dryRunMode } from "../utils/dryRun"; -import { TransactionReceipt } from "@ethersproject/providers"; - -const { trace } = getLogger("title-escrow:nominateChangeOfOwner"); - -export const nominateChangeOfOwner = async ({ - tokenRegistry: address, - tokenId, - newOwner, - network, - gasPriceScale, - dryRun, - ...rest -}: TitleEscrowNominateChangeOfOwnerCommand): Promise => { - const wallet = await getWalletOrSigner({ network, ...rest }); - if (dryRun) { - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - const holder = await titleEscrow.holder(); - await validateNominateChangeOwner({ newOwner, titleEscrow }); - await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await titleEscrow.estimateGas.approveNewTransferTargets(newOwner, holder), - network, - }); - process.exit(0); - } - const gasPrice = await wallet.provider.getGasPrice(); - signale.await(`Sending transaction to pool`); - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - const holder = await titleEscrow.holder(); - await validateNominateChangeOwner({ newOwner, titleEscrow }); - await titleEscrow.callStatic.approveNewTransferTargets(newOwner, holder); - const transaction = await titleEscrow.approveNewTransferTargets(newOwner, holder, { - gasPrice: gasPrice.mul(gasPriceScale), - }); - trace(`Tx hash: ${transaction.hash}`); - trace(`Block Number: ${transaction.blockNumber}`); - signale.await(`Waiting for transaction ${transaction.hash} to be mined`); - return transaction.wait(); -}; diff --git a/src/implementations/title-escrow/rejectSurrendered.test.ts b/src/implementations/title-escrow/rejectSurrendered.test.ts index cc208787..e8ab72b1 100644 --- a/src/implementations/title-escrow/rejectSurrendered.test.ts +++ b/src/implementations/title-escrow/rejectSurrendered.test.ts @@ -1,38 +1,35 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; -import { join } from "path"; + import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { rejectSurrendered } from "./rejectSurrendered"; -jest.mock("@govtechsg/token-registry"); +jest.mock("@govtechsg/token-registry/contracts"); const rejectSurrenderedDocumentParams: TitleEscrowSurrenderDocumentCommand = { tokenRegistry: "0x1122", tokenId: "0x12345", network: "goerli", - gasPriceScale: 1, dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it describe("title-escrow", () => { describe("rejects surrendered transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method - const mockedConnectERC721Factory: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTitleEscrowFactory: jest.Mock = TitleEscrowFactory as any; + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockedTitleEscrowFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTitleEscrowFactory: jest.Mock = mockedTitleEscrowFactory.connect; const mockedBeneficiary = jest.fn(); const mockedHolder = jest.fn(); - const mockSendToNewTitleEscrow = jest.fn(); + const mockRestoreTitle = jest.fn(); const mockTransferEvent = jest.fn(); const mockQueryFilter = jest.fn(); - const mockCallStaticSendToNewTitleEscrow = jest.fn().mockResolvedValue(undefined); + const mockCallStaticRestoreTitle = jest.fn().mockResolvedValue(undefined); const mockedLastTitleEscrowAddress = "0xMockedLastTitleEscrowAddress"; const mockedLastBeneficiary = "0xMockedLastBeneficiaryAddress"; @@ -40,14 +37,14 @@ describe("title-escrow", () => { beforeEach(() => { delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockReset(); - mockedConnectERC721Factory.mockReset(); + mockedTradeTrustTokenFactory.mockReset(); + mockedConnectERC721.mockReset(); mockedTitleEscrowFactory.mockReset(); mockedConnectTitleEscrowFactory.mockReset(); mockedBeneficiary.mockReturnValue(mockedLastBeneficiary); mockedHolder.mockReturnValue(mockedLastHolder); - mockSendToNewTitleEscrow.mockReturnValue({ + mockRestoreTitle.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); @@ -65,40 +62,20 @@ describe("title-escrow", () => { beneficiary: mockedBeneficiary, holder: mockedHolder, }); - mockedConnectERC721Factory.mockReturnValue({ - sendToNewTitleEscrow: mockSendToNewTitleEscrow, + mockedConnectERC721.mockReturnValue({ + restore: mockRestoreTitle, filters: { Transfer: mockTransferEvent }, queryFilter: mockQueryFilter, callStatic: { - sendToNewTitleEscrow: mockCallStaticSendToNewTitleEscrow, + restore: mockCallStaticRestoreTitle, }, }); - mockedBeneficiary.mockClear(); mockedHolder.mockClear(); - mockSendToNewTitleEscrow.mockClear(); + mockRestoreTitle.mockClear(); mockTransferEvent.mockClear(); mockQueryFilter.mockClear(); - mockCallStaticSendToNewTitleEscrow.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await rejectSurrendered(rejectSurrenderedDocumentParams); - - const passedSigner: Wallet = mockedConnectERC721Factory.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); - }); - - it("should take in the key from key file", async () => { - await rejectSurrendered({ - ...rejectSurrenderedDocumentParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721Factory.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); + mockCallStaticRestoreTitle.mockClear(); }); it("should pass in the correct params and successfully rejects a surrendered transferable record", async () => { @@ -108,32 +85,12 @@ describe("title-escrow", () => { key: privateKey, }); - const passedSigner: Wallet = mockedConnectERC721Factory.mock.calls[0][1]; + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnectERC721Factory).toHaveBeenCalledWith( - rejectSurrenderedDocumentParams.tokenRegistry, - passedSigner - ); - expect(mockedConnectTitleEscrowFactory).toHaveBeenCalledWith(mockedLastTitleEscrowAddress, passedSigner); - expect(mockedBeneficiary).toHaveBeenCalledTimes(1); - expect(mockedHolder).toHaveBeenCalledTimes(1); - expect(mockCallStaticSendToNewTitleEscrow).toHaveBeenCalledTimes(1); - expect(mockSendToNewTitleEscrow).toHaveBeenCalledTimes(1); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721Factory.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(rejectSurrendered(rejectSurrenderedDocumentParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(rejectSurrendered(rejectSurrenderedDocumentParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); + expect(mockedConnectERC721).toHaveBeenCalledWith(rejectSurrenderedDocumentParams.tokenRegistry, passedSigner); + expect(mockCallStaticRestoreTitle).toHaveBeenCalledTimes(1); + expect(mockRestoreTitle).toHaveBeenCalledWith(rejectSurrenderedDocumentParams.tokenId); }); }); }); diff --git a/src/implementations/title-escrow/rejectSurrendered.ts b/src/implementations/title-escrow/rejectSurrendered.ts index 267f9171..098fb0ef 100644 --- a/src/implementations/title-escrow/rejectSurrendered.ts +++ b/src/implementations/title-escrow/rejectSurrendered.ts @@ -1,64 +1,33 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TradeTrustToken, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import signale from "signale"; import { getLogger } from "../../logger"; -import { ConnectedSigner, getWalletOrSigner } from "../utils/wallet"; +import { getWalletOrSigner } from "../utils/wallet"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; -import { TradeTrustERC721 } from "@govtechsg/token-registry/dist/ts/contracts"; -import { Wallet } from "ethers"; const { trace } = getLogger("title-escrow:acceptSurrendered"); -const retrieveLastBeneficiaryAndHolder = async ( - tokenRegistryInstance: TradeTrustERC721, - tokenId: string, - wallet: Wallet | ConnectedSigner -): Promise<{ lastBeneficiary: string; lastHolder: string }> => { - // Fetch transfer logs from token registry - const transferLogFilter = tokenRegistryInstance.filters.Transfer(null, null, tokenId); - const logs = await tokenRegistryInstance.queryFilter(transferLogFilter, 0); - const lastTitleEscrowAddress = logs[logs.length - 1].args?.[0]; - const lastTitleEscrowInstance = await TitleEscrowFactory.connect(lastTitleEscrowAddress, wallet); - const lastBeneficiary = await lastTitleEscrowInstance.beneficiary(); - const lastHolder = await lastTitleEscrowInstance.holder(); - return { lastBeneficiary, lastHolder }; -}; - export const rejectSurrendered = async ({ tokenRegistry: address, tokenId, network, - gasPriceScale, dryRun, ...rest }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); - const tokenRegistryInstance = await TradeTrustErc721Factory.connect(address, wallet); + const tokenRegistryInstance: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); if (dryRun) { - const { lastBeneficiary, lastHolder } = await retrieveLastBeneficiaryAndHolder( - tokenRegistryInstance, - tokenId, - wallet - ); await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await tokenRegistryInstance.estimateGas.sendToNewTitleEscrow(lastBeneficiary, lastHolder, tokenId), + estimatedGas: await tokenRegistryInstance.estimateGas.restore(tokenId), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); signale.await(`Sending transaction to pool`); - const { lastBeneficiary, lastHolder } = await retrieveLastBeneficiaryAndHolder( - tokenRegistryInstance, - tokenId, - wallet - ); - await tokenRegistryInstance.callStatic.sendToNewTitleEscrow(lastBeneficiary, lastHolder, tokenId); - const transaction = await tokenRegistryInstance.sendToNewTitleEscrow(lastBeneficiary, lastHolder, tokenId, { - gasPrice: gasPrice.mul(gasPriceScale), - }); + await tokenRegistryInstance.callStatic.restore(tokenId); + const transaction = await tokenRegistryInstance.restore(tokenId); + trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/surrenderDocument.test.ts b/src/implementations/title-escrow/surrenderDocument.test.ts index 58f9323f..bc01d275 100644 --- a/src/implementations/title-escrow/surrenderDocument.test.ts +++ b/src/implementations/title-escrow/surrenderDocument.test.ts @@ -1,45 +1,42 @@ -import { TitleEscrowFactory, TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; -import { join } from "path"; + import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { surrenderDocument } from "./surrenderDocument"; -jest.mock("@govtechsg/token-registry"); +jest.mock("@govtechsg/token-registry/contracts"); const surrenderDocumentParams: TitleEscrowSurrenderDocumentCommand = { tokenRegistry: "0x1122", tokenId: "0x12345", network: "goerli", - gasPriceScale: 1, dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it describe("title-escrow", () => { describe("surrender transferable record", () => { - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method - const mockedConnectERC721: jest.Mock = mockedTradeTrustErc721Factory.connect; - const mockedTitleEscrowFactory: jest.Mock = TitleEscrowFactory as any; + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockedTitleEscrowFactory: jest.Mock = TitleEscrow__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method const mockedConnectTitleEscrowFactory: jest.Mock = mockedTitleEscrowFactory.connect; const mockedOwnerOf = jest.fn(); - const mockTransferTo = jest.fn(); - const mockCallStaticTransferTo = jest.fn().mockResolvedValue(undefined); + const mockSurrender = jest.fn(); + const mockCallStaticSurrender = jest.fn().mockResolvedValue(undefined); const mockedTitleEscrowAddress = "0x2133"; beforeEach(() => { delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockReset(); + mockedTradeTrustTokenFactory.mockReset(); mockedConnectERC721.mockReset(); mockedTitleEscrowFactory.mockReset(); mockedConnectTitleEscrowFactory.mockReset(); mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); - mockTransferTo.mockReturnValue({ + mockSurrender.mockReturnValue({ hash: "hash", wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); @@ -47,36 +44,16 @@ describe("title-escrow", () => { ownerOf: mockedOwnerOf, }); mockedConnectTitleEscrowFactory.mockReturnValue({ - transferTo: mockTransferTo, + surrender: mockSurrender, callStatic: { - transferTo: mockCallStaticTransferTo, + surrender: mockCallStaticSurrender, }, }); mockedOwnerOf.mockClear(); - mockTransferTo.mockClear(); - mockCallStaticTransferTo.mockClear(); - }); - - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await surrenderDocument(surrenderDocumentParams); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + mockSurrender.mockClear(); + mockCallStaticSurrender.mockClear(); }); - - it("should take in the key from key file", async () => { - await surrenderDocument({ - ...surrenderDocumentParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); - - const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); - }); - it("should pass in the correct params and successfully surrender a transferable record", async () => { const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; await surrenderDocument({ @@ -90,22 +67,8 @@ describe("title-escrow", () => { expect(mockedConnectERC721).toHaveBeenCalledWith(surrenderDocumentParams.tokenRegistry, passedSigner); expect(mockedOwnerOf).toHaveBeenCalledWith(surrenderDocumentParams.tokenId); expect(mockedConnectTitleEscrowFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); - expect(mockCallStaticTransferTo).toHaveBeenCalledTimes(1); - expect(mockTransferTo).toHaveBeenCalledTimes(1); - }); - - it("should allow errors to bubble up", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnectERC721.mockImplementation(() => { - throw new Error("An Error"); - }); - await expect(surrenderDocument(surrenderDocumentParams)).rejects.toThrow("An Error"); - }); - - it("should throw when keys are not found anywhere", async () => { - await expect(surrenderDocument(surrenderDocumentParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path" - ); + expect(mockCallStaticSurrender).toHaveBeenCalledTimes(1); + expect(mockSurrender).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/implementations/title-escrow/surrenderDocument.ts b/src/implementations/title-escrow/surrenderDocument.ts index f64e41fa..00d62a00 100644 --- a/src/implementations/title-escrow/surrenderDocument.ts +++ b/src/implementations/title-escrow/surrenderDocument.ts @@ -3,7 +3,6 @@ import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { connectToTitleEscrow } from "./helpers"; import { BaseTitleEscrowCommand as TitleEscrowSurrenderDocumentCommand } from "../../commands/title-escrow/title-escrow-command.type"; - import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; @@ -13,25 +12,23 @@ export const surrenderDocument = async ({ tokenRegistry: address, tokenId, network, - gasPriceScale, dryRun, ...rest }: TitleEscrowSurrenderDocumentCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); + const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + if (dryRun) { - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await titleEscrow.estimateGas.transferTo(address), + estimatedGas: await titleEscrow.estimateGas.surrender(), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - await titleEscrow.callStatic.transferTo(address); - const transaction = await titleEscrow.transferTo(address, { gasPrice: gasPrice.mul(gasPriceScale) }); + await titleEscrow.callStatic.surrender(); + const transaction = await titleEscrow.surrender(); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/transferHolder.test.ts b/src/implementations/title-escrow/transferHolder.test.ts new file mode 100644 index 00000000..7b6ff058 --- /dev/null +++ b/src/implementations/title-escrow/transferHolder.test.ts @@ -0,0 +1,75 @@ +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { Wallet } from "ethers"; + +import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { transferHolder } from "./transferHolder"; + +jest.mock("@govtechsg/token-registry/contracts"); + +const transferHolderParams: TitleEscrowTransferHolderCommand = { + newHolder: "0xabcd", + tokenId: "0xzyxw", + tokenRegistry: "0x1234", + network: "goerli", + dryRun: false, +}; + +describe("title-escrow", () => { + describe("change holder of transferable record", () => { + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + + const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; + const mockedOwnerOf = jest.fn(); + const mockTransferHolder = jest.fn(); + const mockCallStaticTransferHolder = jest.fn().mockResolvedValue(undefined); + const mockedTitleEscrowAddress = "0x2133"; + mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + mockTransferHolder.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); + mockedConnectERC721.mockReturnValue({ + ownerOf: mockedOwnerOf, + }); + mockedConnectTokenFactory.mockReturnValue({ + transferHolder: mockTransferHolder, + callStatic: { + transferHolder: mockCallStaticTransferHolder, + }, + }); + + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedTradeTrustTokenFactory.mockClear(); + mockedConnectERC721.mockClear(); + mockedTokenFactory.mockClear(); + mockedConnectTokenFactory.mockClear(); + mockedOwnerOf.mockClear(); + mockTransferHolder.mockClear(); + mockCallStaticTransferHolder.mockClear(); + }); + + it("should pass in the correct params and call the following procedures to invoke a change in holder of a transferable record", async () => { + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await transferHolder({ + ...transferHolderParams, + key: privateKey, + }); + + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${privateKey}`); + expect(mockedConnectERC721).toHaveBeenCalledWith(transferHolderParams.tokenRegistry, passedSigner); + expect(mockedOwnerOf).toHaveBeenCalledWith(transferHolderParams.tokenId); + expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); + expect(mockCallStaticTransferHolder).toHaveBeenCalledTimes(1); + expect(mockTransferHolder).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/implementations/title-escrow/changeHolder.ts b/src/implementations/title-escrow/transferHolder.ts similarity index 54% rename from src/implementations/title-escrow/changeHolder.ts rename to src/implementations/title-escrow/transferHolder.ts index 29b57650..aaa21687 100644 --- a/src/implementations/title-escrow/changeHolder.ts +++ b/src/implementations/title-escrow/transferHolder.ts @@ -2,37 +2,34 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { connectToTitleEscrow } from "./helpers"; -import { TitleEscrowChangeHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { TitleEscrowTransferHolderCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; -const { trace } = getLogger("title-escrow:changeHolder"); +const { trace } = getLogger("title-escrow:transferHolder"); -export const changeHolderOfTitleEscrow = async ({ +export const transferHolder = async ({ tokenRegistry: address, - to, + newHolder: to, tokenId, network, - gasPriceScale, dryRun, ...rest -}: TitleEscrowChangeHolderCommand): Promise => { +}: TitleEscrowTransferHolderCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); + const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); if (dryRun) { - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await titleEscrow.estimateGas.changeHolder(to), + estimatedGas: await titleEscrow.estimateGas.transferHolder(to), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - await titleEscrow.callStatic.changeHolder(to); - const transaction = await titleEscrow.changeHolder(to, { gasPrice: gasPrice.mul(gasPriceScale) }); + await titleEscrow.callStatic.transferHolder(to); + const transaction = await titleEscrow.transferHolder(to); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/title-escrow/transferOwners.test.ts b/src/implementations/title-escrow/transferOwners.test.ts new file mode 100644 index 00000000..9f411473 --- /dev/null +++ b/src/implementations/title-escrow/transferOwners.test.ts @@ -0,0 +1,100 @@ +import { TitleEscrow__factory, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; +import { Wallet } from "ethers"; + +import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { transferOwners } from "./transferOwners"; + +jest.mock("@govtechsg/token-registry/contracts"); + +const endorseChangeOwnersParams: TitleEscrowEndorseTransferOfOwnersCommand = { + newHolder: "0xabcd", + newOwner: "0fosui", + tokenId: "0xzyxw", + tokenRegistry: "0x1234", + network: "goerli", + dryRun: false, +}; + +describe("title-escrow", () => { + describe("endorse change of owners of transferable record", () => { + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; + const mockedTokenFactory: jest.Mock = TitleEscrow__factory as any; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore mock static method + + const mockedConnectTokenFactory: jest.Mock = mockedTokenFactory.connect; + const mockedOwnerOf = jest.fn(); + const mockTransferOwners = jest.fn(); + const mockCallStaticTransferOwners = jest.fn().mockResolvedValue(undefined); + const mockedTitleEscrowAddress = "0x2133"; + const mockedBeneficiary = "0xdssfs"; + const mockedHolder = "0xdsfls"; + const mockGetBeneficiary = jest.fn(); + const mockGetHolder = jest.fn(); + mockGetBeneficiary.mockReturnValue(mockedBeneficiary); + mockGetHolder.mockReturnValue(mockedHolder); + mockedConnectERC721.mockReturnValue({ + ownerOf: mockedOwnerOf, + }); + mockedConnectTokenFactory.mockReturnValue({ + transferOwners: mockTransferOwners, + beneficiary: mockGetBeneficiary, + holder: mockGetHolder, + callStatic: { + transferOwners: mockCallStaticTransferOwners, + }, + }); + mockedOwnerOf.mockReturnValue(mockedTitleEscrowAddress); + mockTransferOwners.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), + }); + + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedTradeTrustTokenFactory.mockClear(); + mockedConnectERC721.mockClear(); + mockedTokenFactory.mockClear(); + mockedConnectTokenFactory.mockClear(); + mockedOwnerOf.mockClear(); + mockTransferOwners.mockClear(); + mockGetBeneficiary.mockClear(); + mockGetHolder.mockClear(); + mockCallStaticTransferOwners.mockClear(); + }); + + it("should pass in the correct params and call the following procedures to invoke an endorsement of change of owner of a transferable record", async () => { + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await transferOwners({ + ...endorseChangeOwnersParams, + key: privateKey, + }); + + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; + + expect(passedSigner.privateKey).toBe(`0x${privateKey}`); + expect(mockedConnectERC721).toHaveBeenCalledWith(endorseChangeOwnersParams.tokenRegistry, passedSigner); + expect(mockedOwnerOf).toHaveBeenCalledWith(endorseChangeOwnersParams.tokenId); + expect(mockedConnectTokenFactory).toHaveBeenCalledWith(mockedTitleEscrowAddress, passedSigner); + expect(mockGetBeneficiary).toHaveBeenCalledTimes(1); + expect(mockGetHolder).toHaveBeenCalledTimes(1); + expect(mockCallStaticTransferOwners).toHaveBeenCalledTimes(1); + expect(mockTransferOwners).toHaveBeenCalledTimes(1); + }); + + it("should throw an error if new owner and new holder addresses are the same as current owner and holder addressses", async () => { + mockGetBeneficiary.mockReturnValue(endorseChangeOwnersParams.newOwner); + mockGetHolder.mockReturnValue(endorseChangeOwnersParams.newHolder); + const privateKey = "0000000000000000000000000000000000000000000000000000000000000001"; + await expect( + transferOwners({ + ...endorseChangeOwnersParams, + key: privateKey, + }) + ).rejects.toThrow("new owner and new holder addresses are the same as the current owner and holder addresses"); + }); + }); +}); diff --git a/src/implementations/title-escrow/endorseChangeOfOwner.ts b/src/implementations/title-escrow/transferOwners.ts similarity index 56% rename from src/implementations/title-escrow/endorseChangeOfOwner.ts rename to src/implementations/title-escrow/transferOwners.ts index 2267c5c0..0d9286b0 100644 --- a/src/implementations/title-escrow/endorseChangeOfOwner.ts +++ b/src/implementations/title-escrow/transferOwners.ts @@ -2,42 +2,36 @@ import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; import { connectToTitleEscrow, validateEndorseChangeOwner } from "./helpers"; -import { TitleEscrowEndorseChangeOfOwnerCommand } from "../../commands/title-escrow/title-escrow-command.type"; +import { TitleEscrowEndorseTransferOfOwnersCommand } from "../../commands/title-escrow/title-escrow-command.type"; import { dryRunMode } from "../utils/dryRun"; import { TransactionReceipt } from "@ethersproject/providers"; const { trace } = getLogger("title-escrow:endorseChangeOfOwner"); -export const endorseChangeOfOwner = async ({ +export const transferOwners = async ({ tokenRegistry: address, tokenId, newHolder, newOwner, network, - gasPriceScale, dryRun, ...rest -}: TitleEscrowEndorseChangeOfOwnerCommand): Promise => { +}: TitleEscrowEndorseTransferOfOwnersCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); + const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); + await validateEndorseChangeOwner({ newHolder, newOwner, titleEscrow }); if (dryRun) { - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - await validateEndorseChangeOwner({ newHolder, newOwner, titleEscrow }); await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await titleEscrow.estimateGas.transferToNewEscrow(newOwner, newHolder), + estimatedGas: await titleEscrow.estimateGas.transferOwners(newOwner, newHolder), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - const titleEscrow = await connectToTitleEscrow({ tokenId, address, wallet }); - await validateEndorseChangeOwner({ newHolder, newOwner, titleEscrow }); - await titleEscrow.callStatic.transferToNewEscrow(newOwner, newHolder); - const transaction = await titleEscrow.transferToNewEscrow(newOwner, newHolder, { - gasPrice: gasPrice.mul(gasPriceScale), - }); + await titleEscrow.callStatic.transferOwners(newOwner, newHolder); + const transaction = await titleEscrow.transferOwners(newOwner, newHolder); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/token-registry/issue.test.ts b/src/implementations/token-registry/issue.test.ts index 6b9a220e..b40342fe 100644 --- a/src/implementations/token-registry/issue.test.ts +++ b/src/implementations/token-registry/issue.test.ts @@ -1,66 +1,49 @@ -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import { Wallet } from "ethers"; -import { join } from "path"; + import { TokenRegistryIssueCommand } from "../../commands/token-registry/token-registry-command.type"; import { addAddressPrefix } from "../../utils"; import { issueToTokenRegistry } from "./issue"; -jest.mock("@govtechsg/token-registry"); +jest.mock("@govtechsg/token-registry/contracts"); const deployParams: TokenRegistryIssueCommand = { - to: "0xabcd", + beneficiary: "0xabcd", + holder: "0xabce", tokenId: "0xzyxw", address: "0x1234", network: "goerli", - gasPriceScale: 1, dryRun: false, }; -// TODO the following test is very fragile and might break on every interface change of TradeTrustErc721Factory -// ideally must setup ganache, and run the function over it describe("token-registry", () => { describe("issue", () => { jest.setTimeout(30000); - const mockedTradeTrustErc721Factory: jest.Mock = TradeTrustErc721Factory as any; + const mockedTradeTrustTokenFactory: jest.Mock = TradeTrustToken__factory as any; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore mock static method - const mockedConnect: jest.Mock = mockedTradeTrustErc721Factory.connect; + const mockedConnectERC721: jest.Mock = mockedTradeTrustTokenFactory.connect; const mockedIssue = jest.fn(); const mockCallStaticSafeMint = jest.fn().mockResolvedValue(undefined); - beforeEach(() => { - delete process.env.OA_PRIVATE_KEY; - mockedTradeTrustErc721Factory.mockReset(); - mockedConnect.mockReset(); - mockCallStaticSafeMint.mockClear(); - mockedConnect.mockReturnValue({ - "safeMint(address,uint256)": mockedIssue, - callStatic: { - "safeMint(address,uint256)": mockCallStaticSafeMint, - }, - }); - mockedIssue.mockReturnValue({ - hash: "hash", - wait: () => Promise.resolve({ transactionHash: "transactionHash" }), - }); - }); - it("should take in the key from environment variable", async () => { - process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - - await issueToTokenRegistry(deployParams); - - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x${process.env.OA_PRIVATE_KEY}`); + mockedIssue.mockReturnValue({ + hash: "hash", + wait: () => Promise.resolve({ transactionHash: "transactionHash" }), }); - it("should take in the key from key file", async () => { - await issueToTokenRegistry({ - ...deployParams, - keyFile: join(__dirname, "..", "..", "..", "examples", "sample-key"), - }); + const mockTtErc721Contract = { + mint: mockedIssue, + callStatic: { + mint: mockCallStaticSafeMint, + }, + }; - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - expect(passedSigner.privateKey).toBe(`0x0000000000000000000000000000000000000000000000000000000000000003`); + beforeEach(() => { + delete process.env.OA_PRIVATE_KEY; + mockedTradeTrustTokenFactory.mockClear(); + mockCallStaticSafeMint.mockClear(); + mockedConnectERC721.mockReset(); + mockedConnectERC721.mockResolvedValue(mockTtErc721Contract); }); it("should pass in the correct params and return the deployed instance", async () => { @@ -70,12 +53,12 @@ describe("token-registry", () => { key: privateKey, }); - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.to); - expect(mockedIssue.mock.calls[0][1]).toEqual(deployParams.tokenId); + expect(mockedConnectERC721.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.beneficiary); + expect(mockedIssue.mock.calls[0][1]).toEqual(deployParams.holder); + expect(mockedIssue.mock.calls[0][2]).toEqual(deployParams.tokenId); expect(mockCallStaticSafeMint).toHaveBeenCalledTimes(1); expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); }); @@ -88,28 +71,28 @@ describe("token-registry", () => { tokenId: addAddressPrefix("zyxw"), }); - const passedSigner: Wallet = mockedConnect.mock.calls[0][1]; - + const passedSigner: Wallet = mockedConnectERC721.mock.calls[0][1]; expect(passedSigner.privateKey).toBe(`0x${privateKey}`); - expect(mockedConnect.mock.calls[0][0]).toEqual(deployParams.address); - expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.to); - expect(mockedIssue.mock.calls[0][1]).toEqual(deployParams.tokenId); + expect(mockedConnectERC721.mock.calls[0][0]).toEqual(deployParams.address); + expect(mockedIssue.mock.calls[0][0]).toEqual(deployParams.beneficiary); + expect(mockedIssue.mock.calls[0][1]).toEqual(deployParams.holder); + expect(mockedIssue.mock.calls[0][2]).toEqual(deployParams.tokenId); expect(mockCallStaticSafeMint).toHaveBeenCalledTimes(1); expect(instance).toStrictEqual({ transactionHash: "transactionHash" }); }); + it("should throw when keys are not found anywhere", async () => { + await expect(issueToTokenRegistry(deployParams)).rejects.toThrow( + "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" + ); + }); + it("should allow errors to bubble up", async () => { process.env.OA_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000002"; - mockedConnect.mockImplementation(() => { + mockedConnectERC721.mockImplementation(() => { throw new Error("An Error"); }); await expect(issueToTokenRegistry(deployParams)).rejects.toThrow("An Error"); }); - - it("should throw when keys are not found anywhere", async () => { - await expect(issueToTokenRegistry(deployParams)).rejects.toThrow( - "No private key found in OA_PRIVATE_KEY, key, key-file, please supply at least one or supply an encrypted wallet path, or provide aws kms signer information" - ); - }); }); }); diff --git a/src/implementations/token-registry/issue.ts b/src/implementations/token-registry/issue.ts index aef23b3e..1b845468 100644 --- a/src/implementations/token-registry/issue.ts +++ b/src/implementations/token-registry/issue.ts @@ -1,4 +1,4 @@ -import { TradeTrustErc721Factory } from "@govtechsg/token-registry"; +import { TradeTrustToken, TradeTrustToken__factory } from "@govtechsg/token-registry/contracts"; import signale from "signale"; import { getLogger } from "../../logger"; import { getWalletOrSigner } from "../utils/wallet"; @@ -10,30 +10,27 @@ const { trace } = getLogger("token-registry:issue"); export const issueToTokenRegistry = async ({ address, - to, + beneficiary, + holder, tokenId, network, - gasPriceScale, dryRun, ...rest }: TokenRegistryIssueCommand): Promise => { const wallet = await getWalletOrSigner({ network, ...rest }); + const tokenRegistry: TradeTrustToken = await TradeTrustToken__factory.connect(address, wallet); + if (dryRun) { - const tokenRegistry = await TradeTrustErc721Factory.connect(address, wallet); await dryRunMode({ - gasPriceScale: gasPriceScale, - estimatedGas: await tokenRegistry.estimateGas["safeMint(address,uint256)"](to, tokenId), + estimatedGas: await tokenRegistry.estimateGas.mint(beneficiary, holder, tokenId), network, }); process.exit(0); } - const gasPrice = await wallet.provider.getGasPrice(); + signale.await(`Sending transaction to pool`); - const erc721 = await TradeTrustErc721Factory.connect(address, wallet); - await erc721.callStatic["safeMint(address,uint256)"](to, tokenId); - // must invoke the function manually, the lib doesn't handle overload functions - // https://github.com/ethereum-ts/TypeChain/issues/150 - const transaction = await erc721["safeMint(address,uint256)"](to, tokenId, { gasPrice: gasPrice.mul(gasPriceScale) }); + await tokenRegistry.callStatic.mint(beneficiary, holder, tokenId); + const transaction = await tokenRegistry.mint(beneficiary, holder, tokenId); trace(`Tx hash: ${transaction.hash}`); trace(`Block Number: ${transaction.blockNumber}`); signale.await(`Waiting for transaction ${transaction.hash} to be mined`); diff --git a/src/implementations/utils/dryRun.ts b/src/implementations/utils/dryRun.ts index b367a24d..ee342fa7 100644 --- a/src/implementations/utils/dryRun.ts +++ b/src/implementations/utils/dryRun.ts @@ -5,12 +5,10 @@ import { BigNumber } from "ethers"; import { TransactionRequest } from "@ethersproject/providers"; export const dryRunMode = async ({ - gasPriceScale, transaction, estimatedGas, network, }: { - gasPriceScale: number; network: string; transaction?: TransactionRequest; estimatedGas?: BigNumber; @@ -61,10 +59,7 @@ For each mode the following information will be shown: - ${highlight( "tx price (eth)" )}: the price of the transaction in ethereum. This is the amount payed for the transaction (outside of dry-run). You can directly use that amount to convert it to your currency using any currency converter supporting ethereum. -- ${highlight( - "gas price scale" - )}: this is an estimation of the value you must set for the '--gas-price-scale' parameter. Keep in mind that two successive runs will likely produce 2 different values as the gas price is fluctuating. - + Get more information about gas: https://ethereum.stackexchange.com/questions/3/what-is-meant-by-the-term-gas\n\n` ); @@ -74,14 +69,13 @@ Get more information about gas: https://ethereum.stackexchange.com/questions/3/w utils.formatEther(_estimatedGas.mul(gasPrice)) )} eth based on the selected gas price` ); - const scaledGasPrice = utils.parseUnits(Math.round(gasPriceAsGwei * gasPriceScale).toString(), "gwei"); + const scaledGasPrice = utils.parseUnits(Math.round(gasPriceAsGwei).toString(), "gwei"); console.table({ current: { time: "N/A", "gas price (gwei)": Number(formatGwei(scaledGasPrice)), "tx price (gwei)": formatGwei(_estimatedGas.mul(scaledGasPrice)), "tx price (eth)": utils.formatEther(_estimatedGas.mul(scaledGasPrice)), - "gas price scale": Number(gasPriceScale), }, fastest: { time: "< 30 s", From 25044990a60c8bcb73f6afff4150beb02324c570 Mon Sep 17 00:00:00 2001 From: Puay Hiang <68947167+puayhiang@users.noreply.github.com> Date: Wed, 15 Mar 2023 15:02:13 +0800 Subject: [PATCH 20/20] feat(token-registry): Token Registry V4 BREAKING CHANGE: Token Registry V4