diff --git a/README.md b/README.md index d1c7795..6b0570c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,18 @@ We also hope to help you understand the main concepts and best practices of Cade `AssetHandover` is a dapp where account `holders` can grant a `recipient` the ability to `withdraw` specific `tokens` that they own (both `FungibleToken` and `NonFungibleToken`), at a future release date. Each account can only declare one recipient, as this removes the complexity of handling race conditions upon withdrawals. However, an account can be the recipient of multiple handovers. The account holder can specify which fungible tokens will be handed over and a maximum amount for each token. It is also possible to specify which non-fungible tokens will be handed over and a specific list of NFT IDs from each `NFT` Collection. The above tokens are not locked for the account holder, meaning that they can still be utilized/transferred. The recipient (or any other account) can attempt to withdraw them, at any given time, however, this will only be successful after the release date has passed, and only for the authorized recipient. One real-world scenario would be to create a digital `"will"` for one's account, or to simply add another account as a backup, in case the account holder loses access to his/her account or is no longer able to interact with it. +## Project Overview + +Below is a description of the Dapp's components. + +### 1. Web Server | [asset-handover/api](https://github.com/Build-Squad/asset-handover/tree/master/api) + +The API demonstrates how to send transactions and queries to the Flow blockchain. By using the API you would be able to interact with the 3 main smart contracts of [Asset-Handover](https://github.com/Build-Squad/asset-handover/blob/master/cadence/contracts/AssetHandover.cdc), [BlpToken](https://github.com/Build-Squad/asset-handover/blob/master/cadence/contracts/tokens/BlpToken.cdc) and [Domains](https://github.com/Build-Squad/asset-handover/blob/master/cadence/contracts/nfts/Domains.cdc). + +### 2. Cadence Code | [asset-handover/cadence](https://github.com/onflow/asset-handover/tree/master/cadence) + +[Cadence](https://docs.onflow.org/cadence) This folder contains all of the blockchain logic for the application, meaning, smart contracts, smart contract implementations, as well as the scripts and transactions that interact with them. + ## ✨ Getting Started ### 1. Install Dependencies diff --git a/api/.env.local b/api/.env.local new file mode 100644 index 0000000..c7901c7 --- /dev/null +++ b/api/.env.local @@ -0,0 +1,12 @@ + +PORT = 3000 +FLOW_ACCESS_API_URL = 'https://rest-testnet.onflow.org' +ASSET_HANDOVER_ADDRESS = '0xff4cc652369f3857' +BLP_TOKEN_ADDRESS = '0xff4cc652369f3857' +DOMAINS_TOKEN_ADDRESS = '0xff4cc652369f3857' +NETWORK= 'testnet' +MINTER_ADDRESS= '0xee174d2e2ed89369' +MINTER_PRIVATE_KEY_HEX= '5998d5276ca1f204f55ab31fe970ef7654772ac6325bc4ce1ca226f3bd2cc93a' +MINTER_ACCOUNT_KEY_INDEX = 0 +FUNGIBLE_TOKEN_ADDRESS = '0x9a0766d93b6608b7' +NON_FUNGIBLE_TOKEN_ADDRESS = '0x631e88ae7f1d7c20' \ No newline at end of file diff --git a/api/README.md b/api/README.md new file mode 100644 index 0000000..6c3d04d --- /dev/null +++ b/api/README.md @@ -0,0 +1,53 @@ +# Asset-handover API +The Asset-handover API is a RESTful API built with [express](https://expressjs.com/) that sends transactions to Flow using the [Flow JS SDK](https://github.com/onflow/fcl-js/tree/master/packages/sdk). It contains endpoints for the [Asset-handover](src/services/assetHandover.js), the [BLP token](src/services/blpToken.js) and [Domains token](src/services/domainsToken.js) services to read & write data to the Flow blockchain. + +## Services +The Asset-handover API contains four main services. These services synchronize data between the blockchain, and also provide API endpoints that could be consumed by the asset-handover client-side application. + +### [Asset-Handover Service](src/services/assetHandover.js) +This service contains functions that utilize the Flow Service to send Cadence transactions and scripts that fetch and update Asset-handover data. + +You can also run the [transactions and scripts](../cadence) manually using the [Flow CLI](https://docs.onflow.org/flow-cli/). + +[Exposed Endpoints](src/routes/assetHandover.js): +- **POST `/v1/asset-handover/lockUp/updateCreationFees`**: Allows the admin (deployer) of the Asset-Handover smart contract to update the fees charged for creating a lock-up. Uses [updateCreationFees.cdc](/cadence/transactions/lockUps/updateCreationFees.cdc) + +- **POST `/v1/asset-handover/lockUp/updateWithdrawFees`**: Allows the admin (deployer) of the Asset-Handover smart contract to update the fees charged for withdrawing from a lock-up. Uses [updateWithdrawFees.cdc](/cadence/transactions/lockUps/updateWithdrawFees.cdc) + +- **GET `/v1/asset-handover/accountLockUp/:address`**: Fetches the lock-up by holder account. Calls [getAccountLockUp.cdc](/cadence/scripts/lockUps/getAccountLockUp.cdc) + +- **GET `/v1/asset-handover/LockUpsByRecipient/:address`**: Fetches the lock-up by recipient account. Calls [getLockUpsByRecipient.cdc](/cadence/scripts/lockUps/getLockUpsByRecipient.cdc) + +- **GET `/v1/asset-handover/fungibleTokenInfoMapping`**: Fetches the fungible tokens of the Asset-handover registry. Calls [getFungibleTokenInfoMapping.cdc](/cadence/scripts/lockUps/getFungibleTokenInfoMapping.cdc) + +- **GET `/v1/asset-handover/nonFungibleTokenInfoMapping`**: Fetches the non fungible tokens of the Asset-handover registry. Calls [getNonFungibleTokenInfoMapping.cdc](/cadence/scripts/lockUps/getNonFungibleTokenInfoMapping.cdc) + +### [BLP Token Service](src/services/blpToken.js) +This service contains functions that utilize the Flow Service to send Cadence transactions and scripts that fetch and update BLP token data. + +You can also run the [transactions and scripts](../cadence) manually using the [Flow CLI](https://docs.onflow.org/flow-cli/). + +[Exposed Endpoints](src/routes/blpToken.js): +- **POST `/v1/asset-handover/blp/mint`**: Mint BLP tokens for the minter account. Calls [mintTokens.cdc](/cadence/transactions/blp/mintTokens.cdc) + +- **GET `/v1/asset-handover/blp/:address`**: Fetches the balance of BLP tokens for an account. Calls [getAccountBalance.cdc](/cadence/scripts/blp/getAccountBalance.cdc) + +### [Domains Token Service](src/services/domainsToken.js) +This service contains functions that utilize the Flow Service to send Cadence transactions and scripts that fetch and update Domains token data. + +You can also run the [transactions and scripts](../cadence) manually using the [Flow CLI](https://docs.onflow.org/flow-cli/). + +[Exposed Endpoints](src/routes/domainsToken.js): +- **GET `/v1/asset-handover/domains/accountCollection/:address`**: Fetches all the listings created by the account. Returns an array of `listing_resource_ids`. Calls [getAccountCollection.cdc](/cadence/scripts/domains/getAccountCollection.cdc) + +- **POST `/v1/asset-handover/domains/mint`**: Mint Domains token for an account. Calls [registerDomain.cdc](/cadence/transactions/domains/registerDomain.cdc) + +### [Flow Service](src/services/flow.js) +This service contains functions that interact with the Flow blockchain using the [FCL JS](https://docs.onflow.org/fcl/) library. While it has no exposed endpoints, its methods used to read, write and authorize data on the chain are used extensively by its sister services. + +Notable functions: +- `sendTx()`: Takes a Cadence transaction as an arguement and sends it to write data into the Flow blockchain. +- `executeScript()`: Takes a Cadence script as an arguement and executes it to read data from the Flow blockchain. +- `authorizeMinter()`: Returns an asynchronous method to authorize an account to mint tokens. + +*We reuse this service from another active sample-dapp on Flow, [Kitty-items](https://github.com/onflow/kitty-items/tree/master/api#flow-service).* diff --git a/api/package-lock.json b/api/package-lock.json new file mode 100644 index 0000000..cf7d6d1 --- /dev/null +++ b/api/package-lock.json @@ -0,0 +1,1990 @@ +{ + "name": "asset-handover-api", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "asset-handover-api", + "version": "0.0.1", + "license": "ISC", + "dependencies": { + "@onflow/fcl": "^1.3.2", + "body-parser": "^1.20.1", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "elliptic": "^6.5.4", + "express": "^4.18.2", + "express-async-errors": "^3.1.1", + "express-validator": "^6.14.2", + "nodemon": "^2.0.20" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@onflow/config": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@onflow/config/-/config-1.0.5.tgz", + "integrity": "sha512-r2IUyY4SJgAY6YCzKL0cOOertHETp9BgVfCjTIq236WAHr7aMS1oNyqcVPPR++zjDK8n64lRgrxlcYSZB/LrFg==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/util-actor": "^1.1.2" + } + }, + "node_modules/@onflow/fcl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-1.3.2.tgz", + "integrity": "sha512-H2YduE8JUMqjUBR9R3YOFgWrT/yMg+aE5w8uwrMD3YJ/wGpF6iKo0XJGRa7kPVn5pP0HjKQnOlmCLvHwEGEhQQ==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.5", + "@onflow/interaction": "0.0.11", + "@onflow/rlp": "^1.0.2", + "@onflow/sdk": "^1.1.2", + "@onflow/types": "^1.0.5", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "@onflow/util-uid": "^1.0.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@onflow/interaction": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@onflow/interaction/-/interaction-0.0.11.tgz", + "integrity": "sha512-Xuq1Mmx6Wyba/F/L+QLQs0yJeQDsIDwy5SKk5vrCuVgIj0yD8k506g5L8ODrbM1LWll8i0tQsoOi0F85vNl5sA==" + }, + "node_modules/@onflow/rlp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-1.0.4.tgz", + "integrity": "sha512-/4jSoh9eXPoN9iUCsbI+PAuTRJyYgT+XR4NdZYmxILRwOtIsSd2I9XatA4rrSzk9DJ0h8l9SmYx1y0XLnD+ohQ==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "buffer": "^6.0.3" + } + }, + "node_modules/@onflow/sdk": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/sdk/-/sdk-1.1.2.tgz", + "integrity": "sha512-HNfQ6Q91FfFwd2g1wa/YHvmv6BYeXGONkEJyfOaoPRIbp3lG9W35HYX7Yvgtn7fvcQG+TRL8n386mzJ6Vn4dmA==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/rlp": "^1.0.2", + "@onflow/transport-http": "^1.5.0", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "deepmerge": "^4.2.2", + "sha3": "^2.1.4" + } + }, + "node_modules/@onflow/transport-http": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@onflow/transport-http/-/transport-http-1.5.0.tgz", + "integrity": "sha512-CGgfPC1kI+ssqDgFXxGgKHLVtu1tCGtyGJiS/fplyVun9RC7AwEp2KkjTP2dxRlto3HmntoaBkOw7z38UCxRZw==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@onflow/types": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@onflow/types/-/types-1.0.5.tgz", + "integrity": "sha512-Uf6APOwgkxPwgonQ8JRrOB0KZ5eJpNBVXlEiZIv5ddzsfM6qd9p3csEE5YmuLhAooAcisG3ROTzw2hT/zUNGbw==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/util-actor": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-1.1.2.tgz", + "integrity": "sha512-fsRGw5c2idlG5T0u48tzah1iE0OIc4WmgrNYN0gQ1NHX+Ue8iySoQdCIcdSxJCHOXwpHWe68fWTonCzbRJOssQ==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "queue-microtask": "1.1.2" + } + }, + "node_modules/@onflow/util-address": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-1.0.3.tgz", + "integrity": "sha512-w8DPYSvYm5h0hhZ0hZwiCwu9UgJBtIv2KyhDiH3TZG8srT+9GxtTPX4NzZtAVuxqWTNVlTGePD/Upxusiwnk6g==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/util-invariant": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-1.0.3.tgz", + "integrity": "sha512-+Hk93W9JwACLkM5/oOILqI9L55o5QxETBV5Du/2dLgNbjXDElMmQzURFXx6fTmODTLZ6Ri+d1nvb7AxapYFe0Q==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/util-logger": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/util-logger/-/util-logger-1.1.2.tgz", + "integrity": "sha512-BcVDzKNGv/j3gqnq1lc0fpJV7LCHWPJyzYR588/keiY90mFHwLe8ahXYG1UYPu02KymlVqlInfISKWlNwk4aug==", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.4" + } + }, + "node_modules/@onflow/util-template": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-1.0.4.tgz", + "integrity": "sha512-syTJzhFn+CaW/FAqVLJJ3Oq9pTuxS5Wk0+g9AkSKWJ8PCY+FLr7UC2mGr/5pO//L13Elz7cv/djXVxNA1ULQmw==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/@onflow/util-uid": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-1.0.3.tgz", + "integrity": "sha512-hBdfV+0BSDphcvLvL8KFivyV5ZhpVyRcrOn0fuSD4is/hN5d1mLFBev9gur9gdmQUUwgFt0v+aA/Tc/7mRDctQ==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/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==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "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" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-async-errors": { + "version": "3.1.1", + "license": "ISC", + "peerDependencies": { + "express": "^4.16.2" + } + }, + "node_modules/express-validator": { + "version": "6.14.2", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "validator": "^13.7.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "2.0.20", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nopt": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.11.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.1.2.tgz", + "integrity": "sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ==" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "5.7.1", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "dependencies": { + "buffer": "6.0.3" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "license": "ISC", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@onflow/config": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@onflow/config/-/config-1.0.5.tgz", + "integrity": "sha512-r2IUyY4SJgAY6YCzKL0cOOertHETp9BgVfCjTIq236WAHr7aMS1oNyqcVPPR++zjDK8n64lRgrxlcYSZB/LrFg==", + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/util-actor": "^1.1.2" + } + }, + "@onflow/fcl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-1.3.2.tgz", + "integrity": "sha512-H2YduE8JUMqjUBR9R3YOFgWrT/yMg+aE5w8uwrMD3YJ/wGpF6iKo0XJGRa7kPVn5pP0HjKQnOlmCLvHwEGEhQQ==", + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.5", + "@onflow/interaction": "0.0.11", + "@onflow/rlp": "^1.0.2", + "@onflow/sdk": "^1.1.2", + "@onflow/types": "^1.0.5", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "@onflow/util-uid": "^1.0.2", + "node-fetch": "^2.6.7" + } + }, + "@onflow/interaction": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@onflow/interaction/-/interaction-0.0.11.tgz", + "integrity": "sha512-Xuq1Mmx6Wyba/F/L+QLQs0yJeQDsIDwy5SKk5vrCuVgIj0yD8k506g5L8ODrbM1LWll8i0tQsoOi0F85vNl5sA==" + }, + "@onflow/rlp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-1.0.4.tgz", + "integrity": "sha512-/4jSoh9eXPoN9iUCsbI+PAuTRJyYgT+XR4NdZYmxILRwOtIsSd2I9XatA4rrSzk9DJ0h8l9SmYx1y0XLnD+ohQ==", + "requires": { + "@babel/runtime": "^7.18.6", + "buffer": "^6.0.3" + } + }, + "@onflow/sdk": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/sdk/-/sdk-1.1.2.tgz", + "integrity": "sha512-HNfQ6Q91FfFwd2g1wa/YHvmv6BYeXGONkEJyfOaoPRIbp3lG9W35HYX7Yvgtn7fvcQG+TRL8n386mzJ6Vn4dmA==", + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.3", + "@onflow/rlp": "^1.0.2", + "@onflow/transport-http": "^1.5.0", + "@onflow/util-actor": "^1.1.1", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "deepmerge": "^4.2.2", + "sha3": "^2.1.4" + } + }, + "@onflow/transport-http": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@onflow/transport-http/-/transport-http-1.5.0.tgz", + "integrity": "sha512-CGgfPC1kI+ssqDgFXxGgKHLVtu1tCGtyGJiS/fplyVun9RC7AwEp2KkjTP2dxRlto3HmntoaBkOw7z38UCxRZw==", + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/util-address": "^1.0.2", + "@onflow/util-invariant": "^1.0.2", + "@onflow/util-logger": "^1.1.1", + "@onflow/util-template": "^1.0.3", + "node-fetch": "^2.6.7" + } + }, + "@onflow/types": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@onflow/types/-/types-1.0.5.tgz", + "integrity": "sha512-Uf6APOwgkxPwgonQ8JRrOB0KZ5eJpNBVXlEiZIv5ddzsfM6qd9p3csEE5YmuLhAooAcisG3ROTzw2hT/zUNGbw==", + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-actor": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-1.1.2.tgz", + "integrity": "sha512-fsRGw5c2idlG5T0u48tzah1iE0OIc4WmgrNYN0gQ1NHX+Ue8iySoQdCIcdSxJCHOXwpHWe68fWTonCzbRJOssQ==", + "requires": { + "@babel/runtime": "^7.18.6", + "queue-microtask": "1.1.2" + } + }, + "@onflow/util-address": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-1.0.3.tgz", + "integrity": "sha512-w8DPYSvYm5h0hhZ0hZwiCwu9UgJBtIv2KyhDiH3TZG8srT+9GxtTPX4NzZtAVuxqWTNVlTGePD/Upxusiwnk6g==", + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-invariant": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-1.0.3.tgz", + "integrity": "sha512-+Hk93W9JwACLkM5/oOILqI9L55o5QxETBV5Du/2dLgNbjXDElMmQzURFXx6fTmODTLZ6Ri+d1nvb7AxapYFe0Q==", + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-logger": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@onflow/util-logger/-/util-logger-1.1.2.tgz", + "integrity": "sha512-BcVDzKNGv/j3gqnq1lc0fpJV7LCHWPJyzYR588/keiY90mFHwLe8ahXYG1UYPu02KymlVqlInfISKWlNwk4aug==", + "requires": { + "@babel/runtime": "^7.18.6", + "@onflow/config": "^1.0.4" + } + }, + "@onflow/util-template": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-1.0.4.tgz", + "integrity": "sha512-syTJzhFn+CaW/FAqVLJJ3Oq9pTuxS5Wk0+g9AkSKWJ8PCY+FLr7UC2mGr/5pO//L13Elz7cv/djXVxNA1ULQmw==", + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "@onflow/util-uid": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-1.0.3.tgz", + "integrity": "sha512-hBdfV+0BSDphcvLvL8KFivyV5ZhpVyRcrOn0fuSD4is/hN5d1mLFBev9gur9gdmQUUwgFt0v+aA/Tc/7mRDctQ==", + "requires": { + "@babel/runtime": "^7.18.6" + } + }, + "abbrev": { + "version": "1.1.1" + }, + "accepts": { + "version": "1.3.8", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "anymatch": { + "version": "3.1.3", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-flatten": { + "version": "1.1.1" + }, + "balanced-match": { + "version": "1.0.2" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "binary-extensions": { + "version": "2.2.0" + }, + "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==" + }, + "body-parser": { + "version": "1.20.1", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "bytes": { + "version": "3.1.2" + }, + "call-bind": { + "version": "1.0.2", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "chokidar": { + "version": "3.5.3", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "concat-map": { + "version": "0.0.1" + }, + "content-disposition": { + "version": "0.5.4", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4" + }, + "cookie": { + "version": "0.5.0" + }, + "cookie-signature": { + "version": "1.0.6" + }, + "cors": { + "version": "2.8.5", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "requires": { + "ms": "2.0.0" + } + }, + "deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==" + }, + "depd": { + "version": "2.0.0" + }, + "destroy": { + "version": "1.2.0" + }, + "dotenv": { + "version": "16.0.3" + }, + "ee-first": { + "version": "1.1.1" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "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" + } + }, + "encodeurl": { + "version": "1.0.2" + }, + "escape-html": { + "version": "1.0.3" + }, + "etag": { + "version": "1.8.1" + }, + "express": { + "version": "4.18.2", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "express-async-errors": { + "version": "3.1.1", + "requires": {} + }, + "express-validator": { + "version": "6.14.2", + "requires": { + "lodash": "^4.17.21", + "validator": "^13.7.0" + } + }, + "fill-range": { + "version": "7.0.1", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0" + }, + "fresh": { + "version": "0.5.2" + }, + "fsevents": { + "version": "2.3.2", + "optional": true + }, + "function-bind": { + "version": "1.1.1" + }, + "get-intrinsic": { + "version": "1.1.3", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "glob-parent": { + "version": "5.1.2", + "requires": { + "is-glob": "^4.0.1" + } + }, + "has": { + "version": "1.0.3", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0" + }, + "has-symbols": { + "version": "1.0.3" + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "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": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-errors": { + "version": "2.0.0", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore-by-default": { + "version": "1.0.1" + }, + "inherits": { + "version": "2.0.4" + }, + "ipaddr.js": { + "version": "1.9.1" + }, + "is-binary-path": { + "version": "2.1.0", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1" + }, + "is-glob": { + "version": "4.0.3", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0" + }, + "lodash": { + "version": "4.17.21" + }, + "media-typer": { + "version": "0.3.0" + }, + "merge-descriptors": { + "version": "1.0.1" + }, + "methods": { + "version": "1.1.2" + }, + "mime": { + "version": "1.6.0" + }, + "mime-db": { + "version": "1.52.0" + }, + "mime-types": { + "version": "2.1.35", + "requires": { + "mime-db": "1.52.0" + } + }, + "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==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "minimatch": { + "version": "3.1.2", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0" + }, + "negotiator": { + "version": "0.6.3" + }, + "node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nodemon": { + "version": "2.0.20", + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3" + } + } + }, + "nopt": { + "version": "1.0.10", + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0" + }, + "object-assign": { + "version": "4.1.1" + }, + "object-inspect": { + "version": "1.12.2" + }, + "on-finished": { + "version": "2.4.1", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3" + }, + "path-to-regexp": { + "version": "0.1.7" + }, + "picomatch": { + "version": "2.3.1" + }, + "proxy-addr": { + "version": "2.0.7", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pstree.remy": { + "version": "1.1.8" + }, + "qs": { + "version": "6.11.0", + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue-microtask": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.1.2.tgz", + "integrity": "sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ==" + }, + "range-parser": { + "version": "1.2.1" + }, + "raw-body": { + "version": "2.5.1", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readdirp": { + "version": "3.6.0", + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "safe-buffer": { + "version": "5.2.1" + }, + "safer-buffer": { + "version": "2.1.2" + }, + "semver": { + "version": "5.7.1" + }, + "send": { + "version": "0.18.0", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3" + } + } + }, + "serve-static": { + "version": "1.15.0", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0" + }, + "sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "requires": { + "buffer": "6.0.3" + } + }, + "side-channel": { + "version": "1.0.4", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "simple-update-notifier": { + "version": "1.0.8", + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0" + } + } + }, + "statuses": { + "version": "2.0.1" + }, + "supports-color": { + "version": "5.5.0", + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1" + }, + "touch": { + "version": "3.1.0", + "requires": { + "nopt": "~1.0.10" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "type-is": { + "version": "1.6.18", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "undefsafe": { + "version": "2.0.5" + }, + "unpipe": { + "version": "1.0.0" + }, + "utils-merge": { + "version": "1.0.1" + }, + "validator": { + "version": "13.7.0" + }, + "vary": { + "version": "1.1.2" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/api/package.json b/api/package.json new file mode 100644 index 0000000..dbcf8a0 --- /dev/null +++ b/api/package.json @@ -0,0 +1,24 @@ +{ + "name": "asset-handover-api", + "version": "0.0.1", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "start": "npx nodemon src/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@onflow/fcl": "^1.3.2", + "body-parser": "^1.20.1", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "elliptic": "^6.5.4", + "express": "^4.18.2", + "express-async-errors": "^3.1.1", + "express-validator": "^6.14.2", + "nodemon": "^2.0.20" + } +} diff --git a/api/src/app.js b/api/src/app.js new file mode 100644 index 0000000..aff531e --- /dev/null +++ b/api/src/app.js @@ -0,0 +1,35 @@ +import pkg from 'body-parser'; +import cors from 'cors'; +import express from 'express'; +import 'express-async-errors'; +import initAssetHandoverRouter from './routes/assetHandover.js'; +import initBlpTokenRouter from './routes/blpToken.js'; +import initDomainsTokenRouter from './routes/domainsToken.js'; + +const { json, urlencoded } = pkg; + +const V1 = '/v1/'; + +// Init all +const initApp = ( + AssetHandoverService, + BlpTokenService, + DomainsTokenService +) => { + const app = express(); + + app.use(cors()); + app.use(json()); + app.use(urlencoded({ extended: false })); + app.use(V1, initAssetHandoverRouter(AssetHandoverService)); + app.use(V1, initBlpTokenRouter(BlpTokenService)); + app.use(V1, initDomainsTokenRouter(DomainsTokenService)); + + app.all('*', async (req, res) => { + return res.sendStatus(404); + }); + + return app; +}; + +export default initApp; diff --git a/api/src/config.js b/api/src/config.js new file mode 100644 index 0000000..c01a907 --- /dev/null +++ b/api/src/config.js @@ -0,0 +1,33 @@ +const defaultPort = 3000; + +export function getConfig(env) { + env = env ?? process.env; + + console.log('ENV = ' + JSON.stringify(env)); + + const port = env.PORT || defaultPort; + const accessApi = env.FLOW_ACCESS_API_URL; + const assetHandoverAddress = env.ASSET_HANDOVER_ADDRESS; + const blpTokenAddress = env.BLP_TOKEN_ADDRESS; + const domainsTokenAddress = env.DOMAINS_TOKEN_ADDRESS; + const testnet = env.NETWORK; + const minterAddress = env.MINTER_ADDRESS; + const minterPrivateKeyHex = env.MINTER_PRIVATE_KEY_HEX; + const minterAccountKeyIndex = env.MINTER_ACCOUNT_KEY_INDEX; + const fungibleTokenAddress = env.FUNGIBLE_TOKEN_ADDRESS; + const nonFungibleTokenAddress = env.NON_FUNGIBLE_TOKEN_ADDRESS; + + return { + port, + accessApi, + assetHandoverAddress, + testnet, + minterAddress, + minterPrivateKeyHex, + minterAccountKeyIndex, + fungibleTokenAddress, + blpTokenAddress, + domainsTokenAddress, + nonFungibleTokenAddress, + }; +} diff --git a/api/src/index.js b/api/src/index.js new file mode 100644 index 0000000..539481d --- /dev/null +++ b/api/src/index.js @@ -0,0 +1,76 @@ +import initApp from './app.js'; +import { getConfig } from './config.js'; +import dotenv from 'dotenv'; +import * as fcl from '@onflow/fcl'; +import { AssetHandoverService } from './services/assetHandover.js'; +import { BlpTokenService } from './services/blpToken.js'; +import { FlowService } from './services/flow.js'; +import { DomainsTokenService } from './services/domainsToken.js'; + +const envVars = dotenv.config({ + path: '.env.local', +}); + +async function run() { + const config = getConfig(envVars.parsed); + + const flowService = new FlowService( + config.minterAddress, + config.minterPrivateKeyHex, + config.minterAccountKeyIndex + ); + + // Make sure we're pointing to the correct Flow Access API. + fcl.config({ + 'flow.network': config.testnet, + 'accessNode.api': config.accessApi, + //'discovery.wallet': 'https://fcl-discovery.onflow.org/testnet/authn', + }); + + const startAPIServer = () => { + console.log('Starting API server ....'); + console.log('ADDRESS IS: ' + config.assetHandoverAddress); + console.log(config); + const assetHandoverService = new AssetHandoverService( + config.assetHandoverAddress, + config.fungibleTokenAddress, + flowService + ); + const blpTokenService = new BlpTokenService( + config.blpTokenAddress, + config.fungibleTokenAddress, + flowService + ); + const domainsTokenService = new DomainsTokenService( + config.domainsTokenAddress, + config.nonFungibleTokenAddress, + config.fungibleTokenAddress, + flowService + ); + const app = initApp( + assetHandoverService, + blpTokenService, + domainsTokenService + ); + + app.listen(config.port, () => { + console.log(`Listening on port ${config.port}!`); + }); + + process.on('SIGINT', () => { + console.info('SIGINT signal received.'); + console.log('Closing API server ....'); + app.close(() => { + console.log('API server closed ....'); + }); + }); + }; + startAPIServer(); +} + +const redOutput = '\x1b[31m%s\x1b[0m'; + +run().catch((e) => { + console.error(redOutput, e); + process.exit(1); +}); diff --git a/api/src/middlewares/validateRequest.js b/api/src/middlewares/validateRequest.js new file mode 100644 index 0000000..551ddb7 --- /dev/null +++ b/api/src/middlewares/validateRequest.js @@ -0,0 +1,18 @@ +import { validationResult } from 'express-validator'; + +// validateRequest works in conjunction with the 'express-validator' package. If we use the validations from +// 'express-validator', this middleware will check if any errors were thrown within the route and then +// return an appropriate error message to the API user. +const validateRequest = (req, res, next) => { + const errors = validationResult(req); + + if (!errors.isEmpty()) { + return res.status(400).send({ + errors: errors.array(), + }); + } + + next(); +}; + +export { validateRequest }; diff --git a/api/src/routes/assetHandover.js b/api/src/routes/assetHandover.js new file mode 100644 index 0000000..3d94957 --- /dev/null +++ b/api/src/routes/assetHandover.js @@ -0,0 +1,101 @@ +import express from 'express'; +import { body, param } from 'express-validator'; +import { validateRequest } from '../middlewares/validateRequest.js'; + +function initAssetHandoverRouter(AssetHandoverService) { + const router = express.Router(); + + router.post( + '/asset-handover/lockUp/updateCreationFees', + [ + body('fees', 'Enter a valid fee in decimal format.') + .trim() + .isFloat(), + ], + validateRequest, + async (req, res) => { + const fees = parseFloat(req.body).toFixed(8); + console.log('body: ' + JSON.stringify(fees)); + const tx = await AssetHandoverService.updateCreationFees(fees); + return res.send({ + transaction: tx, + }); + } + ); + + router.post( + '/asset-handover/lockUp/updateWithdrawFees', + [ + body('fees', 'Enter a valid fee in decimal format.') + .trim() + .isFloat(), + ], + validateRequest, + async (req, res) => { + const fees = parseFloat(req.body).toFixed(8); + console.log('body: ' + JSON.stringify(req.body)); + const tx = await AssetHandoverService.updateWithdrawFees(fees); + return res.send({ + transaction: tx, + }); + } + ); + + router.get( + '/asset-handover/accountLockUp/:address', + [ + param('address', 'Enter a valid Flow address.') + .trim() + .isLength({ min: 18, max: 18 }) + .isAlphanumeric(), + ], + validateRequest, + async (req, res) => { + const accountLockUp = + await AssetHandoverService.getAccountLockUp( + req.params.address + ); + return res.status(200).send({ accountLockUp }); + } + ); + + router.get( + '/asset-handover/fungibleTokenInfoMapping', + async (req, res) => { + const fungibleTokenInfoMapping = + await AssetHandoverService.getFungibleTokenInfoMapping(); + return res.status(200).send({ fungibleTokenInfoMapping }); + } + ); + + router.get( + '/asset-handover/nonFungibleTokenInfoMapping', + async (req, res) => { + const nonFungibleTokenInfoMapping = + await AssetHandoverService.getNonFungibleTokenInfoMapping(); + return res.status(200).send({ nonFungibleTokenInfoMapping }); + } + ); + + router.get( + '/asset-handover/LockUpsByRecipient/:address', + [ + param('address', 'Enter a valid Flow address.') + .trim() + .isLength({ min: 18, max: 18 }) + .isAlphanumeric(), + ], + validateRequest, + async (req, res) => { + const lockUpsByRecipient = + await AssetHandoverService.getLockUpsByRecipient( + req.params.address + ); + return res.status(200).send({ lockUpsByRecipient }); + } + ); + + return router; +} + +export default initAssetHandoverRouter; diff --git a/api/src/routes/blpToken.js b/api/src/routes/blpToken.js new file mode 100644 index 0000000..ce55002 --- /dev/null +++ b/api/src/routes/blpToken.js @@ -0,0 +1,46 @@ +import express from 'express'; +import { body, param } from 'express-validator'; +import { validateRequest } from '../middlewares/validateRequest.js'; + +function initBlpTokenRouter(BlpTokenService) { + const router = express.Router(); + + router.post( + '/asset-handover/blp/mint', + [ + body('fees', 'Enter a valid fee in decimal format.') + .trim() + .isFloat(), + ], + validateRequest, + async (req, res) => { + const amount = parseFloat(req.body).toFixed(8); + console.log('body: ' + JSON.stringify(req.body)); + const tx = await BlpTokenService.mint(amount); + return res.send({ + transaction: tx, + }); + } + ); + + router.get( + '/asset-handover/blp/:address', + [ + param('address', 'Enter a valid Flow address.') + .trim() + .isLength({ min: 18, max: 18 }) + .isAlphanumeric(), + ], + validateRequest, + async (req, res) => { + const accountBalance = await BlpTokenService.getAccountBalance( + req.params.address + ); + return res.status(200).send({ accountBalance }); + } + ); + + return router; +} + +export default initBlpTokenRouter; diff --git a/api/src/routes/domainsToken.js b/api/src/routes/domainsToken.js new file mode 100644 index 0000000..7e030b4 --- /dev/null +++ b/api/src/routes/domainsToken.js @@ -0,0 +1,53 @@ +import express from 'express'; +import { body, param } from 'express-validator'; +import { validateRequest } from '../middlewares/validateRequest.js'; + +function initDomainsTokenRouter(DomainsTokenService) { + const router = express.Router(); + + router.post( + '/asset-handover/domains/mint', + [ + body('name', 'Enter a valid string.').exists({ + checkFalsy: true, + }), + body('duration', 'Enter a valid duration in decimal format.') + .trim() + .isFloat(), + ], + validateRequest, + async (req, res) => { + const { name, duration } = req.body; + console.log('body: ' + JSON.stringify(req.body)); + const tx = await DomainsTokenService.mint( + name, + parseFloat(duration).toFixed(8) + ); + return res.send({ + transaction: tx, + }); + } + ); + + router.get( + '/asset-handover/domains/accountCollection/:address', + [ + param('address', 'Enter a valid Flow address.') + .trim() + .isLength({ min: 18, max: 18 }) + .isAlphanumeric(), + ], + validateRequest, + async (req, res) => { + const accountCollection = + await DomainsTokenService.getAccountCollection( + req.params.address + ); + return res.status(200).send({ accountCollection }); + } + ); + + return router; +} + +export default initDomainsTokenRouter; diff --git a/api/src/services/assetHandover.js b/api/src/services/assetHandover.js new file mode 100644 index 0000000..c011ff2 --- /dev/null +++ b/api/src/services/assetHandover.js @@ -0,0 +1,149 @@ +import * as fcl from '@onflow/fcl'; +import * as t from '@onflow/types'; +import * as fs from 'fs'; + +const assetHandoverPath = '"../../contracts/AssetHandover.cdc"'; + +class AssetHandoverService { + constructor( + assetHandoverAddress, + fungibleTokenAddress, + flowService + ) { + this.assetHandoverAddress = assetHandoverAddress; + this.fungibleTokenAddress = fungibleTokenAddress; + this.flowService = flowService; + } + + updateCreationFees = async (fees) => { + const authorization = this.flowService.authorizeMinter(); + + const filePath = new URL( + '../../../cadence/transactions/lockUps/updateCreationFees.cdc', + import.meta.url + ); + const transaction = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.sendTx({ + transaction, + args: [fcl.arg(fees, t.UFix64)], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }); + }; + + updateWithdrawFees = async (fees) => { + const authorization = this.flowService.authorizeMinter(); + + const filePath = new URL( + '../../../cadence/transactions/lockUps/updateWithdrawFees.cdc', + import.meta.url + ); + const transaction = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.sendTx({ + transaction, + args: [fcl.arg(fees, t.UFix64)], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }); + }; + + getFungibleTokenInfoMapping = async () => { + const filePath = new URL( + '../../../cadence/scripts/lockups/getFungibleTokenInfoMapping.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.executeScript({ + script, + args: [], + }); + }; + + getNonFungibleTokenInfoMapping = async () => { + const filePath = new URL( + '../../../cadence/scripts/lockups/getNonFungibleTokenInfoMapping.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.executeScript({ + script, + args: [], + }); + }; + + getAccountLockUp = async (address) => { + const filePath = new URL( + '../../../cadence/scripts/lockups/getAccountLockUp.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.executeScript({ + script, + args: [fcl.arg(address, t.Address)], + }); + }; + + getLockUpsByRecipient = async (address) => { + const filePath = new URL( + '../../../cadence/scripts/lockups/getLockUpsByRecipient.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + assetHandoverPath, + fcl.withPrefix(this.assetHandoverAddress) + ); + + return this.flowService.executeScript({ + script, + args: [fcl.arg(address, t.Address)], + }); + }; +} + +export { AssetHandoverService }; diff --git a/api/src/services/blpToken.js b/api/src/services/blpToken.js new file mode 100644 index 0000000..ead3d02 --- /dev/null +++ b/api/src/services/blpToken.js @@ -0,0 +1,60 @@ +import * as fcl from '@onflow/fcl'; +import * as t from '@onflow/types'; +import * as fs from 'fs'; + +const blpTokenPath = '"../../contracts/tokens/BlpToken.cdc"'; +const fungibleTokenPath = + '"../../contracts/interfaces/FungibleToken.cdc"'; + +class BlpTokenService { + constructor(blpTokenAddress, fungibleTokenAddress, flowService) { + this.blpTokenAddress = blpTokenAddress; + this.fungibleTokenAddress = fungibleTokenAddress; + this.flowService = flowService; + } + + mint = async (amount) => { + const authorization = this.flowService.authorizeMinter(); + + const filePath = new URL( + '../../../cadence/transactions/blp/mintTokens.cdc', + import.meta.url + ); + const transaction = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace(blpTokenPath, fcl.withPrefix(this.blpTokenAddress)); + + return this.flowService.sendTx({ + transaction, + args: [fcl.arg(amount, t.UFix64)], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }); + }; + + getAccountBalance = async (address) => { + const filePath = new URL( + '../../../cadence/scripts/blp/getAccountBalance.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace( + fungibleTokenPath, + fcl.withPrefix(this.fungibleTokenAddress) + ) + .replace(blpTokenPath, fcl.withPrefix(this.blpTokenAddress)); + + return this.flowService.executeScript({ + script, + args: [fcl.arg(address, t.Address)], + }); + }; +} + +export { BlpTokenService }; diff --git a/api/src/services/domainsToken.js b/api/src/services/domainsToken.js new file mode 100644 index 0000000..4303150 --- /dev/null +++ b/api/src/services/domainsToken.js @@ -0,0 +1,76 @@ +import * as fcl from '@onflow/fcl'; +import * as t from '@onflow/types'; +import * as fs from 'fs'; + +const domainsPath = '"../../contracts/nfts/Domains.cdc"'; +const nonFungibleTokenPath = + '"../../contracts/interfaces/NonFungibleToken.cdc"'; +const fungibleTokenPath = + '"../../contracts/interfaces/FungibleToken.cdc"'; + +class DomainsTokenService { + constructor( + domainsTokenAddress, + nonFungibleTokenAddress, + fungibleTokenAddress, + flowService + ) { + this.domainsTokenAddress = domainsTokenAddress; + this.nonFungibleTokenAddress = nonFungibleTokenAddress; + this.fungibleTokenAddress = fungibleTokenAddress; + this.flowService = flowService; + } + + mint = async (name, duration) => { + const authorization = this.flowService.authorizeMinter(); + + const filePath = new URL( + '../../../cadence/transactions/domains/registerDomain.cdc', + import.meta.url + ); + const transaction = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace(domainsPath, fcl.withPrefix(this.domainsTokenAddress)) + .replace( + fungibleTokenPath, + fcl.withPrefix(this.fungibleTokenAddress) + ) + .replace( + nonFungibleTokenPath, + fcl.withPrefix(this.nonFungibleTokenAddress) + ); + + return this.flowService.sendTx({ + transaction, + args: [fcl.arg(name, t.String), fcl.arg(duration, t.UFix64)], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }); + }; + + getAccountCollection = async (address) => { + const filePath = new URL( + '../../../cadence/scripts/domains/getAccountCollection.cdc', + import.meta.url + ); + const script = fs + .readFileSync(filePath, { + encoding: 'utf8', + }) + .replace(domainsPath, fcl.withPrefix(this.domainsTokenAddress)) + .replace( + nonFungibleTokenPath, + fcl.withPrefix(this.nonFungibleTokenAddress) + ); + + return this.flowService.executeScript({ + script, + args: [fcl.arg(address, t.Address)], + }); + }; +} + +export { DomainsTokenService }; diff --git a/api/src/services/flow.js b/api/src/services/flow.js new file mode 100644 index 0000000..05d9f71 --- /dev/null +++ b/api/src/services/flow.js @@ -0,0 +1,93 @@ +import * as fcl from '@onflow/fcl'; +import pkg from 'elliptic'; +import { SHA3 } from 'sha3'; + +const { ec } = pkg; +const elC = new ec('p256'); + +class FlowService { + constructor( + minterFlowAddress, + minterPrivateKeyHex, + minterAccountIndex + ) { + this.minterFlowAddress = minterFlowAddress; + this.minterPrivateKeyHex = minterPrivateKeyHex; + this.minterAccountIndex = minterAccountIndex; + } + + authorizeMinter = () => { + return async (account = {}) => { + const user = await this.getAccount(this.minterFlowAddress); + const key = user.keys[this.minterAccountIndex]; + //const key = user.keys[parseInt(this.minterAccountIndex, 10)]; + + const sign = this.signWithKey; + const pk = this.minterPrivateKeyHex; + + return { + ...account, + tempId: `${user.address}-${key.index}`, + addr: fcl.sansPrefix(user.address), + keyId: Number(key.index), + signingFunction: (signable) => { + return { + addr: fcl.withPrefix(user.address), + keyId: Number(key.index), + signature: sign(pk, signable.message), + }; + }, + }; + }; + }; + + getAccount = async (addr) => { + const { account } = await fcl.send([fcl.getAccount(addr)]); + return account; + }; + + signWithKey = (privateKey, msg) => { + const key = elC.keyFromPrivate(Buffer.from(privateKey, 'hex')); + const sig = key.sign(this.hashMsg(msg)); + const n = 32; + const r = sig.r.toArrayLike(Buffer, 'be', n); + const s = sig.s.toArrayLike(Buffer, 'be', n); + return Buffer.concat([r, s]).toString('hex'); + }; + + hashMsg = (msg) => { + const sha = new SHA3(256); + sha.update(Buffer.from(msg, 'hex')); + return sha.digest(); + }; + + sendTx = async ({ + transaction, + args, + proposer, + authorizations, + payer, + skipSeal, + }) => { + const response = await fcl.mutate({ + cadence: transaction, + args: (_arg, _t) => args, + proposer, + authorizations, + payer, + limit: 9999, + }); + + if (skipSeal) return response; + return await fcl.tx(response).onceSealed(); + }; + + async executeScript({ script, args }) { + return await fcl.query({ + cadence: script, + args: (_arg, _t) => args, + }); + } +} + +export { FlowService };