From 1d5b015af563a8d5f6d91d24bef2b9f2b5bf5540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Augusto?= Date: Tue, 22 Nov 2022 22:44:50 +0000 Subject: [PATCH] feat(cbdc-bridging-app): implementation of CBDC bridging example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This application demonstrates a CBDC bridging between a Fabric and a Besu network using the ODAP business logic plugin, a protocol under standardization for cross-chain asset transfers in the IETF. The goal is to demonstrate the use of npm packages of Cactus so that anyone can build a solution on top of Cacti. Summary: * CBDC application backend (set up underlying ledgers and deployment of smart contracts) * Add support for multiple organizations in some operations in the FabricTestLedger * Add support for minting some Ether to a given address in the BesuTestLedger Future Work: * CBDC app UI demonstrating the use of the app is, for now, only available as docker image * A Dockerfile for the backend will be created as soon as this package is published to npm * More documentation will be added in the next weeks Dependent on #2205 Signed-off-by: André Augusto --- .cspell.json | 3 + .vscode/template.launch.json | 25 + .../main/typescript/carbon-accounting-app.ts | 8 +- .../CHANGELOG.md | 0 .../Dockerfile | 69 + .../README.md | 38 + .../healthcheck.sh | 3 + .../package.json | 116 + .../process.env | 5 + .../run-cbdc-app.sh | 14 + .../src/crypto-material/crypto-material.json | 40 + .../asset-reference/typescript/.gitignore | 16 + .../asset-reference/typescript/package.json | 64 + .../src/asset-reference-contract.spec.ts | 295 + .../src/asset-reference-contract.ts | 332 + .../typescript/src/asset-reference.ts | 20 + .../asset-reference/typescript/src/index.ts | 9 + .../asset-reference/typescript/tsconfig.json | 19 + .../cbdc-erc-20/javascript/.eslintignore | 5 + .../cbdc-erc-20/javascript/.gitignore | 78 + .../crypto-material/crypto-material.json | 22 + .../cbdc-erc-20/javascript/index.js | 12 + .../cbdc-erc-20/javascript/lib/tokenERC20.js | 690 ++ .../cbdc-erc-20/javascript/package.json | 51 + .../javascript/test/tokenERC20.test.js | 397 ++ .../main/typescript/cbdc-bridging-app-cli.ts | 84 + .../src/main/typescript/cbdc-bridging-app.ts | 240 + .../src/main/typescript/index.ts | 1 + .../src/main/typescript/index.web.ts | 1 + .../cbdc-bridging-app-dummy-infrastructure.ts | 756 +++ .../odap-extension/besu-odap-gateway.ts | 614 ++ .../odap-extension/client-helper.ts | 141 + .../odap-extension/fabric-odap-gateway.ts | 611 ++ .../odap-extension/server-helper.ts | 129 + .../src/main/typescript/public-api.ts | 2 + .../AssetReferenceContract.json | 5778 +++++++++++++++++ .../asset-reference-contract/MyOwnable.sol | 78 + .../asset-reference-contract-test.sol | 60 + .../asset-reference-contract.sol | 130 + .../solidity/cbdc-erc-20/CBDCcontract.json | 2495 +++++++ .../cbdc-erc-20/cbdc-contract-test.sol | 49 + .../src/solidity/cbdc-erc-20/cbdc-erc-20.sol | 60 + .../test/typescript/cucumber/besu-helper.ts | 116 + .../test/typescript/cucumber/fabric-helper.ts | 161 + .../cucumber/features/besu-gateway.feature | 40 + .../cucumber/features/bridge-back.feature | 87 + .../cucumber/features/bridge-out.feature | 42 + .../cucumber/features/fabric-gateway.feature | 57 + .../cucumber/steps/besu-gateway.steps.ts | 161 + .../cucumber/steps/bridge-back.steps.ts | 132 + .../cucumber/steps/bridge-out.steps.ts | 157 + .../test/typescript/cucumber/steps/common.ts | 54 + .../cucumber/steps/fabric-gateway.steps.ts | 177 + .../integration/api-surface.test.ts | 6 + .../test/typescript/unit/api-surface.test.ts | 6 + .../supervisord.conf | 25 + .../tsconfig.json | 52 + .../cactus-example-cbdc-bridging/README.md | 22 + .../src/main/typescript/supply-chain-app.ts | 8 +- package.json | 1 + .../deploy-cc-from-golang-source.test.ts | 4 +- .../run-transaction-endpoint-v1.test.ts | 4 +- ...cc-from-golang-source-private-data.test.ts | 4 +- .../deploy-cc-from-golang-source.test.ts | 4 +- .../deploy-cc-from-javascript-source.test.ts | 4 +- .../deploy-cc-from-typescript-source.test.ts | 4 +- .../fabric-v2-2-x/deploy-lock-asset.test.ts | 4 +- .../fabric-v2-2-x/get-block.test.ts | 8 +- .../run-transaction-endpoint-v1.test.ts | 4 +- .../openapi/openapi-validation.test.ts | 4 +- .../backup-gateway-after-client-crash.test.ts | 8 +- .../client-crash-after-delete-asset.test.ts | 8 +- .../client-crash-after-lock-asset.test.ts | 8 +- ...dap-api-call-with-ledger-connector.test.ts | 8 +- .../integration/odap-rollback.test.ts | 8 +- .../server-crash-after-create-asset.test.ts | 8 +- .../main/typescript/besu/besu-test-ledger.ts | 28 +- .../fabric/fabric-test-ledger-v1.ts | 36 +- tsconfig.json | 3 + 79 files changed, 14967 insertions(+), 56 deletions(-) create mode 100644 examples/cactus-example-cbdc-bridging-backend/CHANGELOG.md create mode 100644 examples/cactus-example-cbdc-bridging-backend/Dockerfile create mode 100644 examples/cactus-example-cbdc-bridging-backend/README.md create mode 100644 examples/cactus-example-cbdc-bridging-backend/healthcheck.sh create mode 100644 examples/cactus-example-cbdc-bridging-backend/package.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/process.env create mode 100755 examples/cactus-example-cbdc-bridging-backend/run-cbdc-app.sh create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/crypto-material/crypto-material.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/.gitignore create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/package.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.spec.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/index.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/tsconfig.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.eslintignore create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.gitignore create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/crypto-material/crypto-material.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/index.js create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/lib/tokenERC20.js create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/package.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/test/tokenERC20.test.js create mode 100755 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app-cli.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app.ts create mode 100755 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.ts create mode 100755 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.web.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/infrastructure/cbdc-bridging-app-dummy-infrastructure.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/besu-odap-gateway.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/client-helper.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/fabric-odap-gateway.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/server-helper.ts create mode 100755 examples/cactus-example-cbdc-bridging-backend/src/main/typescript/public-api.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/AssetReferenceContract.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract-test.sol create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/CBDCcontract.json create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-contract-test.sol create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/besu-helper.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/fabric-helper.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/besu-gateway.feature create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-back.feature create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-out.feature create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/fabric-gateway.feature create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/besu-gateway.steps.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-back.steps.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-out.steps.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/common.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/fabric-gateway.steps.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/integration/api-surface.test.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/src/test/typescript/unit/api-surface.test.ts create mode 100644 examples/cactus-example-cbdc-bridging-backend/supervisord.conf create mode 100644 examples/cactus-example-cbdc-bridging-backend/tsconfig.json create mode 100644 examples/cactus-example-cbdc-bridging/README.md diff --git a/.cspell.json b/.cspell.json index ea9bf98ec5..d34808c321 100644 --- a/.cspell.json +++ b/.cspell.json @@ -20,6 +20,8 @@ "cafile", "caio", "cccs", + "cbdc", + "Cbdc", "ccid", "cids", "commenceack", @@ -55,6 +57,7 @@ "htlc", "Htlc", "HTLC", + "Hursley", "HyperLedger", "ipaddress", "ipfs", diff --git a/.vscode/template.launch.json b/.vscode/template.launch.json index f03337c6c0..7264b43e2c 100644 --- a/.vscode/template.launch.json +++ b/.vscode/template.launch.json @@ -65,6 +65,31 @@ "${workspaceFolder}/**/*.js", "!**/node_modules/**" ] + }, + { + "name": "Example: CBDC Bridging Fabric-EVM App", + "type": "node", + "request": "launch", + "protocol": "inspector", + "env": { + "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" + }, + "args": [ + "${workspaceFolder}/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app-cli.ts", + "dotenv_config_path=${workspaceFolder}/examples/cactus-example-cbdc-bridging-backend/process.env" + ], + "runtimeArgs": [ + "-r", + "ts-node/register", + "-r", + "dotenv/config" + ], + "console": "integratedTerminal", + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/**/*.js", + "!**/node_modules/**" + ] } ] } \ No newline at end of file diff --git a/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts b/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts index 4d6512bef2..ca2b4729c9 100644 --- a/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts +++ b/examples/cactus-example-carbon-accounting-backend/src/main/typescript/carbon-accounting-app.ts @@ -103,9 +103,13 @@ export class CarbonAccountingApp { const sshConfig = await this.ledgers.fabric.getSshConfig(); const connectionProfile = await this.ledgers.fabric.getConnectionProfileOrg1(); - const enrollAdminOut = await this.ledgers.fabric.enrollAdmin(); + const enrollAdminOut = await this.ledgers.fabric.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await this.ledgers.fabric.enrollUser(adminWallet); + const [userIdentity] = await this.ledgers.fabric.enrollUser( + adminWallet, + "user", + "org1", + ); const keychainEntryKey = "fabric_user2"; const keychainEntryValue = JSON.stringify(userIdentity); await this.keychain.set(keychainEntryKey, keychainEntryValue); diff --git a/examples/cactus-example-cbdc-bridging-backend/CHANGELOG.md b/examples/cactus-example-cbdc-bridging-backend/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/cactus-example-cbdc-bridging-backend/Dockerfile b/examples/cactus-example-cbdc-bridging-backend/Dockerfile new file mode 100644 index 0000000000..0276cab7a8 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/Dockerfile @@ -0,0 +1,69 @@ +FROM cruizba/ubuntu-dind:19.03.11 + +USER root + +RUN apt-get update +RUN apt -y upgrade + +# Need curl for healthchecks +RUN apt-get -y install --no-install-recommends curl + +# The file binary is used to inspect exectubles when debugging container image issues +RUN apt-get -y install --no-install-recommends file + +RUN apt-get -y install --no-install-recommends ca-certificates +RUN apt-get -y install --no-install-recommends tzdata + +RUN apt-get -y install --no-install-recommends git +RUN apt-get -y install --no-install-recommends apt-utils + +RUN apt -y install --no-install-recommends default-jdk + +ARG APP=/usr/src/app/cactus/ +COPY . ${APP} + +ENV TZ=Etc/UTC + +RUN mkdir -p ${APP} + +RUN mkdir -p "${APP}/log/" + +WORKDIR ${APP} + +SHELL ["/bin/bash", "--login", "-i", "-c"] +# Installing Node Version Manager (nvm) +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash + +RUN source ~/.bashrc && \ + nvm install 16.8.0 && \ + npm install -g yarn && \ + npm run configure + +SHELL ["/bin/bash", "--login", "-c"] + +COPY ./examples/cactus-example-cbdc-bridging-backend/healthcheck.sh / + +ENV API_HOST=localhost +ENV API_SERVER_1_PORT=4000 +ENV API_SERVER_2_PORT=4100 +ENV API_HOST_FRONTEND=localhost +ENV API_PORT_FRONTEND=2000 + +COPY examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts /usr/src/app/cactus/examples/cactus-example-cbdc-bridging-backend/dist/lib/fabric-contracts +COPY examples/cactus-example-cbdc-bridging-backend/supervisord.conf /etc/supervisord.conf +COPY examples/cactus-example-cbdc-bridging-backend/run-cbdc-app.sh /run-cbdc-app.sh +RUN chmod +x /run-cbdc-app.sh + +# supervisord web ui/dashboard +EXPOSE 9001 +# API #1 +EXPOSE 4000 +# API #2 +EXPOSE 4100 + +# Extend the parent image's entrypoint +# https://superuser.com/questions/1459466/can-i-add-an-additional-docker-entrypoint-script +ENTRYPOINT ["/usr/bin/supervisord"] +CMD ["--configuration", "/etc/supervisord.conf", "--nodaemon"] +HEALTHCHECK --interval=1s --timeout=5s --start-period=20s --retries=250 \ + CMD /usr/src/app/cactus/examples/cbdc-bridge-app/healthcheck.sh diff --git a/examples/cactus-example-cbdc-bridging-backend/README.md b/examples/cactus-example-cbdc-bridging-backend/README.md new file mode 100644 index 0000000000..c401469412 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/README.md @@ -0,0 +1,38 @@ +# Hyperledger Cactus Example - CBDC Bridging between Fabric and Besu Backend + +## Running the Test Suites + +> Make sure you have all the dependencies set up as explained in `BUILD.md` + +On the terminal, issue the following commands in the project root: + +1. `npm run configure` +2. `npm run start:example-cbdc-bridging-app` + +Wait for the output to show the message `CbdcBridgingApp running...` + +In a second terminal run the following commands from the project root: +3. `cd examples/cactus-example-cbdc-bridging-backend` +4. `npm run test` + +## Running the Example Application Locally + +> Make sure you have all the dependencies set up as explained in `BUILD.md` + +On the terminal, issue the following commands: + +1. `npm run configure` +2. `npm run start:example-cbdc-bridging-app` + +Wait for the output to show the message `CbdcBridgingApp running...` + +## Debugging the Example Application Locally + +On the terminal, issue the following commands (steps 1 to 6) and then perform the rest of the steps manually. + +1. `npm run configure` +2. Locate the `.vscode/template.launch.json` file +3. Within that file locate the entry named `"Example: CBDC Bridging Fabric-EVM App"` +4. Copy the VSCode debug definition object from 2) to your `.vscode/launch.json` file +5. At this point the VSCode `Run and Debug` panel on the left should have an option also titled `"Example: CBDC Bridging Fabric-EVM App"` which starts the application +6. Wait for the output to show the message `CbdcBridgingApp running...` diff --git a/examples/cactus-example-cbdc-bridging-backend/healthcheck.sh b/examples/cactus-example-cbdc-bridging-backend/healthcheck.sh new file mode 100644 index 0000000000..a4f7037da3 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/healthcheck.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +curl -v -i -X OPTIONS http://127.0.0.1:${API_SERVER_1_PORT} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/package.json b/examples/cactus-example-cbdc-bridging-backend/package.json new file mode 100644 index 0000000000..9e28daadda --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/package.json @@ -0,0 +1,116 @@ +{ + "name": "@hyperledger/cactus-example-cbdc-bridging-backend", + "version": "1.1.2", + "description": "An example application showing how to use Cactus when implementing a CBDC bridging application between Hyperledger Fabric and Hyperledger Besu.", + "keywords": [ + "Hyperledger", + "Cactus", + "ODAP", + "Fabric", + "Besu", + "Blockchain", + "CBDC" + ], + "homepage": "https://github.com/hyperledger/cactus#readme", + "bugs": { + "url": "https://github.com/hyperledger/cactus/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cactus.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Hyperledger Cactus Contributors", + "email": "cactus@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cactus" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "André Augusto", + "email": "andre.augusto@tecnico.ulisboa.pt" + } + ], + "main": "dist/lib/main/typescript/index.js", + "module": "dist/lib/main/typescript/index.js", + "browser": "dist/cactus-example-cbdc-bridging-backend.web.umd.js", + "types": "dist/lib/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "solidity": "hardhat compile", + "start": "CONFIG_FILE=./example-config.json node dist/lib/main/typescript/cbdc-bridging-app-cli.js", + "watch": "npm-watch", + "test": "nyc cucumber-js ./src/test/typescript/cucumber/features/*.feature --require-module ts-node/register --require './src/test/typescript/cucumber/*/*.ts'", + "webpack": "npm-run-all webpack:dev", + "webpack:dev": "npm-run-all webpack:dev:node webpack:dev:web", + "webpack:dev:node": "webpack --env=dev --target=node --config ../../webpack.config.js", + "webpack:dev:web": "webpack --env=dev --target=web --config ../../webpack.config.js", + "build:dev:backend:postbuild": "mkdir ./dist/lib/fabric-contracts && cp -r ./src/fabric-contracts/* ./dist/lib/fabric-contracts/" + }, + "dependencies": { + "@hyperledger/cactus-api-client": "1.1.2", + "@hyperledger/cactus-cmd-api-server": "1.1.2", + "@hyperledger/cactus-common": "1.1.2", + "@hyperledger/cactus-core": "1.1.2", + "@hyperledger/cactus-core-api": "1.1.2", + "@hyperledger/cactus-plugin-keychain-memory": "1.1.2", + "@hyperledger/cactus-plugin-ledger-connector-fabric": "1.1.2", + "@hyperledger/cactus-plugin-ledger-connector-xdai": "1.1.2", + "@hyperledger/cactus-plugin-odap-hermes": "1.1.2", + "@hyperledger/cactus-test-tooling": "1.1.2", + "@openzeppelin/contracts": "4.4.2", + "@openzeppelin/contracts-upgradeable": "4.4.2", + "async-exit-hook": "2.0.1", + "axios": "^0.27.2", + "dotenv": "^16.0.1", + "fabric-network": "2.2.10", + "fs-extra": "10.0.0", + "nyc": "^13.1.0", + "openapi-types": "9.1.0", + "sqlite3": "^5.0.8", + "typescript-optional": "2.0.1", + "uuid": "8.3.2", + "web3-core": "1.5.2", + "web3-utils": "1.5.2" + }, + "devDependencies": { + "@types/chai": "^4.3.1", + "@types/cucumber": "^4.0.4", + "@types/express": "4.17.13", + "@types/express-jwt": "6.0.2", + "@types/fs-extra": "9.0.12", + "@types/node": "^10.7.1", + "@types/uuid": "8.3.1", + "chai": "^4.1.2", + "cucumber": "^5.0.3", + "hardhat": "2.6.0", + "http-status-codes": "2.1.4", + "jose": "4.1.0", + "remix-tests": "^0.1.34", + "ts-node": "^7.0.1" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "publishConfig": { + "access": "public" + }, + "browserMinified": "dist/cactus-example-cbdc-bridging-backend.web.umd.min.js", + "mainMinified": "dist/cactus-example-cbdc-bridging-backend.node.umd.min.js", + "watch": { + "solidity": { + "patterns": [ + "./src/main/solidity/" + ], + "extensions": "sol" + } + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/process.env b/examples/cactus-example-cbdc-bridging-backend/process.env new file mode 100644 index 0000000000..b224de9957 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/process.env @@ -0,0 +1,5 @@ +API_HOST=localhost +API_SERVER_1_PORT=4000 +API_SERVER_2_PORT=4100 +API_HOST_FRONTEND=localhost +API_PORT_FRONTEND=2000 \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/run-cbdc-app.sh b/examples/cactus-example-cbdc-bridging-backend/run-cbdc-app.sh new file mode 100755 index 0000000000..5eba756f93 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/run-cbdc-app.sh @@ -0,0 +1,14 @@ +#!/bin/bash +function main() +{ + echo "Sleeping to let dockerd spin up" + sleep 10 + + docker pull ghcr.io/hyperledger/cactus-besu-all-in-one:2021-01-08-7a055c3 + docker pull ghcr.io/hyperledger/cactus-fabric-all-in-one:v1.0.0-rc.2 + docker pull ipfs/go-ipfs:v0.8.0 + + /root/.nvm/versions/node/v16.8.0/bin/node -r ts-node/register /usr/src/app/cactus/examples/cactus-example-cbdc-bridging-backend/dist/lib/main/typescript/cbdc-bridging-app-cli.js +} + +main diff --git a/examples/cactus-example-cbdc-bridging-backend/src/crypto-material/crypto-material.json b/examples/cactus-example-cbdc-bridging-backend/src/crypto-material/crypto-material.json new file mode 100644 index 0000000000..76ee636dfa --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/crypto-material/crypto-material.json @@ -0,0 +1,40 @@ +{ + "info": { + "description": "this file contains data to be used for testing purposes only. It should not be changed. It should remain consistent with the crypto material in the fabric chaincode" + }, + "accounts": { + "userA": { + "ethAddress": "0x1A86D6f4b5D30A07D1a94bb232eF916AFe5DbDbc", + "privateKey": "0xb47c3ba5a816dbbb2271db721e76e6c80e58fe54972d26a42f00bc97a92a2535", + "fabricID": "x509::/OU=client/OU=org1/OU=department1/CN=userA::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com" + }, + "bridge": { + "ethAddress": "0xf28d5769171bfbD2B3628d722e58129a6aE15022", + "privateKey": "0x2917bd3b0dced512d3555f171a0979fedd37bb0593883089396d9b893aa0505d", + "fabricID": "x509::/OU=client/OU=org2/OU=department1/CN=bridge::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com" + }, + "userB": { + "ethAddress": "0xB264c626D7D09789AbfD2a431A28a054366e7b63", + "privateKey": "0xfe95d40663e60e9a8a2e1f4153c9e207ac26d928e8a5631647735d91e93be402", + "fabricID": "x509::/OU=client/OU=org1/OU=department1/CN=userB::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com" + } + }, + "gateways": { + "gateway1": { + "publicKey": "02e2beaa887c53d0ddb8383e2c49553545f50ae19074b4c7f79238dcb59f4b5fcb", + "privateKey": "206021a8ffaba949d21e32c02d76d2dc13da25e8197532ab300bb49d91397e52" + }, + "gateway2": { + "publicKey": "02309ff54e58748616aa81245c1a4457f11fab47dcdb45b107dbcde24ee772b609", + "privateKey": "6bb4c730739969ff3429be71dd62d05459e61bc3f2956326449ae2ca7de50fb6" + } + }, + "keychains": { + "keychain1": { + "id": "df05d3c2-ddd5-4074-aae3-526564217459" + }, + "keychain2": { + "id": "d741fb4a-c95c-4205-8afa-758a9423234f" + } + } +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/.gitignore b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/.gitignore new file mode 100644 index 0000000000..79bfe1a3e0 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/.gitignore @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# Compiled TypeScript files +dist + diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/package.json b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/package.json new file mode 100644 index 0000000000..7bb68c8fdb --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/package.json @@ -0,0 +1,64 @@ +{ + "name": "asset-reference-contract", + "version": "1.0.0", + "description": "Asset Reference contract implemented in TypeScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "start": "fabric-chaincode-node start", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.4.1", + "fabric-shim": "2.4.1" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/chai-as-promised": "^7.1.2", + "@types/mocha": "^7.0.2", + "@types/node": "^13.9.3", + "@types/sinon": "^7.5.2", + "@types/sinon-chai": "^3.2.3", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "mocha": "^9.2.0", + "nyc": "^15.0.0", + "sinon": "^9.0.1", + "sinon-chai": "^3.5.0", + "ts-node": "^8.8.1", + "tslint": "^6.1.0", + "typescript": "^3.8.3", + "winston": "^3.2.1" + }, + "nyc": { + "extension": [ + ".ts", + ".tsx" + ], + "exclude": [ + "coverage/**", + "dist/**" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.spec.ts b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.spec.ts new file mode 100644 index 0000000000..a9935a6e93 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.spec.ts @@ -0,0 +1,295 @@ +/* + * SPDX-License-Identifier: Apache 2.0 + */ + +import { Context } from "fabric-contract-api"; +import { ChaincodeStub, ClientIdentity } from "fabric-shim"; +import { AssetReferenceContract } from "."; + +import * as winston from "winston"; +import * as chai from "chai"; +import * as chaiAsPromised from "chai-as-promised"; +import * as sinon from "sinon"; +import * as sinonChai from "sinon-chai"; + +const bridgedOutAmountKey = "amountBridgedOut"; + +const USER_A_FABRIC_ID = + "x509::/OU=client/OU=org1/OU=department1/CN=userA::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"; + +const USER_A_ETH_ADDRESS = + "x509::/OU=client/OU=org1/OU=department1/CN=userA::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"; + +chai.should(); +chai.use(chaiAsPromised); +chai.use(sinonChai); + +class TestContext extends Context { + public stub: sinon.SinonStubbedInstance< + ChaincodeStub + > = sinon.createStubInstance(ChaincodeStub); + public clientIdentity: sinon.SinonStubbedInstance< + ClientIdentity + > = sinon.createStubInstance(ClientIdentity); + public logging = { + getLogger: sinon + .stub() + .returns(sinon.createStubInstance(winston.createLogger().constructor)), + setLevel: sinon.stub(), + }; +} + +describe("AssetReference", () => { + let contract: AssetReferenceContract; + let ctx: TestContext; + + beforeEach(() => { + contract = new AssetReferenceContract(); + ctx = new TestContext(); + ctx.clientIdentity.getMSPID.resolves("Org2MSP"); + ctx.stub.getState.withArgs(bridgedOutAmountKey).resolves(Buffer.from("50")); + ctx.stub.getState + .withArgs("1001") + .resolves( + Buffer.from( + `{"id":"1001","isLocked":false,"numberTokens":10,"recipient":"${USER_A_FABRIC_ID}"}`, + ), + ); + ctx.stub.getState + .withArgs("1002") + .resolves( + Buffer.from( + `{"id":"1002","isLocked":true,"numberTokens":30,"recipient":"${USER_A_FABRIC_ID}"}`, + ), + ); + }); + + describe("#assetReferenceExists", () => { + it("should return true for an asset reference", async () => { + await contract.AssetReferenceExists(ctx, "1001").should.eventually.be + .true; + }); + + it("should return false for an asset reference that does not exist", async () => { + await contract.AssetReferenceExists(ctx, "1003").should.eventually.be + .false; + }); + }); + + describe("#createAssetReference", () => { + it("should create an asset reference", async () => { + await contract.CreateAssetReference(ctx, "1003", 100, USER_A_FABRIC_ID); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + "1003", + Buffer.from( + `{"id":"1003","isLocked":false,"numberTokens":100,"recipient":"${USER_A_FABRIC_ID}"}`, + ), + ); + }); + + it("should throw an error for an asset reference that already exists", async () => { + await contract + .CreateAssetReference(ctx, "1001", 100, USER_A_FABRIC_ID) + .should.be.rejectedWith( + /The asset reference with ID 1001 already exists/, + ); + }); + }); + + describe("#readAssetReference", () => { + it("should return an asset reference", async () => { + await contract + .ReadAssetReference(ctx, "1001") + .should.eventually.deep.equal({ + id: "1001", + isLocked: false, + numberTokens: 10, + recipient: USER_A_FABRIC_ID, + }); + }); + + it("should throw an error for an asset reference that does not exist", async () => { + await contract + .ReadAssetReference(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + }); + + describe("#IsAssetReferenceLocked", () => { + it("should return true for a locked asset reference", async () => { + const result1 = await contract.IsAssetReferenceLocked(ctx, "1001"); + const result2 = await contract.IsAssetReferenceLocked(ctx, "1002"); + chai.expect(result1).to.be.false; + chai.expect(result2).to.be.true; + + await contract + .IsAssetReferenceLocked(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + }); + + describe("#lockAssetReference", () => { + it("should lock an asset reference", async () => { + await contract.LockAssetReference(ctx, "1001"); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + "1001", + Buffer.from( + `{"id":"1001","isLocked":true,"numberTokens":10,"recipient":"${USER_A_FABRIC_ID}"}`, + ), + ); + }); + + it("should throw an error for an asset reference that does not exist", async () => { + await contract + .LockAssetReference(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + + it("should throw an error for an asset reference already locked", async () => { + await contract + .LockAssetReference(ctx, "1002") + .should.be.rejectedWith(/The asset reference 1002 is already locked/); + }); + }); + + describe("#unlockAssetReference", () => { + it("should unlock an asset reference", async () => { + await contract.UnlockAssetReference(ctx, "1002"); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + "1002", + Buffer.from( + `{"id":"1002","isLocked":false,"numberTokens":30,"recipient":"${USER_A_FABRIC_ID}"}`, + ), + ); + }); + + it("should throw an error for an asset reference that does not exist", async () => { + await contract + .UnlockAssetReference(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + }); + + describe("#deleteAssetReference", () => { + it("should delete an asset reference", async () => { + await contract.DeleteAssetReference(ctx, "1001"); + ctx.stub.deleteState.should.have.been.calledOnceWithExactly("1001"); + }); + + it("should throw an error for an asset reference that does not exist", async () => { + await contract + .DeleteAssetReference(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + }); + + describe("#GetBridgedOutAmount", () => { + it("should increase bridged out amount", async () => { + await contract.IncreaseBridgedAmount(ctx, 1001); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + bridgedOutAmountKey, + Buffer.from("1051"), + ); + }); + + it("should decrease bridged out amount", async () => { + await contract.DecreaseBridgedAmount(ctx, 10); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + bridgedOutAmountKey, + Buffer.from("40"), + ); + }); + + it("should thrown an error trying to decrease bridged out", async () => { + await contract + .DecreaseBridgedAmount(ctx, 100) + .should.be.rejectedWith(/Bridged back too many tokens/); + }); + + it("should throw an error for an asset reference that does not exist", async () => { + await contract + .DeleteAssetReference(ctx, "1003") + .should.be.rejectedWith(/The asset reference 1003 does not exist/); + }); + }); + + describe("#Refund", () => { + it("should decrease bridged out amount", async () => { + await contract.Refund(ctx, 20, USER_A_FABRIC_ID, USER_A_ETH_ADDRESS); + ctx.stub.putState.should.have.been.calledOnceWithExactly( + bridgedOutAmountKey, + Buffer.from("30"), + ); + }); + }); + + describe("#checkValidTransfer", () => { + const assetID = "1001"; + const amount1 = "10"; + const amount2 = "500"; + + it("should be a valid transfer bridging out CBDC to own address", async () => { + await contract.CheckValidBridgeOut( + ctx, + assetID, + amount1, + USER_A_FABRIC_ID, + USER_A_ETH_ADDRESS, + ).should.not.be.rejected; + }); + + it("should throw an error transfer CBDC escrowed by another user", async () => { + await contract + .CheckValidBridgeOut( + ctx, + assetID, + amount1, + "USER_B_FABRIC_ID", + USER_A_ETH_ADDRESS, + ) + .should.be.rejectedWith( + /it is not possible to transfer tokens escrowed by another user/, + ); + }); + + it("should throw an error bridging out more than the escrowed CBDC", async () => { + await contract + .CheckValidBridgeOut( + ctx, + assetID, + amount2, + USER_A_FABRIC_ID, + USER_A_ETH_ADDRESS, + ) + .should.be.rejectedWith( + /it is not possible to transfer a different amount of CBDC than the ones escrowed/, + ); + }); + }); + + describe("#operations", () => { + const number1 = 10; + const number2 = 500; + + it("add two numbers", () => { + const result = contract.add(number1, number2); + chai.expect(result).to.equal(number1 + number2); + }); + + it("subtract two numbers", () => { + const result = contract.sub(number2, number1); + chai.expect(result).to.equal(number2 - number1); + }); + }); + + describe("#checkPermission", () => { + it("user from organization other than Org2 is not authorized to perform operations", () => { + ctx.clientIdentity.getMSPID.resolves("Org1MSP"); + contract + .LockAssetReference(ctx, "1001") + .should.be.rejectedWith( + `client is not authorized to perform the operation. Org1MSP != "Org2MSP"`, + ); + }); + }); +}); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.ts b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.ts new file mode 100644 index 0000000000..3a1f1d6904 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference-contract.ts @@ -0,0 +1,332 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + Context, + Contract, + Info, + Returns, + Transaction, +} from "fabric-contract-api"; +import { AssetReference } from "./asset-reference"; + +const bridgedOutAmountKey = "amountBridgedOut"; + +@Info({ + title: "AssetReferenceContract", + description: "Smart contract for trading assets", +}) +export class AssetReferenceContract extends Contract { + // AssetReferenceExists returns true when asset with given ID exists in world state. + @Transaction(false) + @Returns("boolean") + public async AssetReferenceExists( + ctx: Context, + id: string, + ): Promise { + const assetJSON = await ctx.stub.getState(id); + return !!assetJSON && assetJSON.length > 0; + } + + // IsAssetReferenceLocked returns true when asset with given ID is locked in world state. + @Transaction(false) + @Returns("boolean") + public async IsAssetReferenceLocked( + ctx: Context, + id: string, + ): Promise { + const assetJSON = await ctx.stub.getState(id); + + if (assetJSON && assetJSON.length > 0) { + const asset = JSON.parse(assetJSON.toString()); + return asset.isLocked; + } else { + throw new Error(`The asset reference ${id} does not exist`); + } + } + + // CreateAssetReference issues a new asset to the world state with given details. + @Transaction() + public async CreateAssetReference( + ctx: Context, + assetId: string, + numberTokens: number, + recipient: string, + ): Promise { + console.log( + "Creating new asset reference with id: " + + assetId + + " representing " + + numberTokens + + " tokens", + ); + const exists: boolean = await this.AssetReferenceExists(ctx, assetId); + if (exists) { + throw new Error(`The asset reference with ID ${assetId} already exists`); + } + + const asset: AssetReference = { + id: assetId, + isLocked: false, + numberTokens: numberTokens, + recipient: recipient, + }; + + const buffer: Buffer = Buffer.from(JSON.stringify(asset)); + await ctx.stub.putState(assetId, buffer); + } + + @Transaction() + public async Refund( + ctx: Context, + numberTokens: number, + finalFabricIdentity: string, + ethAddress: string, + ): Promise { + await this.CheckPermission(ctx); + + console.log( + "Calling refund tokens to " + finalFabricIdentity + " from " + ethAddress, + ); + await ctx.stub.invokeChaincode( + "cbdc", + ["Refund", finalFabricIdentity, numberTokens.toString(), ethAddress], + ctx.stub.getChannelID(), + ); + + await this.DecreaseBridgedAmount(ctx, numberTokens); + } + + @Transaction(false) + @Returns("AssetReference") + public async ReadAssetReference( + ctx: Context, + assetId: string, + ): Promise { + const exists: boolean = await this.AssetReferenceExists(ctx, assetId); + if (!exists) { + throw new Error(`The asset reference ${assetId} does not exist`); + } + const data: Uint8Array = await ctx.stub.getState(assetId); + const asset: AssetReference = JSON.parse(data.toString()) as AssetReference; + return asset; + } + + @Transaction() + public async LockAssetReference( + ctx: Context, + assetId: string, + ): Promise { + await this.CheckPermission(ctx); + + const exists: boolean = await this.AssetReferenceExists(ctx, assetId); + if (!exists) { + throw new Error(`The asset reference ${assetId} does not exist`); + } + + if (await this.IsAssetReferenceLocked(ctx, assetId)) { + throw new Error(`The asset reference ${assetId} is already locked`); + } + + const asset: AssetReference = await this.ReadAssetReference(ctx, assetId); + asset.isLocked = true; + const buffer: Buffer = Buffer.from(JSON.stringify(asset)); + + console.log("Locking asset reference with id: " + assetId); + await ctx.stub.putState(assetId, buffer); + } + + @Transaction(false) + public async CheckValidBridgeOut( + ctx: Context, + assetId: string, + amount: string, + fabricID: string, + ethAddress: string, + ): Promise { + // check if this transfer is allowed + const asset: AssetReference = await this.ReadAssetReference(ctx, assetId); + + if (asset.recipient != fabricID) { + throw new Error( + `it is not possible to transfer tokens escrowed by another user`, + ); + } + + if (asset.numberTokens != parseInt(amount)) { + throw new Error( + `it is not possible to transfer a different amount of CBDC than the ones escrowed`, + ); + } + + await ctx.stub.invokeChaincode( + "cbdc", + ["checkAddressMapping", fabricID, ethAddress], + ctx.stub.getChannelID(), + ); + } + + @Transaction(false) + public async CheckValidBridgeBack( + ctx: Context, + fabricID: string, + ethAddress: string, + ): Promise { + await ctx.stub.invokeChaincode( + "cbdc", + ["checkAddressMapping", fabricID, ethAddress], + ctx.stub.getChannelID(), + ); + } + + @Transaction() + public async UnlockAssetReference( + ctx: Context, + assetId: string, + ): Promise { + await this.CheckPermission(ctx); + + const exists: boolean = await this.AssetReferenceExists(ctx, assetId); + if (!exists) { + throw new Error(`The asset reference ${assetId} does not exist`); + } + + const asset: AssetReference = await this.ReadAssetReference(ctx, assetId); + asset.isLocked = false; + const buffer: Buffer = Buffer.from(JSON.stringify(asset)); + + console.log("Unlocking asset reference with id: " + assetId); + await ctx.stub.putState(assetId, buffer); + } + + @Transaction() + public async DeleteAssetReference( + ctx: Context, + assetId: string, + ): Promise { + await this.CheckPermission(ctx); + + const exists: boolean = await this.AssetReferenceExists(ctx, assetId); + if (!exists) { + throw new Error(`The asset reference ${assetId} does not exist`); + } + const asset = await this.ReadAssetReference(ctx, assetId); + await this.IncreaseBridgedAmount(ctx, asset.numberTokens); + + console.log("Deleting asset reference with id: " + assetId); + await ctx.stub.deleteState(assetId); + } + + @Transaction(false) + public async GetBridgedOutAmount(ctx: Context): Promise { + const amountBytes = await ctx.stub.getState(bridgedOutAmountKey); + + let amountValue; + // If value doesn't yet exist, we'll create it with a value of 0 + if (!amountBytes || amountBytes.length === 0) { + amountValue = 0; + } else { + amountValue = parseInt(amountBytes.toString()); + } + + return amountValue; + } + + @Transaction() + public async IncreaseBridgedAmount( + ctx: Context, + value: number, + ): Promise { + await this.CheckPermission(ctx); + + const newBalance = this.add(await this.GetBridgedOutAmount(ctx), value); + await ctx.stub.putState( + bridgedOutAmountKey, + Buffer.from(newBalance.toString()), + ); + } + + @Transaction() + public async DecreaseBridgedAmount( + ctx: Context, + value: number, + ): Promise { + await this.CheckPermission(ctx); + + const newBalance = this.sub(await this.GetBridgedOutAmount(ctx), value); + + if (newBalance < 0) { + throw new Error(`Bridged back too many tokens`); + } + await ctx.stub.putState( + bridgedOutAmountKey, + Buffer.from(newBalance.toString()), + ); + } + + // GetAllAssets returns all assets found in the world state. + @Transaction(false) + public async GetAllAssetReferences(ctx: Context): Promise { + const allResults = []; + // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. + const iterator = await ctx.stub.getStateByRange("", ""); + let result = await iterator.next(); + while (!result.done) { + const strValue = Buffer.from(result.value.value.toString()).toString( + "utf8", + ); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push(record); + result = await iterator.next(); + } + return JSON.stringify(allResults); + } + + @Transaction() + public async ResetState(ctx: Context): Promise { + const iterator = await ctx.stub.getStateByRange("", ""); + let result = await iterator.next(); + while (!result.done) { + console.log(result.value); + await ctx.stub.putState(result.value.key, undefined); + result = await iterator.next(); + } + + await ctx.stub.putState(bridgedOutAmountKey, Buffer.from("0")); + } + + // add two number checking for overflow + add(a, b) { + const c = a + b; + if (a !== c - b || b !== c - a) { + throw new Error(`Math: addition overflow occurred ${a} + ${b}`); + } + return c; + } + + // add two number checking for overflow + sub(a, b) { + const c = a - b; + if (a !== c + b || b !== a - c) { + throw new Error(`Math: subtraction overflow occurred ${a} - ${b}`); + } + return c; + } + + private async CheckPermission(ctx: Context) { + // this needs to be called by entity2 (the bridging entity) + const clientMSPID = await ctx.clientIdentity.getMSPID(); + if (clientMSPID !== "Org2MSP") { + throw new Error( + `client is not authorized to perform the operation. ${clientMSPID} != "Org2MSP"`, + ); + } + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference.ts b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference.ts new file mode 100644 index 0000000000..cab2df353e --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/asset-reference.ts @@ -0,0 +1,20 @@ +/* + SPDX-License-Identifier: Apache-2.0 +*/ + +import { Object, Property } from "fabric-contract-api"; + +@Object() +export class AssetReference { + @Property() + public id: string; + + @Property() + public isLocked: boolean; + + @Property() + public numberTokens: number; + + @Property() + public recipient: string; +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/index.ts b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/index.ts new file mode 100644 index 0000000000..97998610fb --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/src/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AssetReferenceContract } from "./asset-reference-contract"; + +export { AssetReferenceContract } from "./asset-reference-contract"; + +export const contracts: any[] = [AssetReferenceContract]; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/tsconfig.json b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/tsconfig.json new file mode 100644 index 0000000000..e3af138df0 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/asset-reference/typescript/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true, + }, + "include": [ + "./src/**/*", + "src/**/*.json" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.eslintignore b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.eslintignore new file mode 100644 index 0000000000..1595847010 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.eslintignore @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +coverage diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.gitignore b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.gitignore new file mode 100644 index 0000000000..c84ff1dbdd --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/.gitignore @@ -0,0 +1,78 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/crypto-material/crypto-material.json b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/crypto-material/crypto-material.json new file mode 100644 index 0000000000..2495d48b65 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/crypto-material/crypto-material.json @@ -0,0 +1,22 @@ +{ + "info": { + "description": "this file contains data to be used for testing purposes only. It should not be changed. It should remain consistent with the crypto material in the fabric chaincode" + }, + "accounts": { + "userA": { + "ethAddress": "0x1A86D6f4b5D30A07D1a94bb232eF916AFe5DbDbc", + "privateKey": "0xb47c3ba5a816dbbb2271db721e76e6c80e58fe54972d26a42f00bc97a92a2535", + "fabricID": "x509::/OU=client/OU=org1/OU=department1/CN=userA::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com" + }, + "bridge": { + "ethAddress": "0xf28d5769171bfbD2B3628d722e58129a6aE15022", + "privateKey": "0x2917bd3b0dced512d3555f171a0979fedd37bb0593883089396d9b893aa0505d", + "fabricID": "x509::/OU=client/OU=org2/OU=department1/CN=bridge::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com" + }, + "userB": { + "ethAddress": "0xB264c626D7D09789AbfD2a431A28a054366e7b63", + "privateKey": "0xfe95d40663e60e9a8a2e1f4153c9e207ac26d928e8a5631647735d91e93be402", + "fabricID": "x509::/OU=client/OU=org1/OU=department1/CN=userB::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com" + } + } +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/index.js b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/index.js new file mode 100644 index 0000000000..2c63fa92a5 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/index.js @@ -0,0 +1,12 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +"use strict"; + +const tokenERC20Contract = require("./lib/tokenERC20.js"); + +module.exports.TokenERC20Contract = tokenERC20Contract; +module.exports.contracts = [tokenERC20Contract]; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/lib/tokenERC20.js b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/lib/tokenERC20.js new file mode 100644 index 0000000000..869d3acd3b --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/lib/tokenERC20.js @@ -0,0 +1,690 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +"use strict"; + +const { Contract } = require("fabric-contract-api"); +const CryptoMaterial = require("../crypto-material/crypto-material.json"); + +const accounts = [ + { + fabric: CryptoMaterial.accounts.userA.fabricID, + ethereum: CryptoMaterial.accounts.userA.ethAddress, + }, + { + fabric: CryptoMaterial.accounts.userB.fabricID, + ethereum: CryptoMaterial.accounts.userB.ethAddress, + }, + { + fabric: CryptoMaterial.accounts.bridge.fabricID, + ethereum: CryptoMaterial.accounts.bridge.ethAddress, + }, +]; + +const FABRIC_BRIDGE_IDENTITY = CryptoMaterial.accounts.bridge.fabricID; + +// Define objectType names for prefix +const balancePrefix = "balance"; +const allowancePrefix = "allowance"; +const addressPrefix = "address"; + +// Define key names for options +const nameKey = "name"; +const symbolKey = "symbol"; +const decimalsKey = "decimals"; +const totalSupplyKey = "totalSupply"; + +class TokenERC20Contract extends Contract { + /** + * Return the name of the token - e.g. "MyToken". + * The original function name is `name` in ERC20 specification. + * However, 'name' conflicts with a parameter `name` in `Contract` class. + * As a work around, we use `TokenName` as an alternative function name. + * + * @param {Context} ctx the transaction context + * @returns {String} Returns the name of the token + */ + async TokenName(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const nameBytes = await ctx.stub.getState(nameKey); + + return nameBytes.toString(); + } + + /** + * Return the symbol of the token. E.g. “HIX”. + * + * @param {Context} ctx the transaction context + * @returns {String} Returns the symbol of the token + */ + async Symbol(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const symbolBytes = await ctx.stub.getState(symbolKey); + return symbolBytes.toString(); + } + + /** + * Return the number of decimals the token uses + * e.g. 8, means to divide the token amount by 100000000 to get its user representation. + * + * @param {Context} ctx the transaction context + * @returns {Number} Returns the number of decimals + */ + async Decimals(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const decimalsBytes = await ctx.stub.getState(decimalsKey); + const decimals = parseInt(decimalsBytes.toString()); + return decimals; + } + + /** + * Return the total token supply. + * + * @param {Context} ctx the transaction context + * @returns {Number} Returns the total token supply + */ + async TotalSupply(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const totalSupplyBytes = await ctx.stub.getState(totalSupplyKey); + const totalSupply = parseInt(totalSupplyBytes.toString()); + return totalSupply; + } + + /** + * BalanceOf returns the balance of the given account. + * + * @param {Context} ctx the transaction context + * @param {String} owner The owner from which the balance will be retrieved + * @returns {Number} Returns the account balance + */ + async BalanceOf(ctx, owner) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [owner]); + + const balanceBytes = await ctx.stub.getState(balanceKey); + if (!balanceBytes || balanceBytes.length === 0) { + throw new Error(`the account ${owner} does not exist`); + } + const balance = parseInt(balanceBytes.toString()); + + return balance; + } + + /** + * Transfer transfers tokens from client account to recipient account. + * recipient account must be a valid clientID as returned by the ClientAccountID() function. + * + * @param {Context} ctx the transaction context + * @param {String} to The recipient + * @param {Integer} value The amount of token to be transferred + * @returns {Boolean} Return whether the transfer was successful or not + */ + async Transfer(ctx, to, value) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const from = ctx.clientIdentity.getID(); + + const transferResp = await this._transfer(ctx, from, to, value); + if (!transferResp) { + throw new Error("Failed to transfer"); + } + + // Emit the Transfer event + const transferEvent = { from, to, value: parseInt(value) }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + + return true; + } + + /** + * Transfer `value` amount of tokens from `from` to `to`. + * + * @param {Context} ctx the transaction context + * @param {String} from The sender + * @param {String} to The recipient + * @param {Integer} value The amount of token to be transferred + * @returns {Boolean} Return whether the transfer was successful or not + */ + async TransferFrom(ctx, from, to, value) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const spender = ctx.clientIdentity.getID(); + + // Retrieve the allowance of the spender + const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [ + from, + spender, + ]); + const currentAllowanceBytes = await ctx.stub.getState(allowanceKey); + + if (!currentAllowanceBytes || currentAllowanceBytes.length === 0) { + throw new Error(`spender ${spender} has no allowance from ${from}`); + } + + const currentAllowance = parseInt(currentAllowanceBytes.toString()); + + // Convert value from string to int + const valueInt = parseInt(value); + + // Check if the transferred value is less than the allowance + if (currentAllowance < valueInt) { + throw new Error("The spender does not have enough allowance to spend."); + } + + const transferResp = await this._transfer(ctx, from, to, value); + if (!transferResp) { + throw new Error("Failed to transfer"); + } + + // Decrease the allowance + const updatedAllowance = this.sub(currentAllowance, valueInt); + await ctx.stub.putState( + allowanceKey, + Buffer.from(updatedAllowance.toString()), + ); + console.log( + `spender ${spender} allowance updated from ${currentAllowance} to ${updatedAllowance}`, + ); + + // Emit the Transfer event + const transferEvent = { from, to, value: valueInt }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + + console.log("transferFrom ended successfully"); + return true; + } + + async _transfer(ctx, from, to, value) { + if (from === to) { + throw new Error( + `cannot transfer to and from same client account: ${from}`, + ); + } + + // Convert value from string to int + const valueInt = parseInt(value); + + if (valueInt < 0) { + // transfer of 0 is allowed in ERC20, so just validate against negative amounts + throw new Error("transfer amount cannot be negative"); + } + + // Retrieve the current balance of the sender + const fromBalanceKey = ctx.stub.createCompositeKey(balancePrefix, [from]); + const fromCurrentBalanceBytes = await ctx.stub.getState(fromBalanceKey); + + if (!fromCurrentBalanceBytes || fromCurrentBalanceBytes.length === 0) { + throw new Error(`client account ${from} has no balance`); + } + + const fromCurrentBalance = parseInt(fromCurrentBalanceBytes.toString()); + + // Check if the sender has enough tokens to spend. + if (fromCurrentBalance < valueInt) { + throw new Error(`client account ${from} has insufficient funds.`); + } + + // Retrieve the current balance of the recipient + const toBalanceKey = ctx.stub.createCompositeKey(balancePrefix, [to]); + const toCurrentBalanceBytes = await ctx.stub.getState(toBalanceKey); + + let toCurrentBalance; + // If recipient current balance doesn't yet exist, we'll create it with a current balance of 0 + if (!toCurrentBalanceBytes || toCurrentBalanceBytes.length === 0) { + toCurrentBalance = 0; + } else { + toCurrentBalance = parseInt(toCurrentBalanceBytes.toString()); + } + + // Update the balance + const fromUpdatedBalance = this.sub(fromCurrentBalance, valueInt); + const toUpdatedBalance = this.add(toCurrentBalance, valueInt); + + await ctx.stub.putState( + fromBalanceKey, + Buffer.from(fromUpdatedBalance.toString()), + ); + await ctx.stub.putState( + toBalanceKey, + Buffer.from(toUpdatedBalance.toString()), + ); + + console.log( + `client ${from} balance updated from ${fromCurrentBalance} to ${fromUpdatedBalance}`, + ); + console.log( + `recipient ${to} balance updated from ${toCurrentBalance} to ${toUpdatedBalance}`, + ); + + return true; + } + + /** + * Allows `spender` to spend `value` amount of tokens from the owner. + * + * @param {Context} ctx the transaction context + * @param {String} spender The spender + * @param {Integer} value The amount of tokens to be approved for transfer + * @returns {Boolean} Return whether the approval was successful or not + */ + async Approve(ctx, spender, value) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const owner = ctx.clientIdentity.getID(); + + const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [ + owner, + spender, + ]); + + let valueInt = parseInt(value); + await ctx.stub.putState(allowanceKey, Buffer.from(valueInt.toString())); + + // Emit the Approval event + const approvalEvent = { owner, spender, value: valueInt }; + ctx.stub.setEvent("Approval", Buffer.from(JSON.stringify(approvalEvent))); + + console.log("approve ended successfully"); + return true; + } + + /** + * Returns the amount of tokens which `spender` is allowed to withdraw from `owner`. + * + * @param {Context} ctx the transaction context + * @param {String} owner The owner of tokens + * @param {String} spender The spender who are able to transfer the tokens + * @returns {Number} Return the amount of remaining tokens allowed to spent + */ + async Allowance(ctx, owner, spender) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const allowanceKey = ctx.stub.createCompositeKey(allowancePrefix, [ + owner, + spender, + ]); + + const allowanceBytes = await ctx.stub.getState(allowanceKey); + if (!allowanceBytes || allowanceBytes.length === 0) { + throw new Error(`spender ${spender} has no allowance from ${owner}`); + } + + const allowance = parseInt(allowanceBytes.toString()); + return allowance; + } + + // ================== Extended Functions ========================== + + /** + * Set optional infomation for a token. + * + * @param {Context} ctx the transaction context + * @param {String} name The name of the token + * @param {String} symbol The symbol of the token + * @param {String} decimals The decimals of the token + * @param {String} totalSupply The totalSupply of the token + */ + async Initialize(ctx, name, symbol, decimals) { + // Check minter authorization - this sample assumes Org1 is the central banker with privilege to set Options for these tokens + const clientMSPID = ctx.clientIdentity.getMSPID(); + if (clientMSPID !== "Org1MSP") { + throw new Error("client is not authorized to initialize contract"); + } + + //check contract options are not already set, client is not authorized to change them once initialized + const nameBytes = await ctx.stub.getState(nameKey); + if (nameBytes && nameBytes.length > 0) { + throw new Error( + "contract options are already set, client is not authorized to change them", + ); + } + + await ctx.stub.putState(nameKey, Buffer.from(name)); + await ctx.stub.putState(symbolKey, Buffer.from(symbol)); + await ctx.stub.putState(decimalsKey, Buffer.from(decimals)); + + console.log(`name: ${name}, symbol: ${symbol}, decimals: ${decimals}`); + + await this.initializeAddressMapping(ctx); + return true; + } + + /** + * Mint creates new tokens and adds them to minter's account balance + * + * @param {Context} ctx the transaction context + * @param {Integer} amount amount of tokens to be minted + * @returns {Object} The balance + */ + async Mint(ctx, amount) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + // Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens + const clientMSPID = ctx.clientIdentity.getMSPID(); + + if (clientMSPID !== "Org1MSP") { + throw new Error("client is not authorized to mint new tokens"); + } + + // Get ID of submitting client identity + const minter = ctx.clientIdentity.getID(); + + const amountInt = parseInt(amount); + if (amountInt <= 0) { + throw new Error("mint amount must be a positive integer"); + } + + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [minter]); + + const currentBalanceBytes = await ctx.stub.getState(balanceKey); + // If minter current balance doesn't yet exist, we'll create it with a current balance of 0 + let currentBalance; + if (!currentBalanceBytes || currentBalanceBytes.length === 0) { + currentBalance = 0; + } else { + currentBalance = parseInt(currentBalanceBytes.toString()); + } + const updatedBalance = this.add(currentBalance, amountInt); + + await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString())); + + // Increase totalSupply + const totalSupplyBytes = await ctx.stub.getState(totalSupplyKey); + let totalSupply; + if (!totalSupplyBytes || totalSupplyBytes.length === 0) { + console.log("Initialize the tokenSupply"); + totalSupply = 0; + } else { + totalSupply = parseInt(totalSupplyBytes.toString()); + } + totalSupply = this.add(totalSupply, amountInt); + await ctx.stub.putState( + totalSupplyKey, + Buffer.from(totalSupply.toString()), + ); + + // Emit the Transfer event + const transferEvent = { from: "0x0", to: minter, value: amountInt }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + + console.log( + `minter account ${minter} balance updated from ${currentBalance} to ${updatedBalance}`, + ); + return true; + } + + /** + * Burn redeem tokens from minter's account balance + * + * @param {Context} ctx the transaction context + * @param {Integer} amount amount of tokens to be burned + * @returns {Object} The balance + */ + async Burn(ctx, amount) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + // Check minter authorization - this sample assumes Org1 is the central banker with privilege to burn tokens + const clientMSPID = ctx.clientIdentity.getMSPID(); + if (clientMSPID !== "Org1MSP") { + throw new Error("client is not authorized to mint new tokens"); + } + + const minter = ctx.clientIdentity.getID(); + + const amountInt = parseInt(amount); + + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [minter]); + + const currentBalanceBytes = await ctx.stub.getState(balanceKey); + if (!currentBalanceBytes || currentBalanceBytes.length === 0) { + throw new Error("The balance does not exist"); + } + const currentBalance = parseInt(currentBalanceBytes.toString()); + const updatedBalance = this.sub(currentBalance, amountInt); + + await ctx.stub.putState(balanceKey, Buffer.from(updatedBalance.toString())); + + // Decrease totalSupply + const totalSupplyBytes = await ctx.stub.getState(totalSupplyKey); + if (!totalSupplyBytes || totalSupplyBytes.length === 0) { + throw new Error("totalSupply does not exist."); + } + const totalSupply = this.sub( + parseInt(totalSupplyBytes.toString()), + amountInt, + ); + await ctx.stub.putState( + totalSupplyKey, + Buffer.from(totalSupply.toString()), + ); + + // Emit the Transfer event + const transferEvent = { from: minter, to: "0x0", value: amountInt }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + + console.log( + `minter account ${minter} balance updated from ${currentBalance} to ${updatedBalance}`, + ); + return true; + } + + /** + * ClientAccountBalance returns the balance of the requesting client's account. + * + * @param {Context} ctx the transaction context + * @returns {Number} Returns the account balance + */ + async ClientAccountBalance(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + // Get ID of submitting client identity + const clientAccountID = ctx.clientIdentity.getID(); + + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [ + clientAccountID, + ]); + const balanceBytes = await ctx.stub.getState(balanceKey); + if (!balanceBytes || balanceBytes.length === 0) { + throw new Error(`the account ${clientAccountID} does not exist`); + } + const balance = parseInt(balanceBytes.toString()); + + return balance; + } + + // ClientAccountID returns the id of the requesting client's account. + // In this implementation, the client account ID is the clientId itself. + // Users can use this function to get their own account id, which they can then give to others as the payment address + async ClientAccountID(ctx) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + // Get ID of submitting client identity + const clientAccountID = ctx.clientIdentity.getID(); + return clientAccountID; + } + + //Checks that contract options have been already initialized + async CheckInitialized(ctx) { + const nameBytes = await ctx.stub.getState(nameKey); + if (!nameBytes || nameBytes.length === 0) { + throw new Error( + "contract options need to be set before calling any function, call Initialize() to initialize contract", + ); + } + } + + /** + * Escrow transfers tokens from client account to the bridging entity account. + * recipient account must be a valid clientID as returned by the ClientAccountID() function. + * + * @param {Context} ctx the transaction context + * @param {String} to The recipient + * @param {Integer} value The amount of token to be transferred + * @returns {Boolean} Return whether the transfer was successful or not + */ + async Escrow(ctx, value, id) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const from = ctx.clientIdentity.getID(); + + const transferResp = await this._transfer( + ctx, + from, + FABRIC_BRIDGE_IDENTITY, + value, + ); + if (!transferResp) { + throw new Error("Failed to transfer"); + } + + // Emit the Transfer event + const transferEvent = { + from, + FABRIC_BRIDGE_IDENTITY, + value: parseInt(value), + }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + + // this means that the transfer was made to the bridging entity + await ctx.stub.invokeChaincode( + "asset-reference-contract", + ["CreateAssetReference", id, value.toString(), from.toString()], + ctx.stub.getChannelID(), + ); + } + + /** + * Refunds transfers tokens from the bridging entity account to the client account. + * recipient account must be a valid clientID as returned by the ClientAccountID() function. + * + * @param {Context} ctx the transaction context + * @param {String} to The recipient + * @param {Integer} value The amount of token to be transferred + * @returns {Boolean} Return whether the transfer was successful or not + */ + async Refund(ctx, to, value, eth_address) { + //check contract options are already set first to execute the function + await this.CheckInitialized(ctx); + + const from = ctx.clientIdentity.getID(); + + if (from !== FABRIC_BRIDGE_IDENTITY) { + throw new Error("client is not authorized to refund tokens"); + } + + const clientEthAddress = await this.getAddressMapping(ctx, to); + + if (clientEthAddress !== eth_address) { + throw new Error( + "client is not authorized to bridge back tokens to another client account", + ); + } + + const transferResp = await this._transfer(ctx, from, to, value); + if (!transferResp) { + throw new Error("Failed to transfer"); + } + + // Emit the Transfer event + const transferEvent = { + from, + FABRIC_BRIDGE_IDENTITY, + value: parseInt(value), + }; + ctx.stub.setEvent("Transfer", Buffer.from(JSON.stringify(transferEvent))); + } + + async initializeAddressMapping(ctx) { + // initialize mapping between Fabric Identities and Ethereum addresses + for (let account of accounts) { + const addressKey = ctx.stub.createCompositeKey(addressPrefix, [ + account.fabric, + ]); + + // additionally initialize all addresses to 0 + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [ + account.fabric, + ]); + + await ctx.stub.putState(addressKey, Buffer.from(account.ethereum)); + await ctx.stub.putState(balanceKey, Buffer.from("0")); + } + } + + async getAddressMapping(ctx, fabricID) { + await this.CheckInitialized(ctx); + + const addressKey = ctx.stub.createCompositeKey(addressPrefix, [fabricID]); + + console.log("retrieving address with key: " + addressKey); + const addressBytes = await ctx.stub.getState(addressKey); + if (!addressBytes || addressBytes.length === 0) { + throw new Error(`the account ${fabricID} does not exist`); + } + const address = addressBytes.toString(); + + return address; + } + + async checkAddressMapping(ctx, fabricID, ethAddress) { + const storedAddress = await this.getAddressMapping(ctx, fabricID); + + if (storedAddress != ethAddress) { + throw new Error(`it is not possible to bridge CBDC to another user.`); + } + } + + // add two number checking for overflow + add(a, b) { + let c = a + b; + if (a !== c - b || b !== c - a) { + throw new Error(`Math: addition overflow occurred ${a} + ${b}`); + } + return c; + } + + // add two number checking for overflow + sub(a, b) { + let c = a - b; + if (a !== c + b || b !== a - c) { + throw new Error(`Math: subtraction overflow occurred ${a} - ${b}`); + } + return c; + } + + /** + * Testing purposes function + */ + async ResetState(ctx) { + for (let account of accounts) { + const balanceKey = ctx.stub.createCompositeKey(balancePrefix, [ + account.fabric, + ]); + await ctx.stub.putState(balanceKey, Buffer.from("0")); + } + } +} + +module.exports = TokenERC20Contract; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/package.json b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/package.json new file mode 100644 index 0000000000..2a0a21457b --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/package.json @@ -0,0 +1,51 @@ +{ + "name": "cbdc", + "version": "0.0.1", + "description": "Token-ERC20 contract implemented in JavaScript", + "main": "index.js", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "scripts": { + "lint": "eslint .", + "test": "nyc mocha --recursive", + "mocha": "mocha --recursive", + "start": "fabric-chaincode-node start" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.2.3", + "fabric-shim": "^2.0.0" + }, + "devDependencies": { + "@types/sinon-chai": "^3.2.8", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "eslint": "^4.19.1", + "mocha": "^8.0.1", + "nyc": "^14.1.1", + "sinon": "^6.0.0", + "sinon-chai": "^3.2.0" + }, + "nyc": { + "exclude": [ + "coverage/**", + "test/**", + "index.js", + ".eslintrc.js" + ], + "reporter": [ + "text-summary", + "html" + ], + "all": true, + "check-coverage": false, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/test/tokenERC20.test.js b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/test/tokenERC20.test.js new file mode 100644 index 0000000000..8481b9af8b --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/fabric-contracts/cbdc-erc-20/javascript/test/tokenERC20.test.js @@ -0,0 +1,397 @@ +/* + * Copyright IBM Corp. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +"use strict"; + +const { Context } = require("fabric-contract-api"); +const { ChaincodeStub, ClientIdentity } = require("fabric-shim"); + +const { TokenERC20Contract } = require("../lib/tokenERC20"); + +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); +const sinon = require("sinon"); +const expect = chai.expect; + +chai.should(); +chai.use(chaiAsPromised); + +describe("Chaincode", () => { + let sandbox; + let token; + let ctx; + let mockStub; + let mockClientIdentity; + + beforeEach("Sandbox creation", async () => { + sandbox = sinon.createSandbox(); + token = new TokenERC20Contract("token-erc20"); + + ctx = sinon.createStubInstance(Context); + mockStub = sinon.createStubInstance(ChaincodeStub); + ctx.stub = mockStub; + mockClientIdentity = sinon.createStubInstance(ClientIdentity); + ctx.clientIdentity = mockClientIdentity; + + await token.Initialize(ctx, "some name", "some symbol", "2"); + + mockStub.putState.resolves("some state"); + mockStub.setEvent.returns("set event"); + }); + + afterEach("Sandbox restoration", () => { + sandbox.restore(); + }); + + describe("#TokenName", () => { + it("should work", async () => { + mockStub.getState.resolves("some state"); + + const response = await token.TokenName(ctx); + sinon.assert.calledWith(mockStub.getState, "name"); + expect(response).to.equals("some state"); + }); + }); + + describe("#Symbol", () => { + it("should work", async () => { + mockStub.getState.resolves("some state"); + + const response = await token.Symbol(ctx); + sinon.assert.calledWith(mockStub.getState, "symbol"); + expect(response).to.equals("some state"); + }); + }); + + describe("#Decimals", () => { + it("should work", async () => { + mockStub.getState.resolves(Buffer.from("2")); + + const response = await token.Decimals(ctx); + sinon.assert.calledWith(mockStub.getState, "decimals"); + expect(response).to.equals(2); + }); + }); + + describe("#TotalSupply", () => { + it("should work", async () => { + mockStub.getState.resolves(Buffer.from("10000")); + + const response = await token.TotalSupply(ctx); + sinon.assert.calledWith(mockStub.getState, "totalSupply"); + expect(response).to.equals(10000); + }); + }); + + describe("#BalanceOf", () => { + it("should work", async () => { + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.resolves(Buffer.from("1000")); + + const response = await token.BalanceOf(ctx, "Alice"); + expect(response).to.equals(1000); + }); + }); + + describe("#_transfer", () => { + it("should fail when the sender and the recipient are the same", async () => { + await expect( + token._transfer(ctx, "Alice", "Alice", "1000"), + ).to.be.rejectedWith( + Error, + "cannot transfer to and from same client account", + ); + }); + + it("should fail when the sender does not have enough token", async () => { + mockStub.createCompositeKey + .withArgs("balance", ["Alice"]) + .returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(Buffer.from("500")); + + await expect( + token._transfer(ctx, "Alice", "Bob", "1000"), + ).to.be.rejectedWith( + Error, + "client account Alice has insufficient funds.", + ); + }); + + it("should transfer to a new account when the sender has enough token", async () => { + mockStub.createCompositeKey + .withArgs("balance", ["Alice"]) + .returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(Buffer.from("1000")); + + mockStub.createCompositeKey + .withArgs("balance", ["Bob"]) + .returns("balance_Bob"); + mockStub.getState.withArgs("balance_Bob").resolves(null); + + const response = await token._transfer(ctx, "Alice", "Bob", "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("0"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "balance_Bob", + Buffer.from("1000"), + ); + expect(response).to.equals(true); + }); + + it("should transfer to the existing account when the sender has enough token", async () => { + mockStub.createCompositeKey + .withArgs("balance", ["Alice"]) + .returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(Buffer.from("1000")); + + mockStub.createCompositeKey + .withArgs("balance", ["Bob"]) + .returns("balance_Bob"); + mockStub.getState.withArgs("balance_Bob").resolves(Buffer.from("2000")); + + const response = await token._transfer(ctx, "Alice", "Bob", "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("0"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "balance_Bob", + Buffer.from("3000"), + ); + expect(response).to.equals(true); + }); + }); + + describe("#Transfer", () => { + it("should work", async () => { + mockClientIdentity.getID.returns("Alice"); + sinon.stub(token, "_transfer").returns(true); + + const response = await token.Transfer(ctx, "Bob", "1000"); + const event = { from: "Alice", to: "Bob", value: 1000 }; + sinon.assert.calledWith( + mockStub.setEvent, + "Transfer", + Buffer.from(JSON.stringify(event)), + ); + expect(response).to.equals(true); + }); + }); + + describe("#TransferFrom", () => { + it("should fail when the spender is not allowed to spend the token", async () => { + mockClientIdentity.getID.returns("Charlie"); + + mockStub.createCompositeKey + .withArgs("allowance", ["Alice", "Charlie"]) + .returns("allowance_Alice_Charlie"); + mockStub.getState + .withArgs("allowance_Alice_Charlie") + .resolves(Buffer.from("0")); + + await expect( + token.TransferFrom(ctx, "Alice", "Bob", "1000"), + ).to.be.rejectedWith( + Error, + "The spender does not have enough allowance to spend.", + ); + }); + + it("should transfer when the spender is allowed to spend the token", async () => { + mockClientIdentity.getID.returns("Charlie"); + + mockStub.createCompositeKey + .withArgs("allowance", ["Alice", "Charlie"]) + .returns("allowance_Alice_Charlie"); + mockStub.getState + .withArgs("allowance_Alice_Charlie") + .resolves(Buffer.from("3000")); + + sinon.stub(token, "_transfer").returns(true); + + const response = await token.TransferFrom(ctx, "Alice", "Bob", "1000"); + sinon.assert.calledWith( + mockStub.putState, + "allowance_Alice_Charlie", + Buffer.from("2000"), + ); + const event = { from: "Alice", to: "Bob", value: 1000 }; + sinon.assert.calledWith( + mockStub.setEvent, + "Transfer", + Buffer.from(JSON.stringify(event)), + ); + expect(response).to.equals(true); + }); + }); + + describe("#Approve", () => { + it("should work", async () => { + mockClientIdentity.getID.returns("Dave"); + mockStub.createCompositeKey.returns("allowance_Dave_Eve"); + + const response = await token.Approve(ctx, "Ellen", "1000"); + sinon.assert.calledWith( + mockStub.putState, + "allowance_Dave_Eve", + Buffer.from("1000"), + ); + expect(response).to.equals(true); + }); + }); + + describe("#Allowance", () => { + it("should work", async () => { + mockStub.createCompositeKey.returns("allowance_Dave_Eve"); + mockStub.getState.resolves(Buffer.from("1000")); + + const response = await token.Allowance(ctx, "Dave", "Eve"); + expect(response).to.equals(1000); + }); + }); + + describe("#Initialize", () => { + it("should work", async () => { + //we consider it has already been initialized in the before-each statement + sinon.assert.calledWith( + mockStub.putState, + "name", + Buffer.from("some name"), + ); + sinon.assert.calledWith( + mockStub.putState, + "symbol", + Buffer.from("some symbol"), + ); + sinon.assert.calledWith(mockStub.putState, "decimals", Buffer.from("2")); + }); + + it("should failed if called a second time", async () => { + //we consider it has already been initialized in the before-each statement + await expect( + await token.Initialize(ctx, "some name", "some symbol", "2"), + ).to.be.rejectedWith( + Error, + "contract options are already set, client is not authorized to change them", + ); + }); + }); + + describe("#Mint", () => { + it("should add token to a new account and a new total supply", async () => { + mockClientIdentity.getMSPID.returns("Org1MSP"); + mockClientIdentity.getID.returns("Alice"); + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(null); + mockStub.getState.withArgs("totalSupply").resolves(null); + + const response = await token.Mint(ctx, "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("1000"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "totalSupply", + Buffer.from("1000"), + ); + expect(response).to.equals(true); + }); + + it("should add token to the existing account and the existing total supply", async () => { + mockClientIdentity.getMSPID.returns("Org1MSP"); + mockClientIdentity.getID.returns("Alice"); + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(Buffer.from("1000")); + mockStub.getState.withArgs("totalSupply").resolves(Buffer.from("2000")); + + const response = await token.Mint(ctx, "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("2000"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "totalSupply", + Buffer.from("3000"), + ); + expect(response).to.equals(true); + }); + + it("should add token to a new account and the existing total supply", async () => { + mockClientIdentity.getMSPID.returns("Org1MSP"); + mockClientIdentity.getID.returns("Alice"); + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(null); + mockStub.getState.withArgs("totalSupply").resolves(Buffer.from("2000")); + + const response = await token.Mint(ctx, "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("1000"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "totalSupply", + Buffer.from("3000"), + ); + expect(response).to.equals(true); + }); + }); + + describe("#Burn", () => { + it("should work", async () => { + mockClientIdentity.getMSPID.returns("Org1MSP"); + mockClientIdentity.getID.returns("Alice"); + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.withArgs("balance_Alice").resolves(Buffer.from("1000")); + mockStub.getState.withArgs("totalSupply").resolves(Buffer.from("2000")); + + const response = await token.Burn(ctx, "1000"); + sinon.assert.calledWith( + mockStub.putState.getCall(0), + "balance_Alice", + Buffer.from("0"), + ); + sinon.assert.calledWith( + mockStub.putState.getCall(1), + "totalSupply", + Buffer.from("1000"), + ); + expect(response).to.equals(true); + }); + }); + + describe("#ClientAccountBalance", () => { + it("should work", async () => { + mockClientIdentity.getID.returns("Alice"); + mockStub.createCompositeKey.returns("balance_Alice"); + mockStub.getState.resolves(Buffer.from("1000")); + + const response = await token.ClientAccountBalance(ctx); + expect(response).to.equals(1000); + }); + }); + + describe("#ClientAccountID", () => { + it("should work", async () => { + mockClientIdentity.getID.returns("x509::{subject DN}::{issuer DN}"); + + const response = await token.ClientAccountID(ctx); + sinon.assert.calledOnce(mockClientIdentity.getID); + expect(response).to.equals("x509::{subject DN}::{issuer DN}"); + }); + }); +}); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app-cli.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app-cli.ts new file mode 100755 index 0000000000..ed3bac88a4 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app-cli.ts @@ -0,0 +1,84 @@ +#!/usr/bin/env node + +import { + AuthorizationProtocol, + ConfigService, + IAuthorizationConfig, +} from "@hyperledger/cactus-cmd-api-server"; +import { LoggerProvider } from "@hyperledger/cactus-common"; +import { ICbdcBridgingApp, CbdcBridgingApp } from "./cbdc-bridging-app"; +import CryptoMaterial from "../../crypto-material/crypto-material.json"; +import "dotenv/config"; + +export async function launchApp( + env?: NodeJS.ProcessEnv, + args?: string[], +): Promise { + const configService = new ConfigService(); + const exampleConfig = await configService.newExampleConfig(); + exampleConfig.configFile = ""; + exampleConfig.authorizationConfigJson = (JSON.stringify( + exampleConfig.authorizationConfigJson, + ) as unknown) as IAuthorizationConfig; + exampleConfig.authorizationProtocol = AuthorizationProtocol.NONE; + + const convictConfig = await configService.newExampleConfigConvict( + exampleConfig, + ); + + env = await configService.newExampleConfigEnv(convictConfig.getProperties()); + + const config = await configService.getOrCreate({ args, env }); + const serverOptions = config.getProperties(); + + LoggerProvider.setLogLevel(serverOptions.logLevel); + + const clientGatewayKeyPair = { + privateKey: Uint8Array.from( + Buffer.from(CryptoMaterial.gateways["gateway1"].privateKey, "hex"), + ), + publicKey: Uint8Array.from( + Buffer.from(CryptoMaterial.gateways["gateway1"].publicKey, "hex"), + ), + }; + + const serverGatewayKeyPair = { + privateKey: Uint8Array.from( + Buffer.from(CryptoMaterial.gateways["gateway2"].privateKey, "hex"), + ), + publicKey: Uint8Array.from( + Buffer.from(CryptoMaterial.gateways["gateway2"].publicKey, "hex"), + ), + }; + + if ( + process.env.API_HOST == undefined || + process.env.API_SERVER_1_PORT == undefined || + process.env.API_SERVER_2_PORT == undefined + ) { + throw new Error("Env variables not set"); + } + + const appOptions: ICbdcBridgingApp = { + apiHost: process.env.API_HOST, + apiServer1Port: parseInt(process.env.API_SERVER_1_PORT), + apiServer2Port: parseInt(process.env.API_SERVER_2_PORT), + clientGatewayKeyPair: clientGatewayKeyPair, + serverGatewayKeyPair: serverGatewayKeyPair, + logLevel: "DEBUG", + }; + + const cbdcBridgingApp = new CbdcBridgingApp(appOptions); + try { + await cbdcBridgingApp.start(); + console.info("CbdcBridgingApp running..."); + } catch (ex) { + console.error(`CbdcBridgingApp crashed. Existing...`, ex); + await cbdcBridgingApp.stop(); + process.exit(-1); + } +} + +if (require.main === module) { + launchApp(); +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app.ts new file mode 100644 index 0000000000..a7d948af89 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/cbdc-bridging-app.ts @@ -0,0 +1,240 @@ +import { AddressInfo } from "net"; +import { v4 as uuidv4 } from "uuid"; +import { Server } from "http"; +import exitHook, { IAsyncExitHookDoneCallback } from "async-exit-hook"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { + LogLevelDesc, + Logger, + LoggerProvider, + Servers, +} from "@hyperledger/cactus-common"; +import { + ApiServer, + AuthorizationProtocol, + ConfigService, + ICactusApiServerOptions, +} from "@hyperledger/cactus-cmd-api-server"; +import { + Configuration, + DefaultApi as OdapApi, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/index"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { CbdcBridgingAppDummyInfrastructure } from "./infrastructure/cbdc-bridging-app-dummy-infrastructure"; +import { DefaultApi as FabricApi } from "@hyperledger/cactus-plugin-ledger-connector-fabric"; +import { DefaultApi as BesuApi } from "@hyperledger/cactus-plugin-ledger-connector-besu"; +import { IOdapPluginKeyPair } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { DefaultApi as IpfsApi } from "@hyperledger/cactus-plugin-object-store-ipfs"; +import { FabricOdapGateway } from "./odap-extension/fabric-odap-gateway"; +import { BesuOdapGateway } from "./odap-extension/besu-odap-gateway"; +import CryptoMaterial from "../../crypto-material/crypto-material.json"; + +export interface ICbdcBridgingApp { + apiHost: string; + apiServer1Port: number; + apiServer2Port: number; + clientGatewayKeyPair: IOdapPluginKeyPair; + serverGatewayKeyPair: IOdapPluginKeyPair; + logLevel?: LogLevelDesc; + apiServerOptions?: ICactusApiServerOptions; + disableSignalHandlers?: true; +} + +export type ShutdownHook = () => Promise; +export class CbdcBridgingApp { + private readonly log: Logger; + private readonly shutdownHooks: ShutdownHook[]; + readonly infrastructure: CbdcBridgingAppDummyInfrastructure; + + public constructor(public readonly options: ICbdcBridgingApp) { + const fnTag = "CbdcBridgingApp#constructor()"; + + if (!options) { + throw new Error(`${fnTag} options parameter is falsy`); + } + const { logLevel } = options; + + const level = logLevel || "INFO"; + const label = "cbdc-bridging-app"; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.shutdownHooks = []; + + this.infrastructure = new CbdcBridgingAppDummyInfrastructure({ + logLevel: level, + }); + } + + public async start(): Promise { + this.log.debug(`Starting CBDC Bridging App...`); + + if (!this.options.disableSignalHandlers) { + exitHook((callback: IAsyncExitHookDoneCallback) => { + this.stop().then(callback); + }); + this.log.debug(`Registered signal handlers for graceful auto-shutdown`); + } + + await this.infrastructure.start(); + this.onShutdown(() => this.infrastructure.stop()); + + const fabricPlugin = await this.infrastructure.createFabricLedgerConnector(); + const besuPlugin = await this.infrastructure.createBesuLedgerConnector(); + const clientIpfsPlugin = await this.infrastructure.createIPFSConnector(); + const serverIpfsPlugin = await this.infrastructure.createIPFSConnector(); + + // Reserve the ports where the API Servers will run + const httpApiA = await Servers.startOnPort( + this.options.apiServer1Port, + this.options.apiHost, + ); + const httpApiB = await Servers.startOnPort( + this.options.apiServer2Port, + this.options.apiHost, + ); + + const addressInfoA = httpApiA.address() as AddressInfo; + const nodeApiHostA = `http://${this.options.apiHost}:${addressInfoA.port}`; + + const addressInfoB = httpApiB.address() as AddressInfo; + const nodeApiHostB = `http://${this.options.apiHost}:${addressInfoB.port}`; + + const fabricOdapGateway = await this.infrastructure.createClientGateway( + nodeApiHostA, + this.options.clientGatewayKeyPair, + `http://${this.options.apiHost}:${addressInfoA.port}`, + ); + + const besuOdapGateway = await this.infrastructure.createServerGateway( + nodeApiHostB, + this.options.serverGatewayKeyPair, + `http://${this.options.apiHost}:${addressInfoB.port}`, + ); + + const clientPluginRegistry = new PluginRegistry({ + plugins: [ + new PluginKeychainMemory({ + keychainId: CryptoMaterial.keychains.keychain1.id, + instanceId: uuidv4(), + logLevel: "INFO", + }), + ], + }); + const serverPluginRegistry = new PluginRegistry({ + plugins: [ + new PluginKeychainMemory({ + keychainId: CryptoMaterial.keychains.keychain2.id, + instanceId: uuidv4(), + logLevel: "INFO", + }), + ], + }); + + clientPluginRegistry.add(fabricPlugin); + clientPluginRegistry.add(fabricOdapGateway); + clientPluginRegistry.add(clientIpfsPlugin); + + serverPluginRegistry.add(besuPlugin); + serverPluginRegistry.add(serverIpfsPlugin); + serverPluginRegistry.add(besuOdapGateway); + + const apiServer1 = await this.startNode(httpApiA, clientPluginRegistry); + const apiServer2 = await this.startNode(httpApiB, serverPluginRegistry); + + const fabricApiClient = new FabricApi( + new Configuration({ basePath: nodeApiHostA }), + ); + + const besuApiClient = new BesuApi( + new Configuration({ basePath: nodeApiHostB }), + ); + + this.log.info("Deploying chaincode and smart contracts..."); + + await this.infrastructure.deployFabricCbdcContract(fabricApiClient); + + await this.infrastructure.deployFabricAssetReferenceContract( + fabricApiClient, + ); + + await this.infrastructure.deployBesuContracts(besuApiClient); + + this.log.info(`Chaincode and smart Contracts deployed.`); + + return { + apiServer1, + apiServer2, + fabricGatewayApi: new OdapApi( + new Configuration({ basePath: nodeApiHostA }), + ), + besuGatewayApi: new OdapApi( + new Configuration({ basePath: nodeApiHostB }), + ), + ipfsApiClient: new IpfsApi(new Configuration({ basePath: nodeApiHostA })), + fabricApiClient, + besuApiClient, + fabricOdapGateway, + besuOdapGateway, + }; + } + + public async stop(): Promise { + for (const hook of this.shutdownHooks) { + await hook(); // FIXME add timeout here so that shutdown does not hang + } + } + + public onShutdown(hook: ShutdownHook): void { + this.shutdownHooks.push(hook); + } + + public async startNode( + httpServerApi: Server, + pluginRegistry: PluginRegistry, + ): Promise { + this.log.info(`Starting API Server node...`); + + const addressInfoApi = httpServerApi.address() as AddressInfo; + + let config; + if (this.options.apiServerOptions) { + config = this.options.apiServerOptions; + } else { + const configService = new ConfigService(); + const convictConfig = await configService.getOrCreate(); + config = convictConfig.getProperties(); + config.configFile = ""; + config.apiCorsDomainCsv = `http://${process.env.API_HOST_FRONTEND}:${process.env.API_PORT_FRONTEND}`; + config.cockpitCorsDomainCsv = `http://${process.env.API_HOST_FRONTEND}:${process.env.API_PORT_FRONTEND}`; + config.apiPort = addressInfoApi.port; + config.apiHost = addressInfoApi.address; + config.grpcPort = 0; + config.logLevel = this.options.logLevel || "INFO"; + config.authorizationProtocol = AuthorizationProtocol.NONE; + } + + const apiServer = new ApiServer({ + config, + httpServerApi, + pluginRegistry, + }); + + this.onShutdown(() => apiServer.shutdown()); + + await apiServer.start(); + + return apiServer; + } +} + +export interface IStartInfo { + readonly apiServer1: ApiServer; + readonly apiServer2: ApiServer; + readonly fabricGatewayApi: OdapApi; + readonly besuGatewayApi: OdapApi; + readonly ipfsApiClient: IpfsApi; + readonly besuApiClient: BesuApi; + readonly fabricApiClient: FabricApi; + readonly fabricOdapGateway: FabricOdapGateway; + readonly besuOdapGateway: BesuOdapGateway; +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.ts new file mode 100755 index 0000000000..87cb558397 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.web.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.web.ts new file mode 100755 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export {}; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/infrastructure/cbdc-bridging-app-dummy-infrastructure.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/infrastructure/cbdc-bridging-app-dummy-infrastructure.ts new file mode 100644 index 0000000000..da50b93a2f --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/infrastructure/cbdc-bridging-app-dummy-infrastructure.ts @@ -0,0 +1,756 @@ +import path from "path"; +import { v4 as uuidv4 } from "uuid"; +import fs from "fs-extra"; +import { create } from "ipfs-http-client"; +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { + BesuTestLedger, + FabricTestLedgerV1, + GoIpfsTestContainer, +} from "@hyperledger/cactus-test-tooling"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; +import { + DefaultApi as FabricApi, + ChainCodeProgrammingLanguage, + DefaultEventHandlerStrategy, + DeploymentTargetOrgFabric2x, + FabricContractInvocationType, + FileBase64, + PluginLedgerConnectorFabric, +} from "@hyperledger/cactus-plugin-ledger-connector-fabric"; +import { + DefaultApi as BesuApi, + DeployContractSolidityBytecodeV1Request, + EthContractInvocationType, + PluginFactoryLedgerConnector, + PluginLedgerConnectorBesu, + Web3SigningCredentialType, + InvokeContractV1Request as BesuInvokeContractV1Request, +} from "@hyperledger/cactus-plugin-ledger-connector-besu"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { PluginObjectStoreIpfs } from "@hyperledger/cactus-plugin-object-store-ipfs"; +import AssetReferenceContractJson from "../../../solidity/asset-reference-contract/AssetReferenceContract.json"; +import CBDCcontractJson from "../../../solidity/cbdc-erc-20/CBDCcontract.json"; +import { IOdapPluginKeyPair } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { FabricOdapGateway } from "../odap-extension/fabric-odap-gateway"; +import { BesuOdapGateway } from "../odap-extension/besu-odap-gateway"; +import { PluginImportType } from "@hyperledger/cactus-core-api"; +import CryptoMaterial from "../../../crypto-material/crypto-material.json"; + +export interface ICbdcBridgingAppDummyInfrastructureOptions { + logLevel?: LogLevelDesc; +} + +export class CbdcBridgingAppDummyInfrastructure { + public static readonly CLASS_NAME = "CbdcBridgingAppDummyInfrastructure"; + // TODO: Move this to the FabricTestLedger class where it belongs. + public static readonly FABRIC_2_AIO_CLI_CFG_DIR = + "/opt/gopath/src/github.com/hyperledger/fabric/peer/organizations/"; + + private readonly besu: BesuTestLedger; + private readonly fabric: FabricTestLedgerV1; + private readonly ipfs: GoIpfsTestContainer; + private readonly ipfsParentPath: string; + + private readonly log: Logger; + + public get className(): string { + return CbdcBridgingAppDummyInfrastructure.CLASS_NAME; + } + + public get orgCfgDir(): string { + return CbdcBridgingAppDummyInfrastructure.FABRIC_2_AIO_CLI_CFG_DIR; + } + + constructor( + public readonly options: ICbdcBridgingAppDummyInfrastructureOptions, + ) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + + this.ipfsParentPath = `/${uuidv4()}/${uuidv4()}/`; + + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.besu = new BesuTestLedger({ + logLevel: level || "DEBUG", + emitContainerLogs: true, + envVars: ["BESU_NETWORK=dev"], + }); + + this.fabric = new FabricTestLedgerV1({ + publishAllPorts: true, + imageName: "ghcr.io/hyperledger/cactus-fabric2-all-in-one", + envVars: new Map([["FABRIC_VERSION", "2.2.0"]]), + logLevel: level || "DEBUG", + }); + + this.ipfs = new GoIpfsTestContainer({ + logLevel: level || "DEBUG", + }); + } + + public get org1Env(): NodeJS.ProcessEnv & DeploymentTargetOrgFabric2x { + return { + CORE_LOGGING_LEVEL: "debug", + FABRIC_LOGGING_SPEC: "debug", + CORE_PEER_LOCALMSPID: "Org1MSP", + + ORDERER_CA: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + + FABRIC_CFG_PATH: "/etc/hyperledger/fabric", + CORE_PEER_TLS_ENABLED: "true", + CORE_PEER_TLS_ROOTCERT_FILE: `${this.orgCfgDir}peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt`, + CORE_PEER_MSPCONFIGPATH: `${this.orgCfgDir}peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp`, + CORE_PEER_ADDRESS: "peer0.org1.example.com:7051", + ORDERER_TLS_ROOTCERT_FILE: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + }; + } + + public get org2Env(): NodeJS.ProcessEnv & DeploymentTargetOrgFabric2x { + return { + CORE_LOGGING_LEVEL: "debug", + FABRIC_LOGGING_SPEC: "debug", + CORE_PEER_LOCALMSPID: "Org2MSP", + + FABRIC_CFG_PATH: "/etc/hyperledger/fabric", + CORE_PEER_TLS_ENABLED: "true", + ORDERER_CA: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + + CORE_PEER_ADDRESS: "peer0.org2.example.com:9051", + CORE_PEER_MSPCONFIGPATH: `${this.orgCfgDir}peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp`, + CORE_PEER_TLS_ROOTCERT_FILE: `${this.orgCfgDir}peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt`, + ORDERER_TLS_ROOTCERT_FILE: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + }; + } + + public async start(): Promise { + try { + this.log.info(`Starting dummy infrastructure...`); + await Promise.all([ + this.besu.start(), + this.fabric.start(), + this.ipfs.start(), + ]); + this.log.info(`Started dummy infrastructure OK`); + } catch (ex) { + this.log.error(`Starting of dummy infrastructure crashed: `, ex); + throw ex; + } + } + + public async stop(): Promise { + try { + this.log.info(`Stopping...`); + await Promise.all([ + this.besu.stop().then(() => this.besu.destroy()), + this.fabric.stop().then(() => this.fabric.destroy()), + this.ipfs.stop().then(() => this.ipfs.destroy()), + ]); + this.log.info(`Stopped OK`); + } catch (ex) { + this.log.error(`Stopping crashed: `, ex); + throw ex; + } + } + + public async createFabricLedgerConnector(): Promise< + PluginLedgerConnectorFabric + > { + const connectionProfileOrg1 = await this.fabric.getConnectionProfileOrg1(); + const enrollAdminOutOrg1 = await this.fabric.enrollAdmin("org1"); + const adminWalletOrg1 = enrollAdminOutOrg1[1]; + const [userIdentity1] = await this.fabric.enrollUser( + adminWalletOrg1, + "userA", + "org1", + ); + const [userIdentity2] = await this.fabric.enrollUser( + adminWalletOrg1, + "userB", + "org1", + ); + + const enrollAdminOut = await this.fabric.enrollAdmin("org2"); + const adminWallet = enrollAdminOut[1]; + const [bridgeIdentity] = await this.fabric.enrollUser( + adminWallet, + "bridge", + "org2", + ); + + const sshConfig = await this.fabric.getSshConfig(); + + const keychainEntryKey1 = "userA"; + const keychainEntryValue1 = JSON.stringify(userIdentity1); + + const keychainEntryKey2 = "userB"; + const keychainEntryValue2 = JSON.stringify(userIdentity2); + + const keychainEntryKey3 = "bridge"; + const keychainEntryValue3 = JSON.stringify(bridgeIdentity); + + const keychainPlugin = new PluginKeychainMemory({ + instanceId: uuidv4(), + keychainId: CryptoMaterial.keychains.keychain1.id, + logLevel: undefined, + backend: new Map([ + [keychainEntryKey1, keychainEntryValue1], + [keychainEntryKey2, keychainEntryValue2], + [keychainEntryKey3, keychainEntryValue3], + ]), + }); + + const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] }); + + this.log.info(`Creating Fabric Connector...`); + return new PluginLedgerConnectorFabric({ + instanceId: uuidv4(), + dockerBinary: "/usr/local/bin/docker", + peerBinary: "/fabric-samples/bin/peer", + goBinary: "/usr/local/go/bin/go", + pluginRegistry, + cliContainerEnv: this.org1Env, + sshConfig, + connectionProfile: connectionProfileOrg1, + logLevel: this.options.logLevel || "INFO", + discoveryOptions: { + enabled: true, + asLocalhost: true, + }, + eventHandlerOptions: { + strategy: DefaultEventHandlerStrategy.NetworkScopeAllfortx, + commitTimeout: 300, + }, + }); + } + + public async createBesuLedgerConnector(): Promise { + const rpcApiHttpHost = await this.besu.getRpcApiHttpHost(); + const rpcApiWsHost = await this.besu.getRpcApiWsHost(); + + const keychainEntryKey = AssetReferenceContractJson.contractName; + const keychainEntryValue = JSON.stringify(AssetReferenceContractJson); + + const keychainEntryKey2 = CBDCcontractJson.contractName; + const keychainEntryValue2 = JSON.stringify(CBDCcontractJson); + + const keychainPlugin = new PluginKeychainMemory({ + instanceId: uuidv4(), + keychainId: CryptoMaterial.keychains.keychain2.id, + logLevel: undefined, + backend: new Map([ + [keychainEntryKey, keychainEntryValue], + [keychainEntryKey2, keychainEntryValue2], + ]), + }); + + this.log.info(`Creating Besu Connector...`); + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + const besuConnector = await factory.create({ + rpcApiHttpHost, + rpcApiWsHost, + instanceId: uuidv4(), + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + + const accounts = [ + CryptoMaterial.accounts.userA.ethAddress, + CryptoMaterial.accounts.userB.ethAddress, + CryptoMaterial.accounts.bridge.ethAddress, + ]; + + for (const account of accounts) { + await this.besu.sendEthToAccount(account); + } + + return besuConnector; + } + + public async createIPFSConnector(): Promise { + this.log.info(`Creating Besu Connector...`); + + const ipfsClientOrOptions = create({ + url: await this.ipfs.getApiUrl(), + }); + + return new PluginObjectStoreIpfs({ + parentDir: this.ipfsParentPath, + logLevel: this.options.logLevel, + instanceId: uuidv4(), + ipfsClientOrOptions, + }); + } + + public async createClientGateway( + nodeApiHost: string, + keyPair: IOdapPluginKeyPair, + ipfsPath: string, + ): Promise { + this.log.info(`Creating Source Gateway...`); + const pluginSourceGateway = new FabricOdapGateway({ + name: "cactus-plugin-source#odapGateway", + dltIDs: ["DLT2"], + instanceId: uuidv4(), + keyPair: keyPair, + ipfsPath: ipfsPath, + fabricPath: nodeApiHost, + fabricSigningCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "bridge", + }, + fabricChannelName: "mychannel", + fabricContractName: "asset-reference-contract", + }); + + await pluginSourceGateway.database?.migrate.rollback(); + await pluginSourceGateway.database?.migrate.latest(); + + return pluginSourceGateway; + } + + public async createServerGateway( + nodeApiHost: string, + keyPair: IOdapPluginKeyPair, + ipfsPath: string, + ): Promise { + this.log.info(`Creating Recipient Gateway...`); + const pluginRecipientGateway = new BesuOdapGateway({ + name: "cactus-plugin-recipient#odapGateway", + dltIDs: ["DLT1"], + instanceId: uuidv4(), + keyPair: keyPair, + ipfsPath: ipfsPath, + besuPath: nodeApiHost, + besuWeb3SigningCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + besuContractName: AssetReferenceContractJson.contractName, + besuKeychainId: CryptoMaterial.keychains.keychain2.id, + }); + + await pluginRecipientGateway.database?.migrate.rollback(); + await pluginRecipientGateway.database?.migrate.latest(); + + return pluginRecipientGateway; + } + + public async deployFabricAssetReferenceContract( + fabricApiClient: FabricApi, + ): Promise { + const channelId = "mychannel"; + + const contractName = "asset-reference-contract"; + + const contractRelPath = + "../../../fabric-contracts/asset-reference/typescript"; + const contractDir = path.join(__dirname, contractRelPath); + + // ├── package.json + // ├── src + // │ ├── assetTransfer.ts + // │ ├── asset.ts + // │ └── index.ts + // ├── tsconfig.json + // └── tslint.json + const sourceFiles: FileBase64[] = []; + { + const filename = "./tsconfig.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./package.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./index.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./asset-reference.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./asset-reference-contract.ts"; + const relativePath = "./src/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + + let retries = 0; + while (retries <= 5) { + await fabricApiClient + .deployContractV1( + { + channelId, + ccVersion: "1.0.0", + sourceFiles, + ccName: contractName, + targetOrganizations: [this.org1Env, this.org2Env], + caFile: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + ccLabel: "asset-reference-contract", + ccLang: ChainCodeProgrammingLanguage.Typescript, + ccSequence: 1, + orderer: "orderer.example.com:7050", + ordererTLSHostnameOverride: "orderer.example.com", + connTimeout: 120, + }, + { + maxContentLength: Infinity, + maxBodyLength: Infinity, + }, + ) + .then(async (res: { data: { packageIds: any; lifecycle: any } }) => { + retries = 6; + + const { packageIds, lifecycle } = res.data; + + const { + approveForMyOrgList, + installList, + queryInstalledList, + commit, + packaging, + queryCommitted, + } = lifecycle; + + Checks.truthy(packageIds, `packageIds truthy OK`); + Checks.truthy( + Array.isArray(packageIds), + `Array.isArray(packageIds) truthy OK`, + ); + Checks.truthy(approveForMyOrgList, `approveForMyOrgList truthy OK`); + Checks.truthy( + Array.isArray(approveForMyOrgList), + `Array.isArray(approveForMyOrgList) truthy OK`, + ); + Checks.truthy(installList, `installList truthy OK`); + Checks.truthy( + Array.isArray(installList), + `Array.isArray(installList) truthy OK`, + ); + Checks.truthy(queryInstalledList, `queryInstalledList truthy OK`); + Checks.truthy( + Array.isArray(queryInstalledList), + `Array.isArray(queryInstalledList) truthy OK`, + ); + Checks.truthy(commit, `commit truthy OK`); + Checks.truthy(packaging, `packaging truthy OK`); + Checks.truthy(queryCommitted, `queryCommitted truthy OK`); + }) + .catch(() => console.log("trying to deploy fabric contract again")); + retries++; + } + } + + public async deployFabricCbdcContract( + fabricApiClient: FabricApi, + ): Promise { + const channelId = "mychannel"; + const channelName = channelId; + + const contractName = "cbdc"; + + const contractRelPath = "../../../fabric-contracts/cbdc-erc-20/javascript"; + const contractDir = path.join(__dirname, contractRelPath); + + // ├── package.json + // ├── index.js + // ├── lib + // │ ├── tokenERC20.js + const sourceFiles: FileBase64[] = []; + { + const filename = "./package.json"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./index.js"; + const relativePath = "./"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./tokenERC20.js"; + const relativePath = "./lib/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + { + const filename = "./crypto-material.json"; + const relativePath = "./crypto-material/"; + const filePath = path.join(contractDir, relativePath, filename); + const buffer = await fs.readFile(filePath); + sourceFiles.push({ + body: buffer.toString("base64"), + filepath: relativePath, + filename, + }); + } + + let retries = 0; + while (retries <= 5) { + await fabricApiClient + .deployContractV1( + { + channelId, + ccVersion: "1.0.0", + sourceFiles, + ccName: contractName, + targetOrganizations: [this.org1Env, this.org2Env], + caFile: `${this.orgCfgDir}ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem`, + ccLabel: "cbdc", + ccLang: ChainCodeProgrammingLanguage.Javascript, + ccSequence: 1, + orderer: "orderer.example.com:7050", + ordererTLSHostnameOverride: "orderer.example.com", + connTimeout: 120, + }, + { + maxContentLength: Infinity, + maxBodyLength: Infinity, + }, + ) + .then(async (res: { data: { packageIds: any; lifecycle: any } }) => { + retries = 6; + + const { packageIds, lifecycle } = res.data; + + const { + approveForMyOrgList, + installList, + queryInstalledList, + commit, + packaging, + queryCommitted, + } = lifecycle; + + Checks.truthy(packageIds, `packageIds truthy OK`); + Checks.truthy( + Array.isArray(packageIds), + `Array.isArray(packageIds) truthy OK`, + ); + Checks.truthy(approveForMyOrgList, `approveForMyOrgList truthy OK`); + Checks.truthy( + Array.isArray(approveForMyOrgList), + `Array.isArray(approveForMyOrgList) truthy OK`, + ); + Checks.truthy(installList, `installList truthy OK`); + Checks.truthy( + Array.isArray(installList), + `Array.isArray(installList) truthy OK`, + ); + Checks.truthy(queryInstalledList, `queryInstalledList truthy OK`); + Checks.truthy( + Array.isArray(queryInstalledList), + `Array.isArray(queryInstalledList) truthy OK`, + ); + Checks.truthy(commit, `commit truthy OK`); + Checks.truthy(packaging, `packaging truthy OK`); + Checks.truthy(queryCommitted, `queryCommitted truthy OK`); + + // FIXME - without this wait it randomly fails with an error claiming that + // the endorsement was impossible to be obtained. The fabric-samples script + // does the same thing, it just waits 10 seconds for good measure so there + // might not be a way for us to avoid doing this, but if there is a way we + // absolutely should not have timeouts like this, anywhere... + await new Promise((resolve) => setTimeout(resolve, 10000)); + + await fabricApiClient.runTransactionV1({ + contractName, + channelName, + params: ["name1", "symbol1", "8"], + methodName: "Initialize", + invocationType: FabricContractInvocationType.Send, + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }); + }) + .catch(() => console.log("trying to deploy fabric contract again")); + retries++; + } + } + + public async deployBesuContracts(besuApiClient: BesuApi): Promise { + const fnTag = `${this.className}#deployBesuContracts()`; + + const deployCbdcContractResponse = await besuApiClient.deployContractSolBytecodeV1( + { + keychainId: CryptoMaterial.keychains.keychain2.id, + contractName: CBDCcontractJson.contractName, + contractAbi: CBDCcontractJson.abi, + constructorArgs: [], + web3SigningCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + bytecode: CBDCcontractJson.bytecode, + gas: 10000000, + } as DeployContractSolidityBytecodeV1Request, + ); + + if (deployCbdcContractResponse == undefined) { + throw new Error(`${fnTag}, error when deploying CBDC smart contract`); + } + + const deployAssetReferenceContractResponse = await besuApiClient.deployContractSolBytecodeV1( + { + keychainId: CryptoMaterial.keychains.keychain2.id, + contractName: AssetReferenceContractJson.contractName, + contractAbi: AssetReferenceContractJson.abi, + constructorArgs: [ + deployCbdcContractResponse.data.transactionReceipt.contractAddress, + ], + web3SigningCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + bytecode: AssetReferenceContractJson.bytecode, + gas: 10000000, + } as DeployContractSolidityBytecodeV1Request, + ); + + if (deployAssetReferenceContractResponse == undefined) { + throw new Error( + `${fnTag}, error when deploying Asset Reference smart contract`, + ); + } + + // set Asset Reference smart contract address in cbdc one (sidechain contract) + const insertARContractAddress = await besuApiClient.invokeContractV1({ + contractName: CBDCcontractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "setAssetReferenceContract", + gas: 1000000, + params: [ + deployAssetReferenceContractResponse.data.transactionReceipt + .contractAddress, + ], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + } as BesuInvokeContractV1Request); + + if (insertARContractAddress == undefined) { + throw new Error( + `${fnTag}, error when setting Asset Reference smart contract address in sidechain contract`, + ); + } + + // make the owner of the sidechain contract the asset reference one + const transferOwnership = await besuApiClient.invokeContractV1({ + contractName: CBDCcontractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "transferOwnership", + gas: 1000000, + params: [ + deployAssetReferenceContractResponse.data.transactionReceipt + .contractAddress, + ], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + } as BesuInvokeContractV1Request); + + if (transferOwnership == undefined) { + throw new Error( + `${fnTag}, error when transferring the ownershop Reference smart contract address in sidechain contract`, + ); + } + + // make the owner of the asset reference contract the sidechain one + const addOwnerToAssetRefContract = await besuApiClient.invokeContractV1({ + contractName: AssetReferenceContractJson.contractName, + invocationType: EthContractInvocationType.Send, + methodName: "addOwner", + gas: 1000000, + params: [ + deployCbdcContractResponse.data.transactionReceipt.contractAddress, + ], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: Web3SigningCredentialType.PrivateKeyHex, + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + } as BesuInvokeContractV1Request); + + if (addOwnerToAssetRefContract == undefined) { + throw new Error( + `${fnTag}, error when transfering CBDC smart contract ownership`, + ); + } + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/besu-odap-gateway.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/besu-odap-gateway.ts new file mode 100644 index 0000000000..061ede2e6f --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/besu-odap-gateway.ts @@ -0,0 +1,614 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { Knex } from "knex"; +import { Configuration } from "@hyperledger/cactus-core-api"; +import { + DefaultApi as BesuApi, + Web3SigningCredential, + EthContractInvocationType, + InvokeContractV1Request as BesuInvokeContractV1Request, +} from "@hyperledger/cactus-plugin-ledger-connector-besu"; +import { + IOdapPluginKeyPair, + PluginOdapGateway, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { SessionDataRollbackActionsPerformedEnum } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript"; +import { ClientHelper } from "./client-helper"; +import { ServerHelper } from "./server-helper"; + +export interface IBesuOdapGatewayConstructorOptions { + name: string; + dltIDs: string[]; + instanceId: string; + keyPair?: IOdapPluginKeyPair; + backupGatewaysAllowed?: string[]; + + ipfsPath?: string; + + besuPath?: string; + + besuContractName?: string; + besuWeb3SigningCredential?: Web3SigningCredential; + besuKeychainId?: string; + fabricAssetID?: string; + fabricAssetSize?: string; + besuAssetID?: string; + + knexConfig?: Knex.Config; +} + +export class BesuOdapGateway extends PluginOdapGateway { + public besuApi?: BesuApi; + public besuContractName?: string; + public besuWeb3SigningCredential?: Web3SigningCredential; + public besuKeychainId?: string; + + public constructor(options: IBesuOdapGatewayConstructorOptions) { + super({ + name: options.name, + dltIDs: options.dltIDs, + instanceId: options.instanceId, + keyPair: options.keyPair, + backupGatewaysAllowed: options.backupGatewaysAllowed, + ipfsPath: options.ipfsPath, + clientHelper: new ClientHelper(), + serverHelper: new ServerHelper(), + }); + + if (options.besuPath != undefined) this.defineBesuConnection(options); + } + + private defineBesuConnection( + options: IBesuOdapGatewayConstructorOptions, + ): void { + const fnTag = `${this.className}#defineBesuConnection()`; + + const config = new Configuration({ basePath: options.besuPath }); + const apiClient = new BesuApi(config); + this.besuApi = apiClient; + const notEnoughBesuParams: boolean = + options.besuContractName == undefined || + options.besuWeb3SigningCredential == undefined || + options.besuKeychainId == undefined; + if (notEnoughBesuParams) { + throw new Error( + `${fnTag}, besu params missing. Should have: signing credentials, contract name, key chain ID, asset ID`, + ); + } + this.besuContractName = options.besuContractName; + this.besuWeb3SigningCredential = options.besuWeb3SigningCredential; + this.besuKeychainId = options.besuKeychainId; + } + + async isValidBridgeBackCBDC( + assetID: string, + amount: number, + user: string, + ): Promise { + // we should verify if the CBDC being bridged is valid or not + // e.g. if a client is trying to send more CBDC than what was escrowed + + if (this.besuApi != undefined) { + const response = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Call, + methodName: "checkValidBridgeBack", + gas: 1000000, + params: [assetID, amount, user], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (response.data.callOutput != true) { + throw new Error(`${response.statusText}`); + } + } + } + + async createAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#createAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.assetProfile == undefined || + sessionData.assetProfile.keyInformationLink == undefined || + sessionData.assetProfile.keyInformationLink.length != 3 + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + if (assetId == undefined) { + assetId = sessionData.recipientLedgerAssetID; + } + + let besuCreateAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + if (this.besuApi != undefined) { + const amount = sessionData.assetProfile.keyInformationLink[0].toString(); + const userEthAddress = sessionData.assetProfile.keyInformationLink[2].toString(); + + const besuCreateRes = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Send, + methodName: "mint", + gas: 1000000, + params: [userEthAddress, amount], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (besuCreateRes.status != 200) { + throw new Error(`${fnTag}, besu create asset error`); + } + + const besuCreateResDataJson = JSON.parse( + JSON.stringify(besuCreateRes.data), + ); + + if (besuCreateResDataJson.out == undefined) { + throw new Error(`${fnTag}, besu res data out undefined`); + } + + if (besuCreateResDataJson.out.transactionReceipt == undefined) { + throw new Error(`${fnTag}, undefined besu transact receipt`); + } + + const besuCreateAssetReceipt = + besuCreateResDataJson.out.transactionReceipt; + besuCreateAssetProof = JSON.stringify(besuCreateAssetReceipt); + } + + sessionData.commitAcknowledgementClaim = besuCreateAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset creation: ${besuCreateAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "create", + data: besuCreateAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + return besuCreateAssetProof; + } + + async deleteAsset(sessionID: string, assetID?: string): Promise { + const fnTag = `${this.className}#deleteAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if (sessionData == undefined) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + if (assetID == undefined) { + assetID = sessionData.sourceLedgerAssetID; + } + + let besuDeleteAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "delete-asset", + data: JSON.stringify(sessionData), + }); + + if (this.besuApi != undefined) { + const besuAssetDeletion = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Send, + methodName: "deleteAssetReference", + gas: 1000000, + params: [assetID], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (besuAssetDeletion.status != 200) { + throw new Error(`${fnTag}, besu delete asset error`); + } + + const besuAssetDeletionDataJson = JSON.parse( + JSON.stringify(besuAssetDeletion.data), + ); + + if (besuAssetDeletionDataJson.out == undefined) { + throw new Error(`${fnTag}, besu res data out undefined`); + } + + if (besuAssetDeletionDataJson.out.transactionReceipt == undefined) { + throw new Error(`${fnTag}, undefined besu transact receipt`); + } + + const besuAssetDeletionReceipt = + besuAssetDeletionDataJson.out.transactionReceipt; + besuDeleteAssetProof = JSON.stringify(besuAssetDeletionReceipt); + } + + sessionData.commitFinalClaim = besuDeleteAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset deletion: ${besuDeleteAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "delete", + data: besuDeleteAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "delete-asset", + data: JSON.stringify(sessionData), + }); + + return besuDeleteAssetProof; + } + + async lockAsset(sessionID: string, assetID?: string): Promise { + const fnTag = `${this.className}#lockAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if (sessionData == undefined) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + if (assetID == undefined) { + assetID = sessionData.sourceLedgerAssetID; + } + + let besuLockAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "lock-asset", + data: JSON.stringify(sessionData), + }); + + if (this.besuApi != undefined) { + const besuAssetLock = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Send, + methodName: "lockAssetReference", + gas: 1000000, + params: [assetID], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (besuAssetLock.status != 200) { + throw new Error(`${fnTag}, besu lock asset error`); + } + + const besuAssetLockDataJson = JSON.parse( + JSON.stringify(besuAssetLock.data), + ); + + if (besuAssetLockDataJson.out == undefined) { + throw new Error(`${fnTag}, besu res data out undefined`); + } + + if (besuAssetLockDataJson.out.transactionReceipt == undefined) { + throw new Error(`${fnTag}, undefined besu transact receipt`); + } + + const besuAssetLockReceipt = besuAssetLockDataJson.out.transactionReceipt; + besuLockAssetProof = JSON.stringify(besuAssetLockReceipt); + } + + sessionData.lockEvidenceClaim = besuLockAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info(`${fnTag}, proof of the asset lock: ${besuLockAssetProof}`); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "lock", + data: besuLockAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "lock-asset", + data: JSON.stringify(sessionData), + }); + + return besuLockAssetProof; + } + + async unlockAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#unlockAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.rollbackActionsPerformed == undefined || + sessionData.rollbackProofs == undefined + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let besuUnlockAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec-rollback", + operation: "unlock-asset", + data: JSON.stringify(sessionData), + }); + + if (this.besuApi != undefined) { + const assetUnlockResponse = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Send, + methodName: "unLockAssetReference", + gas: 1000000, + params: [assetId], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (assetUnlockResponse.status != 200) { + throw new Error(`${fnTag}, besu unlock asset error`); + } + + const assetUnlockResponseDataJson = JSON.parse( + JSON.stringify(assetUnlockResponse.data), + ); + + if (assetUnlockResponseDataJson.out == undefined) { + throw new Error(`${fnTag}, besu res data out undefined`); + } + + if (assetUnlockResponseDataJson.out.transactionReceipt == undefined) { + throw new Error(`${fnTag}, undefined besu transact receipt`); + } + + const besuCreateAssetReceipt = + assetUnlockResponseDataJson.out.transactionReceipt; + besuUnlockAssetProof = JSON.stringify(besuCreateAssetReceipt); + } + + sessionData.rollbackActionsPerformed.push( + SessionDataRollbackActionsPerformedEnum.Lock, + ); + sessionData.rollbackProofs.push(besuUnlockAssetProof); + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset unlock: ${besuUnlockAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof-rollback", + operation: "unlock", + data: besuUnlockAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done-rollback", + operation: "unlock-asset", + data: JSON.stringify(sessionData), + }); + + return besuUnlockAssetProof; + } + + async createAssetToRollback( + sessionID: string, + assetID?: string, + ): Promise { + const fnTag = `${this.className}#createAssetToRollback()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.rollbackActionsPerformed == undefined || + sessionData.rollbackProofs == undefined || + sessionData.assetProfile == undefined || + sessionData.assetProfile.keyInformationLink == undefined || + sessionData.assetProfile.keyInformationLink.length != 3 + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let besuCreateAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec-rollback", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + if (this.besuApi != undefined) { + const amount = sessionData.assetProfile.keyInformationLink[0].toString(); + const userEthAddress = sessionData.assetProfile.keyInformationLink[2].toString(); + + const assetCreateResponse = await this.besuApi.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Send, + methodName: "createAssetReference", + gas: 1000000, + params: [assetID, amount, userEthAddress], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (assetCreateResponse.status != 200) { + throw new Error(`${fnTag}, besu unlock asset error`); + } + + const assetCreateResponseDataJson = JSON.parse( + JSON.stringify(assetCreateResponse.data), + ); + + if (assetCreateResponseDataJson.out == undefined) { + throw new Error(`${fnTag}, besu res data out undefined`); + } + + if (assetCreateResponseDataJson.out.transactionReceipt == undefined) { + throw new Error(`${fnTag}, undefined besu transact receipt`); + } + + const besuCreateAssetReceipt = + assetCreateResponseDataJson.out.transactionReceipt; + besuCreateAssetProof = JSON.stringify(besuCreateAssetReceipt); + } + + sessionData.rollbackActionsPerformed.push( + SessionDataRollbackActionsPerformedEnum.Create, + ); + sessionData.rollbackProofs.push(besuCreateAssetProof); + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset create: ${besuCreateAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof-rollback", + operation: "create", + data: besuCreateAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done-rollback", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + return besuCreateAssetProof; + } + + async deleteAssetToRollback( + sessionID: string, + assetID?: string, + ): Promise { + // not implemented. We assume the agreement was reached after the final interactions in each ledger + // (delete in the source chain and create in the side chain) + return `not implemented: ${sessionID}, ${assetID}`; + } + + async rollback(sessionID: string) { + const fnTag = `${this.className}#rollback()`; + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.step == undefined || + sessionData.lastSequenceNumber == undefined + ) { + throw new Error(`${fnTag}, session data is undefined`); + } + + sessionData.rollback = true; + + this.log.info(`${fnTag}, rolling back session ${sessionID}`); + + if ( + this.besuApi == undefined || + this.besuContractName == undefined || + this.besuKeychainId == undefined || + this.besuWeb3SigningCredential == undefined || + sessionData.sourceLedgerAssetID == undefined || + sessionData.recipientLedgerAssetID == undefined + ) { + return; + } + + if (this.isClientGateway(sessionID)) { + if (await this.besuAssetExists(sessionData.sourceLedgerAssetID)) { + if (await this.isBesuAssetLocked(sessionData.sourceLedgerAssetID)) { + // Rollback locking of the asset + await this.unlockAsset(sessionID, sessionData.sourceLedgerAssetID); + } + } else { + // Rollback extinguishment of the asset + await this.createAssetToRollback( + sessionID, + sessionData.sourceLedgerAssetID, + ); + } + } + } + + /* Helper functions */ + async besuAssetExists(besuAssetID: string): Promise { + const fnTag = `${this.className}#besuAssetExists()`; + + const assetExists = await this.besuApi?.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Call, + methodName: "isPresent", + gas: 1000000, + params: [besuAssetID], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (assetExists == undefined) { + throw new Error(`${fnTag} the asset does not exist`); + } + + return assetExists?.data.callOutput == true; + } + + async isBesuAssetLocked(besuAssetID: string): Promise { + const fnTag = `${this.className}#isBesuAssetLocked()`; + + const assetIsLocked = await this.besuApi?.invokeContractV1({ + contractName: this.besuContractName, + invocationType: EthContractInvocationType.Call, + methodName: "isAssetLocked", + gas: 1000000, + params: [besuAssetID], + signingCredential: this.besuWeb3SigningCredential, + keychainId: this.besuKeychainId, + } as BesuInvokeContractV1Request); + + if (assetIsLocked == undefined) { + throw new Error(`${fnTag} the asset does not exist`); + } + + return assetIsLocked?.data.callOutput == true; + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/client-helper.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/client-helper.ts new file mode 100644 index 0000000000..0c720de469 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/client-helper.ts @@ -0,0 +1,141 @@ +import { SHA256 } from "crypto-js"; +import { + PluginOdapGateway, + TransferInitializationV1Request, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript"; +import { ClientGatewayHelper } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/client/client-helper"; +import { OdapMessageType } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { FabricOdapGateway } from "./fabric-odap-gateway"; +import { BesuOdapGateway } from "./besu-odap-gateway"; + +export class ClientHelper extends ClientGatewayHelper { + async sendTransferInitializationRequest( + sessionID: string, + odap: PluginOdapGateway, + remote: boolean, + ): Promise { + const fnTag = `${this.className}#sendTransferInitializationRequest()`; + + const sessionData = odap.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.id == undefined || + sessionData.step == undefined || + sessionData.version == undefined || + sessionData.maxRetries == undefined || + sessionData.maxTimeout == undefined || + sessionData.assetProfile == undefined || + sessionData.payloadProfile == undefined || + sessionData.loggingProfile == undefined || + sessionData.sourceBasePath == undefined || + sessionData.recipientBasePath == undefined || + sessionData.accessControlProfile == undefined || + sessionData.applicationProfile == undefined || + sessionData.lastSequenceNumber == undefined || + sessionData.sourceLedgerAssetID == undefined || + sessionData.sourceGatewayDltSystem == undefined || + sessionData.recipientGatewayPubkey == undefined || + sessionData.recipientLedgerAssetID == undefined || + sessionData.recipientGatewayDltSystem == undefined || + sessionData.allowedSourceBackupGateways == undefined || + sessionData.assetProfile.keyInformationLink == undefined + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + if (!odap.supportedDltIDs.includes(sessionData.recipientGatewayDltSystem)) { + throw new Error( + `${fnTag}, recipient gateway dlt system is not supported by this gateway`, + ); + } + + const initializationRequestMessage: TransferInitializationV1Request = { + messageType: OdapMessageType.InitializationRequest, + sessionID: sessionData.id, + version: sessionData.version, + // developer urn + // credential profile + payloadProfile: sessionData.payloadProfile, + applicationProfile: sessionData.applicationProfile, + loggingProfile: sessionData.loggingProfile, + accessControlProfile: sessionData.accessControlProfile, + signature: "", + sourceGatewayPubkey: odap.pubKey, + sourceGatewayDltSystem: sessionData.sourceGatewayDltSystem, + recipientGatewayPubkey: sessionData.recipientGatewayPubkey, + recipientGatewayDltSystem: sessionData.recipientGatewayDltSystem, + sequenceNumber: sessionData.lastSequenceNumber, + sourceBasePath: sessionData.sourceBasePath, + recipientBasePath: sessionData.recipientBasePath, + // escrow type + // expiry time (related to the escrow) + // multiple claims allowed + // multiple cancels allowed + // permissions + maxRetries: sessionData.maxRetries, + maxTimeout: sessionData.maxTimeout, + backupGatewaysAllowed: sessionData.allowedSourceBackupGateways, + recipientLedgerAssetID: sessionData.recipientLedgerAssetID, + sourceLedgerAssetID: sessionData.sourceLedgerAssetID, + }; + + const messageSignature = PluginOdapGateway.bufArray2HexStr( + odap.sign(JSON.stringify(initializationRequestMessage)), + ); + + initializationRequestMessage.signature = messageSignature; + + sessionData.initializationRequestMessageHash = SHA256( + JSON.stringify(initializationRequestMessage), + ).toString(); + + sessionData.clientSignatureInitializationRequestMessage = messageSignature; + + odap.sessions.set(sessionID, sessionData); + + await odap.storeOdapLog({ + sessionID: sessionID, + type: "init", + operation: "validate", + data: JSON.stringify(sessionData), + }); + + if (odap instanceof FabricOdapGateway) { + await odap + .isValidBridgeOutCBDC( + sessionData.sourceLedgerAssetID, + sessionData.assetProfile.keyInformationLink[0].toString(), // Amount + sessionData.assetProfile.keyInformationLink[1].toString(), // FabricID + sessionData.assetProfile.keyInformationLink[2].toString(), // ETH Address + ) + .catch((err) => { + throw new Error(`${err.response.data.error}`); + }); + } else if (odap instanceof BesuOdapGateway) { + await odap + .isValidBridgeBackCBDC( + sessionData.sourceLedgerAssetID, + sessionData.assetProfile.keyInformationLink[0].toString(), // Amount + sessionData.assetProfile.keyInformationLink[2].toString(), // Amount + ) + .catch((err) => { + throw new Error(`${err}`); + }); + } + + this.log.info(`${fnTag}, sending TransferInitializationRequest...`); + + if (!remote) { + return initializationRequestMessage; + } + + await odap.makeRequest( + sessionID, + PluginOdapGateway.getOdapAPI( + sessionData.recipientBasePath, + ).phase1TransferInitiationRequestV1(initializationRequestMessage), + "TransferInitializationRequest", + ); + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/fabric-odap-gateway.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/fabric-odap-gateway.ts new file mode 100644 index 0000000000..92c616fe3e --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/fabric-odap-gateway.ts @@ -0,0 +1,611 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { Knex } from "knex"; +import { Configuration } from "@hyperledger/cactus-core-api"; +import { + DefaultApi as FabricApi, + FabricContractInvocationType, + FabricSigningCredential, + RunTransactionRequest as FabricRunTransactionRequest, +} from "@hyperledger/cactus-plugin-ledger-connector-fabric"; +import { + IOdapPluginKeyPair, + PluginOdapGateway, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { SessionDataRollbackActionsPerformedEnum } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript"; +import { ClientHelper } from "./client-helper"; +import { ServerHelper } from "./server-helper"; + +export interface IFabricOdapGatewayConstructorOptions { + name: string; + dltIDs: string[]; + instanceId: string; + keyPair?: IOdapPluginKeyPair; + backupGatewaysAllowed?: string[]; + + ipfsPath?: string; + fabricPath?: string; + + fabricSigningCredential?: FabricSigningCredential; + fabricChannelName?: string; + fabricContractName?: string; + + knexConfig?: Knex.Config; +} + +export class FabricOdapGateway extends PluginOdapGateway { + public fabricApi?: FabricApi; + public fabricSigningCredential?: FabricSigningCredential; + public fabricChannelName?: string; + public fabricContractName?: string; + + public constructor(options: IFabricOdapGatewayConstructorOptions) { + super({ + name: options.name, + dltIDs: options.dltIDs, + instanceId: options.instanceId, + keyPair: options.keyPair, + backupGatewaysAllowed: options.backupGatewaysAllowed, + ipfsPath: options.ipfsPath, + clientHelper: new ClientHelper(), + serverHelper: new ServerHelper(), + }); + + if (options.fabricPath != undefined) this.defineFabricConnection(options); + } + + private defineFabricConnection( + options: IFabricOdapGatewayConstructorOptions, + ): void { + const fnTag = `${this.className}#defineFabricConnection()`; + + const config = new Configuration({ basePath: options.fabricPath }); + const apiClient = new FabricApi(config); + this.fabricApi = apiClient; + const notEnoughFabricParams: boolean = + options.fabricSigningCredential == undefined || + options.fabricChannelName == undefined || + options.fabricContractName == undefined; + if (notEnoughFabricParams) { + throw new Error( + `${fnTag}, fabric params missing should have: signing credentials, contract name, channel name, asset ID`, + ); + } + this.fabricSigningCredential = options.fabricSigningCredential; + this.fabricChannelName = options.fabricChannelName; + this.fabricContractName = options.fabricContractName; + } + + async isValidBridgeOutCBDC( + assetID: string, + amount: number, + fabricID: string, + ethAddress: string, + ) { + // we should verify if the CBDC being bridged is valid or not + // e.g. if a client is trying to send CBDC to another client in the sidechain, this should fail + + if (this.fabricApi != undefined) { + await this.fabricApi.runTransactionV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: this.fabricContractName, + invocationType: FabricContractInvocationType.Call, + methodName: "CheckValidBridgeOut", + params: [assetID, amount, fabricID, ethAddress], + } as FabricRunTransactionRequest); + } + } + + async isValidBridgeBackCBDC(fabricID: string, ethAddress: string) { + // we should verify if the CBDC being bridged is valid or not + // e.g. if a client is trying to send CBDC to another client in the sidechain, this should fail + + if (this.fabricApi != undefined) { + await this.fabricApi.runTransactionV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: this.fabricContractName, + invocationType: FabricContractInvocationType.Call, + methodName: "CheckValidBridgeBack", + params: [fabricID, ethAddress], + } as FabricRunTransactionRequest); + } + } + + async lockAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#lockAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if (sessionData == undefined) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let fabricLockAssetProof = ""; + + if (assetId == undefined) { + assetId = sessionData.sourceLedgerAssetID; + } + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "lock-asset", + data: JSON.stringify(sessionData), + }); + + if (this.fabricApi != undefined) { + const response = await this.fabricApi.runTransactionV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: this.fabricContractName, + invocationType: FabricContractInvocationType.Send, + methodName: "LockAssetReference", + params: [assetId], + } as FabricRunTransactionRequest); + + const receiptLockRes = await this.fabricApi.getTransactionReceiptByTxIDV1( + { + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: "qscc", + invocationType: FabricContractInvocationType.Call, + methodName: "GetBlockByTxID", + params: [this.fabricChannelName, response.data.transactionId], + } as FabricRunTransactionRequest, + ); + + this.log.warn(receiptLockRes.data); + fabricLockAssetProof = JSON.stringify(receiptLockRes.data); + } + + sessionData.lockEvidenceClaim = fabricLockAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info(`${fnTag}, proof of the asset lock: ${fabricLockAssetProof}`); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "lock", + data: fabricLockAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "lock-asset", + data: JSON.stringify(sessionData), + }); + + return fabricLockAssetProof; + } + + async unlockAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#unlockAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.rollbackActionsPerformed == undefined || + sessionData.rollbackProofs == undefined + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let fabricUnlockAssetProof = ""; + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec-rollback", + operation: "unlock-asset", + data: JSON.stringify(sessionData), + }); + + if (this.fabricApi != undefined) { + const response = await this.fabricApi.runTransactionV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: this.fabricContractName, + invocationType: FabricContractInvocationType.Send, + methodName: "UnlockAssetReference", + params: [assetId], + } as FabricRunTransactionRequest); + + const receiptUnlock = await this.fabricApi.getTransactionReceiptByTxIDV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: "qscc", + invocationType: FabricContractInvocationType.Call, + methodName: "GetBlockByTxID", + params: [this.fabricChannelName, response.data.transactionId], + } as FabricRunTransactionRequest); + + this.log.warn(receiptUnlock.data); + fabricUnlockAssetProof = JSON.stringify(receiptUnlock.data); + } + + sessionData.rollbackActionsPerformed.push( + SessionDataRollbackActionsPerformedEnum.Unlock, + ); + sessionData.rollbackProofs.push(fabricUnlockAssetProof); + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset unlock: ${fabricUnlockAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof-rollback", + operation: "unlock", + data: fabricUnlockAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done-rollback", + operation: "unlock-asset", + data: JSON.stringify(sessionData), + }); + + return fabricUnlockAssetProof; + } + + async createAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#createAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.assetProfile == undefined || + sessionData.assetProfile.keyInformationLink == undefined || + sessionData.assetProfile.keyInformationLink.length != 3 + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let fabricCreateAssetProof = ""; + + if (assetId == undefined) { + assetId = sessionData.recipientLedgerAssetID; + } + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + if (this.fabricApi != undefined) { + const amount = sessionData.assetProfile.keyInformationLink[0].toString(); + const fabricClientID = sessionData.assetProfile.keyInformationLink[1].toString(); + const userEthAddress = sessionData.assetProfile.keyInformationLink[2].toString(); + + const response = await this.fabricApi.runTransactionV1({ + contractName: this.fabricContractName, + channelName: this.fabricChannelName, + params: [amount, fabricClientID, userEthAddress], + methodName: "Refund", + invocationType: FabricContractInvocationType.Send, + signingCredential: this.fabricSigningCredential, + } as FabricRunTransactionRequest); + + const receiptCreateRes = await this.fabricApi.getTransactionReceiptByTxIDV1( + { + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: "qscc", + invocationType: FabricContractInvocationType.Call, + methodName: "GetBlockByTxID", + params: [this.fabricChannelName, response.data.transactionId], + } as FabricRunTransactionRequest, + ); + + this.log.warn(receiptCreateRes.data); + fabricCreateAssetProof = JSON.stringify(receiptCreateRes.data); + } + + sessionData.commitAcknowledgementClaim = fabricCreateAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset create: ${fabricCreateAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "create", + data: fabricCreateAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + return fabricCreateAssetProof; + } + + async deleteAsset(sessionID: string, assetId?: string): Promise { + const fnTag = `${this.className}#deleteAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if (sessionData == undefined) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let fabricDeleteAssetProof = ""; + + if (assetId == undefined) { + assetId = sessionData.sourceLedgerAssetID; + } + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "delete-asset", + data: JSON.stringify(sessionData), + }); + + if (this.fabricApi != undefined) { + const deleteRes = await this.fabricApi.runTransactionV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: this.fabricContractName, + invocationType: FabricContractInvocationType.Send, + methodName: "DeleteAssetReference", + params: [assetId], + } as FabricRunTransactionRequest); + + const receiptDeleteRes = await this.fabricApi.getTransactionReceiptByTxIDV1( + { + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: "qscc", + invocationType: FabricContractInvocationType.Call, + methodName: "GetBlockByTxID", + params: [this.fabricChannelName, deleteRes.data.transactionId], + } as FabricRunTransactionRequest, + ); + + this.log.warn(receiptDeleteRes.data); + fabricDeleteAssetProof = JSON.stringify(receiptDeleteRes.data); + } + + sessionData.commitFinalClaim = fabricDeleteAssetProof; + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset deletion: ${fabricDeleteAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof", + operation: "delete", + data: fabricDeleteAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "delete-asset", + data: JSON.stringify(sessionData), + }); + + return fabricDeleteAssetProof; + } + + async createAssetToRollback( + sessionID: string, + assetID?: string, + ): Promise { + const fnTag = `${this.className}#createAsset()`; + + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + this.fabricChannelName == undefined || + this.fabricContractName == undefined || + this.fabricSigningCredential == undefined || + sessionData.rollbackProofs == undefined || + sessionData.rollbackActionsPerformed == undefined || + sessionData.assetProfile == undefined || + sessionData.assetProfile.keyInformationLink == undefined || + sessionData.assetProfile.keyInformationLink.length != 3 + ) { + throw new Error(`${fnTag}, session data is not correctly initialized`); + } + + let fabricCreateAssetProof = ""; + + if (assetID == undefined) { + assetID = sessionData.recipientLedgerAssetID; + } + + await this.storeOdapLog({ + sessionID: sessionID, + type: "exec-rollback", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + if (this.fabricApi != undefined) { + const amount = sessionData.assetProfile.keyInformationLink[0].toString(); + const userEthAddress = sessionData.assetProfile.keyInformationLink[2].toString(); + + const response = await this.fabricApi.runTransactionV1({ + contractName: this.fabricContractName, + channelName: this.fabricChannelName, + params: [assetID, amount, userEthAddress], + methodName: "CreateAssetReference", + invocationType: FabricContractInvocationType.Send, + signingCredential: this.fabricSigningCredential, + } as FabricRunTransactionRequest); + + const receiptCreate = await this.fabricApi.getTransactionReceiptByTxIDV1({ + signingCredential: this.fabricSigningCredential, + channelName: this.fabricChannelName, + contractName: "qscc", + invocationType: FabricContractInvocationType.Call, + methodName: "GetBlockByTxID", + params: [this.fabricChannelName, response.data.transactionId], + } as FabricRunTransactionRequest); + + this.log.warn(receiptCreate.data); + fabricCreateAssetProof = JSON.stringify(receiptCreate.data); + } + + sessionData.rollbackActionsPerformed.push( + SessionDataRollbackActionsPerformedEnum.Create, + ); + + sessionData.rollbackProofs.push(fabricCreateAssetProof); + + this.sessions.set(sessionID, sessionData); + + this.log.info( + `${fnTag}, proof of the asset creation: ${fabricCreateAssetProof}`, + ); + + await this.storeOdapProof({ + sessionID: sessionID, + type: "proof-rollback", + operation: "create", + data: fabricCreateAssetProof, + }); + + await this.storeOdapLog({ + sessionID: sessionID, + type: "done-rollback", + operation: "create-asset", + data: JSON.stringify(sessionData), + }); + + return fabricCreateAssetProof; + } + + async deleteAssetToRollback( + sessionID: string, + assetID?: string, + ): Promise { + // not implemented. We assume the agreement was reached after the final interactions in each ledger + // (delete in the source chain and create in the side chain) + return `not implemented: ${sessionID}, ${assetID}`; + } + + async rollback(sessionID: string): Promise { + const fnTag = `${this.className}#rollback()`; + const sessionData = this.sessions.get(sessionID); + + if ( + sessionData == undefined || + sessionData.step == undefined || + sessionData.lastSequenceNumber == undefined + ) { + throw new Error(`${fnTag}, session data is undefined`); + } + + sessionData.rollback = true; + + this.log.info(`${fnTag}, rolling back session ${sessionID}`); + + if ( + this.fabricApi == undefined || + this.fabricContractName == undefined || + this.fabricChannelName == undefined || + this.fabricSigningCredential == undefined || + sessionData.sourceLedgerAssetID == undefined || + sessionData.recipientLedgerAssetID == undefined + ) { + return; + } + + if (this.isClientGateway(sessionID)) { + if (await this.fabricAssetExists(sessionData.sourceLedgerAssetID)) { + if (await this.isFabricAssetLocked(sessionData.sourceLedgerAssetID)) { + // Rollback locking of the asset + await this.unlockAsset(sessionID, sessionData.sourceLedgerAssetID); + } + } else { + // Rollback extinguishment of the asset + await this.createAssetToRollback( + sessionID, + sessionData.sourceLedgerAssetID, + ); + } + } + } + + /* Helper functions */ + public async fabricAssetExists( + fabricAssetID: string, + ): Promise { + const fnTag = `${this.className}#fabricAssetExists()`; + + if ( + this.fabricContractName == undefined || + this.fabricChannelName == undefined || + this.fabricSigningCredential == undefined + ) { + throw new Error(`${fnTag} fabric config is not defined`); + } + + const assetExists = await this.fabricApi?.runTransactionV1({ + contractName: this.fabricContractName, + channelName: this.fabricChannelName, + params: [fabricAssetID], + methodName: "AssetReferenceExists", + invocationType: FabricContractInvocationType.Send, + signingCredential: this.fabricSigningCredential, + }); + + if (assetExists == undefined) { + throw new Error(`${fnTag} the asset does not exist`); + } + + return assetExists?.data.functionOutput == "true"; + } + + public async isFabricAssetLocked( + fabricAssetID: string, + ): Promise { + const fnTag = `${this.className}#isFabricAssetLocked()`; + + if ( + this.fabricContractName == undefined || + this.fabricChannelName == undefined || + this.fabricSigningCredential == undefined + ) { + throw new Error(`${fnTag} fabric config is not defined`); + } + + const assetIsLocked = await this.fabricApi?.runTransactionV1({ + contractName: this.fabricContractName, + channelName: this.fabricChannelName, + params: [fabricAssetID], + methodName: "IsAssetReferenceLocked", + invocationType: FabricContractInvocationType.Send, + signingCredential: this.fabricSigningCredential, + }); + + if (assetIsLocked == undefined) { + throw new Error(`${fnTag} the asset does not exist`); + } + + return assetIsLocked?.data.functionOutput == "true"; + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/server-helper.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/server-helper.ts new file mode 100644 index 0000000000..c80226dee9 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/odap-extension/server-helper.ts @@ -0,0 +1,129 @@ +import { SHA256 } from "crypto-js"; +import { + SessionData, + TransferInitializationV1Request, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript"; +import { + OdapMessageType, + PluginOdapGateway, +} from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/plugin-odap-gateway"; +import { ServerGatewayHelper } from "@hyperledger/cactus-plugin-odap-hermes/src/main/typescript/gateway/server/server-helper"; +import { FabricOdapGateway } from "./fabric-odap-gateway"; + +export class ServerHelper extends ServerGatewayHelper { + async checkValidInitializationRequest( + request: TransferInitializationV1Request, + odap: PluginOdapGateway, + ): Promise { + const fnTag = `${odap.className}#checkValidInitializationRequest()`; + + const sessionData: SessionData = {}; + const recvTimestamp: string = Date.now().toString(); + const sessionID = request.sessionID; + + sessionData.id = sessionID; + sessionData.step = 2; + sessionData.initializationRequestMessageRcvTimeStamp = recvTimestamp; + + odap.sessions.set(sessionID, sessionData); + + await odap.storeOdapLog({ + sessionID: sessionID, + type: "exec", + operation: "validate", + data: JSON.stringify(sessionData), + }); + + if (request.messageType != OdapMessageType.InitializationRequest) { + throw new Error( + `${fnTag}, wrong message type for TransferInitializationRequest`, + ); + } + + if (!odap.verifySignature(request, request.sourceGatewayPubkey)) { + throw new Error( + `${fnTag}, TransferInitializationRequest message signature verification failed`, + ); + } + + if (!odap.supportedDltIDs.includes(request.sourceGatewayDltSystem)) { + throw new Error( + `${fnTag}, source gateway dlt system is not supported by this gateway`, + ); + } + + if (request.payloadProfile.assetProfile.issuer != "CB1") { + throw new Error(`${fnTag}, asset issuer not recognized`); + } + + if (request.payloadProfile.assetProfile.assetCode != "CBDC1") { + throw new Error(`${fnTag}, asset code not recognized`); + } + + if (request.payloadProfile.assetProfile.keyInformationLink?.length != 3) { + throw new Error(`${fnTag}, CBDC parameters not specified`); + } + + const expiryDate: string = + request.payloadProfile.assetProfile.expirationDate; + const isDataExpired: boolean = new Date() >= new Date(expiryDate); + if (isDataExpired) { + throw new Error(`${fnTag}, asset has expired`); + } + + sessionData.version = request.version; + sessionData.maxRetries = request.maxRetries; + sessionData.maxTimeout = request.maxTimeout; + + sessionData.allowedSourceBackupGateways = request.backupGatewaysAllowed; + sessionData.allowedRecipientBackupGateways = odap.backupGatewaysAllowed; + + sessionData.sourceBasePath = request.sourceBasePath; + sessionData.recipientBasePath = request.recipientBasePath; + sessionData.lastSequenceNumber = request.sequenceNumber; + sessionData.loggingProfile = request.loggingProfile; + sessionData.accessControlProfile = request.accessControlProfile; + sessionData.payloadProfile = request.payloadProfile; + sessionData.applicationProfile = request.applicationProfile; + sessionData.assetProfile = request.payloadProfile.assetProfile; + sessionData.sourceGatewayPubkey = request.sourceGatewayPubkey; + sessionData.sourceGatewayDltSystem = request.sourceGatewayDltSystem; + sessionData.recipientGatewayPubkey = request.recipientGatewayPubkey; + sessionData.recipientGatewayDltSystem = request.recipientGatewayDltSystem; + sessionData.rollbackActionsPerformed = []; + sessionData.rollbackProofs = []; + sessionData.lastMessageReceivedTimestamp = new Date().toString(); + sessionData.recipientLedgerAssetID = request.recipientLedgerAssetID; + sessionData.sourceLedgerAssetID = request.sourceLedgerAssetID; + + sessionData.initializationRequestMessageHash = SHA256( + JSON.stringify(request), + ).toString(); + + sessionData.clientSignatureInitializationRequestMessage = request.signature; + + sessionData.initializationRequestMessageProcessedTimeStamp = Date.now().toString(); + + odap.sessions.set(request.sessionID, sessionData); + + if (odap instanceof FabricOdapGateway) { + await odap + .isValidBridgeBackCBDC( + request.payloadProfile.assetProfile.keyInformationLink[1].toString(), // FabricID + request.payloadProfile.assetProfile.keyInformationLink[2].toString(), // ETH Address + ) + .catch((err) => { + throw new Error(`${err.response.data.error}`); + }); + } + + await odap.storeOdapLog({ + sessionID: sessionID, + type: "done", + operation: "validate", + data: JSON.stringify(sessionData), + }); + + this.log.info(`TransferInitializationRequest passed all checks.`); + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/public-api.ts b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/public-api.ts new file mode 100755 index 0000000000..b62ece0e7a --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/main/typescript/public-api.ts @@ -0,0 +1,2 @@ +export { CbdcBridgingApp } from "./cbdc-bridging-app"; +export { launchApp } from "./cbdc-bridging-app-cli"; diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/AssetReferenceContract.json b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/AssetReferenceContract.json new file mode 100644 index 0000000000..1156577ad5 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/AssetReferenceContract.json @@ -0,0 +1,5778 @@ +{ + "contractName": "AssetReferenceContract", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + } + ], + "name": "OwnershipRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "addOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "checkValidBridgeBack", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "createAssetReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "deleteAssetReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAllAssetReferences", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "id", + "type": "string" + }, + { + "internalType": "bool", + "name": "isLocked", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "internalType": "struct AssetReference[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "getAssetReference", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "id", + "type": "string" + }, + { + "internalType": "bool", + "name": "isLocked", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "internalType": "struct AssetReference", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "isAssetLocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "isPresent", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "lockAssetReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "removeItemFromList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oldOwner", + "type": "address" + } + ], + "name": "removeOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resetAssetRefsList", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "id", + "type": "string" + } + ], + "name": "unLockAssetReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "metadata": "{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"}],\"name\":\"OwnershipRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"addOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"checkValidBridgeBack\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"createAssetReference\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"deleteAssetReference\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAssetReferences\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"isLocked\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"internalType\":\"struct AssetReference[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"getAssetReference\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"isLocked\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"internalType\":\"struct AssetReference\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"isAssetLocked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"isPresent\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"lockAssetReference\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owners\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_index\",\"type\":\"uint256\"}],\"name\":\"removeItemFromList\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oldOwner\",\"type\":\"address\"}],\"name\":\"removeOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resetAssetRefsList\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"id\",\"type\":\"string\"}],\"name\":\"unLockAssetReference\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"addOwner(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"owners()\":{\"details\":\"Returns the address of the current owners.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol\":\"AssetReferenceContract\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol\":{\"keccak256\":\"0x0e64f282a7a93abd1a52b21d0a9995ea0252568fa4fdb35206e7a036368528fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c700a282ac8079d8ca12e687f58d0eb8665d16f433ea0824bd0fc2424132a167\",\"dweb:/ipfs/QmPsSRdwFppSrDnrjfRVAcKQpQK4DZMW73KGNY3bNBCNsX\"]},\"/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol\":{\"keccak256\":\"0xf4bc191b290664b626acc4e62cb09c3d95a83e836f9a35f83f8ace72a5103c29\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://55fe1524d9dfb3256b44560103c52b5b74313aea0362483cc18d7aa51a90f864\",\"dweb:/ipfs/QmWkcmUzyZP6uWHJtpfB2FvareUH4tXySQJmPEGuv3mgGZ\"]}},\"version\":1}", + "bytecode": "608060405234801561001057600080fd5b50604051611a0f380380611a0f83398101604081905261002f916100d3565b6100383361005d565b600180546001600160a01b0319166001600160a01b0392909216919091179055610103565b600080546001810182558180527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630180546001600160a01b0319166001600160a01b03841690811790915560405190917fb4cd6c6140e1db788f68710738b06ef4d0df6a884a06a088433a4c2f5e036e7c91a250565b6000602082840312156100e557600080fd5b81516001600160a01b03811681146100fc57600080fd5b9392505050565b6118fd806101126000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80637065cb4811610097578063affe39c111610066578063affe39c1146101f5578063b1fa25801461020a578063bc5482751461022a578063c8da01af1461023d57600080fd5b80637065cb48146101a95780638ef45293146101bc5780639ca27299146101cf578063ade3222b146101e257600080fd5b806342966c68116100d357806342966c681461016857806363773a6c1461017b578063663bcc921461018e5780636b4bd58c146101a157600080fd5b8063173825d9146101055780631ae4eb681461011a57806321db00b31461014257806340c10f1914610155575b600080fd5b6101186101133660046111d6565b610252565b005b61012d610128366004611241565b6102d0565b60405190151581526020015b60405180910390f35b610118610150366004611283565b610303565b61011861016336600461129c565b610476565b610118610176366004611283565b610563565b610118610189366004611241565b610647565b61011861019c3660046112c6565b610715565b6101186108fb565b6101186101b73660046111d6565b610923565b6101186101ca366004611241565b610999565b61012d6101dd3660046112c6565b6109db565b6101186101f0366004611241565b610a74565b6101fd610c4d565b6040516101399190611323565b61021d610218366004611241565b610caf565b60405161013991906113f1565b61012d610238366004611241565b610dc2565b610245610df1565b6040516101399190611404565b61025a610f14565b6001600160a01b0381166102c45760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206f6c64206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6102cd81610fc3565b50565b6000600283836040516102e4929190611466565b9081526040519081900360200190206001015460ff1690505b92915050565b60045481106103495760405162461bcd60e51b81526020600482015260126024820152711a5b99195e081bdd5d081bd988189bdd5b9960721b60448201526064016102bb565b805b60045461035a9060019061148c565b81101561041a57600461036e82600161149f565b8154811061037e5761037e6114b2565b90600052602060002090600402016004828154811061039f5761039f6114b2565b60009182526020909120600490910201806103ba838261157b565b50600182810154908201805460ff909216151560ff1990921691909117905560028083015490820155600391820154910180546001600160a01b0319166001600160a01b039092169190911790558061041281611656565b91505061034b565b50600480548061042c5761042c61166f565b6000828152602081206000199092019160048302019061044c828261116c565b5060018101805460ff191690556000600282015560030180546001600160a01b0319169055905550565b61047e610f14565b6001546040516001600160a01b03848116602483015260448201849052600092169060640160408051601f198184030181529181526020820180516001600160e01b03166340c10f1960e01b179052516104d89190611685565b6000604051808303816000865af19150503d8060008114610515576040519150601f19603f3d011682016040523d82523d6000602084013e61051a565b606091505b505090508061055e5760405162461bcd60e51b815260206004820152601060248201526f1b5a5b9d0818d85b1b0819985a5b195960821b60448201526064016102bb565b505050565b61056b610f14565b600154604051602481018390526000916001600160a01b03169060440160408051601f198184030181529181526020820180516001600160e01b0316630852cd8d60e31b179052516105bd9190611685565b6000604051808303816000865af19150503d80600081146105fa576040519150601f19603f3d011682016040523d82523d6000602084013e6105ff565b606091505b50509050806106435760405162461bcd60e51b815260206004820152601060248201526f189d5c9b8818d85b1b0819985a5b195960821b60448201526064016102bb565b5050565b61064f610f14565b6106598282610dc2565b6106755760405162461bcd60e51b81526004016102bb906116a1565b61067f82826102d0565b156106da5760405162461bcd60e51b815260206004820152602560248201527f546865206173736574207265666572656e636520697320616c7265616479206c6044820152641bd8dad95960da1b60648201526084016102bb565b6001600283836040516106ee929190611466565b908152604051908190036020019020600101805491151560ff199092169190911790555050565b61071d610f14565b838360028686604051610731929190611466565b9081526040519081900360200190209161074c9190836116e3565b508160028585604051610760929190611466565b908152602001604051809103902060020181905550600060028585604051610789929190611466565b908152604051908190036020018120600101805492151560ff199093169290921790915581906002906107bf9087908790611466565b908152602001604051809103902060030160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600160038585604051610808929190611466565b908152604080519182900360209081018320805494151560ff199095169490941790935560a0601f870184900490930282018301905260808101858152600492829190889088908190850183828082843760009201829052509385525050506020808301829052604083018790526001600160a01b03861660609093019290925283546001810185559381522081519192600402019081906108aa908261179d565b50602082015160018201805460ff191691151591909117905560408201516002820155606090910151600390910180546001600160a01b0319166001600160a01b0390921691909117905550505050565b60005b6004548110156102cd5761091181610303565b8061091b81611656565b9150506108fe565b61092b610f14565b6001600160a01b0381166109905760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016102bb565b6102cd816110f6565b6109a1610f14565b6109ab8282610dc2565b6109c75760405162461bcd60e51b81526004016102bb906116a1565b6000600283836040516106ee929190611466565b60006109e78585610dc2565b610a035760405162461bcd60e51b81526004016102bb906116a1565b8260028686604051610a16929190611466565b90815260200160405180910390206002015410158015610a6b5750816001600160a01b031660028686604051610a4d929190611466565b908152604051908190036020019020600301546001600160a01b0316145b95945050505050565b610a7c610f14565b610a868282610dc2565b610aa25760405162461bcd60e51b81526004016102bb906116a1565b610aac82826102d0565b610af85760405162461bcd60e51b815260206004820152601d60248201527f546865206173736574207265666572656e6365206973206c6f636b656400000060448201526064016102bb565b610b2460028383604051610b0d929190611466565b908152602001604051809103902060020154610563565b60028282604051610b36929190611466565b9081526040519081900360200190206000610b51828261116c565b5060018101805460ff19169055600060028201819055600391820180546001600160a01b0319169055604051909190610b8d9085908590611466565b908152604051908190036020019020805491151560ff1990921691909117905560005b60045481101561055e578282604051602001610bcd929190611466565b6040516020818303038152906040528051906020012060048281548110610bf657610bf66114b2565b9060005260206000209060040201600001604051602001610c179190611851565b6040516020818303038152906040528051906020012003610c3b57610c3b81610303565b80610c4581611656565b915050610bb0565b60606000805480602002602001604051908101604052809291908181526020018280548015610ca557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c87575b5050505050905090565b604080516080810182526060808252600060208301819052828401819052908201529051600290610ce39085908590611466565b9081526020016040518091039020604051806080016040529081600082018054610d0c906114c8565b80601f0160208091040260200160405190810160405280929190818152602001828054610d38906114c8565b8015610d855780601f10610d5a57610100808354040283529160200191610d85565b820191906000526020600020905b815481529060010190602001808311610d6857829003601f168201915b5050509183525050600182015460ff1615156020820152600282015460408201526003909101546001600160a01b03166060909101529392505050565b600060038383604051610dd6929190611466565b9081526040519081900360200190205460ff16905092915050565b60606004805480602002602001604051908101604052809291908181526020016000905b82821015610f0b5783829060005260206000209060040201604051806080016040529081600082018054610e48906114c8565b80601f0160208091040260200160405190810160405280929190818152602001828054610e74906114c8565b8015610ec15780601f10610e9657610100808354040283529160200191610ec1565b820191906000526020600020905b815481529060010190602001808311610ea457829003601f168201915b505050918352505060018281015460ff161515602080840191909152600284015460408401526003909301546001600160a01b031660609092019190915291835292019101610e15565b50505050905090565b6000805b600054811015610f7057336001600160a01b031660008281548110610f3f57610f3f6114b2565b6000918252602090912001546001600160a01b031603610f5e57600191505b80610f6881611656565b915050610f18565b506001811515146102cd5760405162461bcd60e51b815260206004820152601f60248201527f4f776e61626c653a2063616c6c6572206973206e6f7420616e206f776e65720060448201526064016102bb565b60005b6000548110156110be57816001600160a01b031660008281548110610fed57610fed6114b2565b6000918252602090912001546001600160a01b0316036110ac57600080546110179060019061148c565b81548110611027576110276114b2565b600091825260208220015481546001600160a01b03909116919083908110611051576110516114b2565b6000918252602082200180546001600160a01b0319166001600160a01b0393909316929092179091558054806110895761108961166f565b600082815260209020810160001990810180546001600160a01b03191690550190555b806110b681611656565b915050610fc6565b506040516001600160a01b038216907f86d076ecf250a6d90a67a7c75317f44709d5001395ecf1df6d9dad5278f1e68190600090a250565b600080546001810182558180527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630180546001600160a01b0319166001600160a01b03841690811790915560405190917fb4cd6c6140e1db788f68710738b06ef4d0df6a884a06a088433a4c2f5e036e7c91a250565b508054611178906114c8565b6000825580601f10611188575050565b601f0160209004906000526020600020908101906102cd91905b808211156111b657600081556001016111a2565b5090565b80356001600160a01b03811681146111d157600080fd5b919050565b6000602082840312156111e857600080fd5b6111f1826111ba565b9392505050565b60008083601f84011261120a57600080fd5b50813567ffffffffffffffff81111561122257600080fd5b60208301915083602082850101111561123a57600080fd5b9250929050565b6000806020838503121561125457600080fd5b823567ffffffffffffffff81111561126b57600080fd5b611277858286016111f8565b90969095509350505050565b60006020828403121561129557600080fd5b5035919050565b600080604083850312156112af57600080fd5b6112b8836111ba565b946020939093013593505050565b600080600080606085870312156112dc57600080fd5b843567ffffffffffffffff8111156112f357600080fd5b6112ff878288016111f8565b90955093505060208501359150611318604086016111ba565b905092959194509250565b6020808252825182820181905260009190848201906040850190845b818110156113645783516001600160a01b03168352928401929184019160010161133f565b50909695505050505050565b60005b8381101561138b578181015183820152602001611373565b50506000910152565b600081516080845280518060808601526113b58160a0870160208501611370565b602084810151151590860152604080850151908601526060938401516001600160a01b0316938501939093525050601f01601f19160160a00190565b6020815260006111f16020830184611394565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561145957603f19888603018452611447858351611394565b9450928501929085019060010161142b565b5092979650505050505050565b8183823760009101908152919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156102fd576102fd611476565b808201808211156102fd576102fd611476565b634e487b7160e01b600052603260045260246000fd5b600181811c908216806114dc57607f821691505b6020821081036114fc57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b601f82111561055e57600081815260208120601f850160051c8101602086101561153f5750805b601f850160051c820191505b8181101561155e5782815560010161154b565b505050505050565b600019600383901b1c191660019190911b1790565b818103611586575050565b61159082546114c8565b67ffffffffffffffff8111156115a8576115a8611502565b6115bc816115b684546114c8565b84611518565b6000601f8211600181146115ea57600083156115d85750848201545b6115e28482611566565b85555061164f565b600085815260209020601f19841690600086815260209020845b838110156116245782860154825560019586019590910190602001611604565b50858310156116425781850154600019600388901b60f8161c191681555b50505060018360011b0184555b5050505050565b60006001820161166857611668611476565b5060010190565b634e487b7160e01b600052603160045260246000fd5b60008251611697818460208701611370565b9190910192915050565b60208082526022908201527f546865206173736574207265666572656e636520646f6573206e6f74206578696040820152611cdd60f21b606082015260800190565b67ffffffffffffffff8311156116fb576116fb611502565b61170f8361170983546114c8565b83611518565b6000601f84116001811461173d576000851561172b5750838201355b6117358682611566565b84555061164f565b600083815260209020601f19861690835b8281101561176e578685013582556020948501946001909201910161174e565b508682101561178b5760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b815167ffffffffffffffff8111156117b7576117b7611502565b6117c5816115b684546114c8565b602080601f8311600181146117f457600084156117e25750858301515b6117ec8582611566565b86555061155e565b600085815260208120601f198616915b8281101561182357888601518255948401946001909101908401611804565b50858210156118415787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600080835461185f816114c8565b60018281168015611877576001811461188c576118bb565b60ff19841687528215158302870194506118bb565b8760005260208060002060005b858110156118b25781548a820152908401908201611899565b50505082870194505b5092969550505050505056fea26469706673582212204e3defbf3329eeb8a9a2352d01161391069b644ab4b4adcc96c509b58240729a64736f6c63430008100033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106101005760003560e01c80637065cb4811610097578063affe39c111610066578063affe39c1146101f5578063b1fa25801461020a578063bc5482751461022a578063c8da01af1461023d57600080fd5b80637065cb48146101a95780638ef45293146101bc5780639ca27299146101cf578063ade3222b146101e257600080fd5b806342966c68116100d357806342966c681461016857806363773a6c1461017b578063663bcc921461018e5780636b4bd58c146101a157600080fd5b8063173825d9146101055780631ae4eb681461011a57806321db00b31461014257806340c10f1914610155575b600080fd5b6101186101133660046111d6565b610252565b005b61012d610128366004611241565b6102d0565b60405190151581526020015b60405180910390f35b610118610150366004611283565b610303565b61011861016336600461129c565b610476565b610118610176366004611283565b610563565b610118610189366004611241565b610647565b61011861019c3660046112c6565b610715565b6101186108fb565b6101186101b73660046111d6565b610923565b6101186101ca366004611241565b610999565b61012d6101dd3660046112c6565b6109db565b6101186101f0366004611241565b610a74565b6101fd610c4d565b6040516101399190611323565b61021d610218366004611241565b610caf565b60405161013991906113f1565b61012d610238366004611241565b610dc2565b610245610df1565b6040516101399190611404565b61025a610f14565b6001600160a01b0381166102c45760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206f6c64206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6102cd81610fc3565b50565b6000600283836040516102e4929190611466565b9081526040519081900360200190206001015460ff1690505b92915050565b60045481106103495760405162461bcd60e51b81526020600482015260126024820152711a5b99195e081bdd5d081bd988189bdd5b9960721b60448201526064016102bb565b805b60045461035a9060019061148c565b81101561041a57600461036e82600161149f565b8154811061037e5761037e6114b2565b90600052602060002090600402016004828154811061039f5761039f6114b2565b60009182526020909120600490910201806103ba838261157b565b50600182810154908201805460ff909216151560ff1990921691909117905560028083015490820155600391820154910180546001600160a01b0319166001600160a01b039092169190911790558061041281611656565b91505061034b565b50600480548061042c5761042c61166f565b6000828152602081206000199092019160048302019061044c828261116c565b5060018101805460ff191690556000600282015560030180546001600160a01b0319169055905550565b61047e610f14565b6001546040516001600160a01b03848116602483015260448201849052600092169060640160408051601f198184030181529181526020820180516001600160e01b03166340c10f1960e01b179052516104d89190611685565b6000604051808303816000865af19150503d8060008114610515576040519150601f19603f3d011682016040523d82523d6000602084013e61051a565b606091505b505090508061055e5760405162461bcd60e51b815260206004820152601060248201526f1b5a5b9d0818d85b1b0819985a5b195960821b60448201526064016102bb565b505050565b61056b610f14565b600154604051602481018390526000916001600160a01b03169060440160408051601f198184030181529181526020820180516001600160e01b0316630852cd8d60e31b179052516105bd9190611685565b6000604051808303816000865af19150503d80600081146105fa576040519150601f19603f3d011682016040523d82523d6000602084013e6105ff565b606091505b50509050806106435760405162461bcd60e51b815260206004820152601060248201526f189d5c9b8818d85b1b0819985a5b195960821b60448201526064016102bb565b5050565b61064f610f14565b6106598282610dc2565b6106755760405162461bcd60e51b81526004016102bb906116a1565b61067f82826102d0565b156106da5760405162461bcd60e51b815260206004820152602560248201527f546865206173736574207265666572656e636520697320616c7265616479206c6044820152641bd8dad95960da1b60648201526084016102bb565b6001600283836040516106ee929190611466565b908152604051908190036020019020600101805491151560ff199092169190911790555050565b61071d610f14565b838360028686604051610731929190611466565b9081526040519081900360200190209161074c9190836116e3565b508160028585604051610760929190611466565b908152602001604051809103902060020181905550600060028585604051610789929190611466565b908152604051908190036020018120600101805492151560ff199093169290921790915581906002906107bf9087908790611466565b908152602001604051809103902060030160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600160038585604051610808929190611466565b908152604080519182900360209081018320805494151560ff199095169490941790935560a0601f870184900490930282018301905260808101858152600492829190889088908190850183828082843760009201829052509385525050506020808301829052604083018790526001600160a01b03861660609093019290925283546001810185559381522081519192600402019081906108aa908261179d565b50602082015160018201805460ff191691151591909117905560408201516002820155606090910151600390910180546001600160a01b0319166001600160a01b0390921691909117905550505050565b60005b6004548110156102cd5761091181610303565b8061091b81611656565b9150506108fe565b61092b610f14565b6001600160a01b0381166109905760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016102bb565b6102cd816110f6565b6109a1610f14565b6109ab8282610dc2565b6109c75760405162461bcd60e51b81526004016102bb906116a1565b6000600283836040516106ee929190611466565b60006109e78585610dc2565b610a035760405162461bcd60e51b81526004016102bb906116a1565b8260028686604051610a16929190611466565b90815260200160405180910390206002015410158015610a6b5750816001600160a01b031660028686604051610a4d929190611466565b908152604051908190036020019020600301546001600160a01b0316145b95945050505050565b610a7c610f14565b610a868282610dc2565b610aa25760405162461bcd60e51b81526004016102bb906116a1565b610aac82826102d0565b610af85760405162461bcd60e51b815260206004820152601d60248201527f546865206173736574207265666572656e6365206973206c6f636b656400000060448201526064016102bb565b610b2460028383604051610b0d929190611466565b908152602001604051809103902060020154610563565b60028282604051610b36929190611466565b9081526040519081900360200190206000610b51828261116c565b5060018101805460ff19169055600060028201819055600391820180546001600160a01b0319169055604051909190610b8d9085908590611466565b908152604051908190036020019020805491151560ff1990921691909117905560005b60045481101561055e578282604051602001610bcd929190611466565b6040516020818303038152906040528051906020012060048281548110610bf657610bf66114b2565b9060005260206000209060040201600001604051602001610c179190611851565b6040516020818303038152906040528051906020012003610c3b57610c3b81610303565b80610c4581611656565b915050610bb0565b60606000805480602002602001604051908101604052809291908181526020018280548015610ca557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c87575b5050505050905090565b604080516080810182526060808252600060208301819052828401819052908201529051600290610ce39085908590611466565b9081526020016040518091039020604051806080016040529081600082018054610d0c906114c8565b80601f0160208091040260200160405190810160405280929190818152602001828054610d38906114c8565b8015610d855780601f10610d5a57610100808354040283529160200191610d85565b820191906000526020600020905b815481529060010190602001808311610d6857829003601f168201915b5050509183525050600182015460ff1615156020820152600282015460408201526003909101546001600160a01b03166060909101529392505050565b600060038383604051610dd6929190611466565b9081526040519081900360200190205460ff16905092915050565b60606004805480602002602001604051908101604052809291908181526020016000905b82821015610f0b5783829060005260206000209060040201604051806080016040529081600082018054610e48906114c8565b80601f0160208091040260200160405190810160405280929190818152602001828054610e74906114c8565b8015610ec15780601f10610e9657610100808354040283529160200191610ec1565b820191906000526020600020905b815481529060010190602001808311610ea457829003601f168201915b505050918352505060018281015460ff161515602080840191909152600284015460408401526003909301546001600160a01b031660609092019190915291835292019101610e15565b50505050905090565b6000805b600054811015610f7057336001600160a01b031660008281548110610f3f57610f3f6114b2565b6000918252602090912001546001600160a01b031603610f5e57600191505b80610f6881611656565b915050610f18565b506001811515146102cd5760405162461bcd60e51b815260206004820152601f60248201527f4f776e61626c653a2063616c6c6572206973206e6f7420616e206f776e65720060448201526064016102bb565b60005b6000548110156110be57816001600160a01b031660008281548110610fed57610fed6114b2565b6000918252602090912001546001600160a01b0316036110ac57600080546110179060019061148c565b81548110611027576110276114b2565b600091825260208220015481546001600160a01b03909116919083908110611051576110516114b2565b6000918252602082200180546001600160a01b0319166001600160a01b0393909316929092179091558054806110895761108961166f565b600082815260209020810160001990810180546001600160a01b03191690550190555b806110b681611656565b915050610fc6565b506040516001600160a01b038216907f86d076ecf250a6d90a67a7c75317f44709d5001395ecf1df6d9dad5278f1e68190600090a250565b600080546001810182558180527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630180546001600160a01b0319166001600160a01b03841690811790915560405190917fb4cd6c6140e1db788f68710738b06ef4d0df6a884a06a088433a4c2f5e036e7c91a250565b508054611178906114c8565b6000825580601f10611188575050565b601f0160209004906000526020600020908101906102cd91905b808211156111b657600081556001016111a2565b5090565b80356001600160a01b03811681146111d157600080fd5b919050565b6000602082840312156111e857600080fd5b6111f1826111ba565b9392505050565b60008083601f84011261120a57600080fd5b50813567ffffffffffffffff81111561122257600080fd5b60208301915083602082850101111561123a57600080fd5b9250929050565b6000806020838503121561125457600080fd5b823567ffffffffffffffff81111561126b57600080fd5b611277858286016111f8565b90969095509350505050565b60006020828403121561129557600080fd5b5035919050565b600080604083850312156112af57600080fd5b6112b8836111ba565b946020939093013593505050565b600080600080606085870312156112dc57600080fd5b843567ffffffffffffffff8111156112f357600080fd5b6112ff878288016111f8565b90955093505060208501359150611318604086016111ba565b905092959194509250565b6020808252825182820181905260009190848201906040850190845b818110156113645783516001600160a01b03168352928401929184019160010161133f565b50909695505050505050565b60005b8381101561138b578181015183820152602001611373565b50506000910152565b600081516080845280518060808601526113b58160a0870160208501611370565b602084810151151590860152604080850151908601526060938401516001600160a01b0316938501939093525050601f01601f19160160a00190565b6020815260006111f16020830184611394565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561145957603f19888603018452611447858351611394565b9450928501929085019060010161142b565b5092979650505050505050565b8183823760009101908152919050565b634e487b7160e01b600052601160045260246000fd5b818103818111156102fd576102fd611476565b808201808211156102fd576102fd611476565b634e487b7160e01b600052603260045260246000fd5b600181811c908216806114dc57607f821691505b6020821081036114fc57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b601f82111561055e57600081815260208120601f850160051c8101602086101561153f5750805b601f850160051c820191505b8181101561155e5782815560010161154b565b505050505050565b600019600383901b1c191660019190911b1790565b818103611586575050565b61159082546114c8565b67ffffffffffffffff8111156115a8576115a8611502565b6115bc816115b684546114c8565b84611518565b6000601f8211600181146115ea57600083156115d85750848201545b6115e28482611566565b85555061164f565b600085815260209020601f19841690600086815260209020845b838110156116245782860154825560019586019590910190602001611604565b50858310156116425781850154600019600388901b60f8161c191681555b50505060018360011b0184555b5050505050565b60006001820161166857611668611476565b5060010190565b634e487b7160e01b600052603160045260246000fd5b60008251611697818460208701611370565b9190910192915050565b60208082526022908201527f546865206173736574207265666572656e636520646f6573206e6f74206578696040820152611cdd60f21b606082015260800190565b67ffffffffffffffff8311156116fb576116fb611502565b61170f8361170983546114c8565b83611518565b6000601f84116001811461173d576000851561172b5750838201355b6117358682611566565b84555061164f565b600083815260209020601f19861690835b8281101561176e578685013582556020948501946001909201910161174e565b508682101561178b5760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b815167ffffffffffffffff8111156117b7576117b7611502565b6117c5816115b684546114c8565b602080601f8311600181146117f457600084156117e25750858301515b6117ec8582611566565b86555061155e565b600085815260208120601f198616915b8281101561182357888601518255948401946001909101908401611804565b50858210156118415787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600080835461185f816114c8565b60018281168015611877576001811461188c576118bb565b60ff19841687528215158302870194506118bb565b8760005260208060002060005b858110156118b25781548a820152908401908201611899565b50505082870194505b5092969550505050505056fea26469706673582212204e3defbf3329eeb8a9a2352d01161391069b644ab4b4adcc96c509b58240729a64736f6c63430008100033", + "sourceMap": "191:3466:1:-:0;;;598:63;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;348:26:0;2132:10;348:12;:26::i;:::-;633:13:1;:23;;-1:-1:-1;;;;;;633:23:1;-1:-1:-1;;;;;633:23:1;;;;;;;;;;191:3466;;1603:135:0;1670:7;:22;;;;;;;;;;;;;;-1:-1:-1;;;;;;1670:22:0;-1:-1:-1;;;;;1670:22:0;;;;;;;;1707:24;;1670:22;;1707:24;;;1603:135;:::o;14:290:2:-;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;179:16;;-1:-1:-1;;;;;224:31:2;;214:42;;204:70;;270:1;267;260:12;204:70;293:5;14:290;-1:-1:-1;;;14:290:2:o;:::-;191:3466:1;;;;;;", + "deployedSourceMap": "191:3466:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1411:186:0;;;;;;:::i;:::-;;:::i;:::-;;2108:107:1;;;;;;:::i;:::-;;:::i;:::-;;;1317:14:2;;1310:22;1292:41;;1280:2;1265:18;2108:107:1;;;;;;;;3078:261;;;;;;:::i;:::-;;:::i;2342:234::-;;;;;;:::i;:::-;;:::i;2580:200::-;;;;;;:::i;:::-;;:::i;1062:243::-;;;;;;:::i;:::-;;:::i;665:393::-;;;;;;:::i;:::-;;:::i;3524:131::-;;;:::i;1222:183:0:-;;;;;;:::i;:::-;;:::i;1309:172:1:-;;;;;;:::i;:::-;;:::i;2784:259::-;;;;;;:::i;:::-;;:::i;1485:516::-;;;;;;:::i;:::-;;:::i;608:96:0:-;;;:::i;:::-;;;;;;;:::i;2219:119:1:-;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;2005:99::-;;;;;;:::i;:::-;;:::i;3374:110::-;;;:::i;:::-;;;;;;;:::i;1411:186:0:-;500:13;:11;:13::i;:::-;-1:-1:-1;;;;;1493:22:0;::::1;1485:73;;;::::0;-1:-1:-1;;;1485:73:0;;5204:2:2;1485:73:0::1;::::0;::::1;5186:21:2::0;5243:2;5223:18;;;5216:30;5282:34;5262:18;;;5255:62;-1:-1:-1;;;5333:18:2;;;5326:36;5379:19;;1485:73:0::1;;;;;;;;;1568:22;1581:8;1568:12;:22::i;:::-;1411:186:::0;:::o;2108:107:1:-;2172:4;2191:6;2198:2;;2191:10;;;;;;;:::i;:::-;;;;;;;;;;;;;;:19;;;;;;-1:-1:-1;2108:107:1;;;;;:::o;3078:261::-;3149:13;:20;3140:29;;3132:60;;;;-1:-1:-1;;;3132:60:1;;5889:2:2;3132:60:1;;;5871:21:2;5928:2;5908:18;;;5901:30;-1:-1:-1;;;5947:18:2;;;5940:48;6005:18;;3132:60:1;5687:342:2;3132:60:1;3213:6;3199:111;3225:13;:20;:22;;3246:1;;3225:22;:::i;:::-;3221:1;:26;3199:111;;;3283:13;3297:5;:1;3301;3297:5;:::i;:::-;3283:20;;;;;;;;:::i;:::-;;;;;;;;;;;3264:13;3278:1;3264:16;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:39;;:16;:39;:::i;:::-;-1:-1:-1;3264:39:1;;;;;;;;;;;;;;;;-1:-1:-1;;3264:39:1;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;3264:39:1;-1:-1:-1;;;;;3264:39:1;;;;;;;;;3249:3;;;;:::i;:::-;;;;3199:111;;;;3315:13;:19;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;3315:19:1;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;3315:19:1;;;;;-1:-1:-1;;3315:19:1;;;;;;;;;;;;-1:-1:-1;;;;;;3315:19:1;;;;;-1:-1:-1;3078:261:1:o;2342:234::-;500:13:0;:11;:13::i;:::-;2431::1::1;::::0;2457:65:::1;::::0;-1:-1:-1;;;;;9825:32:2;;;2457:65:1::1;::::0;::::1;9807:51:2::0;9874:18;;;9867:34;;;2413:12:1::1;::::0;2431:13:::1;::::0;9780:18:2;;2457:65:1::1;::::0;;-1:-1:-1;;2457:65:1;;::::1;::::0;;;;;;::::1;::::0;::::1;::::0;;-1:-1:-1;;;;;2457:65:1::1;-1:-1:-1::0;;;2457:65:1::1;::::0;;2431:97;::::1;::::0;2457:65;2431:97:::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2412:116;;;2543:7;2535:36;;;::::0;-1:-1:-1;;;2535:36:1;;10406:2:2;2535:36:1::1;::::0;::::1;10388:21:2::0;10445:2;10425:18;;;10418:30;-1:-1:-1;;;10464:18:2;;;10457:46;10520:18;;2535:36:1::1;10204:340:2::0;2535:36:1::1;2406:170;2342:234:::0;;:::o;2580:200::-;500:13:0;:11;:13::i;:::-;2652::1::1;::::0;2678:48:::1;::::0;::::1;::::0;::::1;10695:25:2::0;;;2634:12:1::1;::::0;-1:-1:-1;;;;;2652:13:1::1;::::0;10668:18:2;;2678:48:1::1;::::0;;-1:-1:-1;;2678:48:1;;::::1;::::0;;;;;;::::1;::::0;::::1;::::0;;-1:-1:-1;;;;;2678:48:1::1;-1:-1:-1::0;;;2678:48:1::1;::::0;;2652:80;::::1;::::0;2678:48;2652:80:::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2633:99;;;2747:7;2739:36;;;::::0;-1:-1:-1;;;2739:36:1;;10933:2:2;2739:36:1::1;::::0;::::1;10915:21:2::0;10972:2;10952:18;;;10945:30;-1:-1:-1;;;10991:18:2;;;10984:46;11047:18;;2739:36:1::1;10731:340:2::0;2739:36:1::1;2627:153;2580:200:::0;:::o;1062:243::-;500:13:0;:11;:13::i;:::-;1141::1::1;1151:2;;1141:9;:13::i;:::-;1133:60;;;;-1:-1:-1::0;;;1133:60:1::1;;;;;;;:::i;:::-;1208:17;1222:2;;1208:13;:17::i;:::-;1207:18;1199:68;;;::::0;-1:-1:-1;;;1199:68:1;;11681:2:2;1199:68:1::1;::::0;::::1;11663:21:2::0;11720:2;11700:18;;;11693:30;11759:34;11739:18;;;11732:62;-1:-1:-1;;;11810:18:2;;;11803:35;11855:19;;1199:68:1::1;11479:401:2::0;1199:68:1::1;1296:4;1274:6;1281:2;;1274:10;;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;:19:::1;;:26:::0;;;::::1;;-1:-1:-1::0;;1274:26:1;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;1062:243:1:o;665:393::-;500:13:0;:11;:13::i;:::-;785:2:1::1;;770:6;777:2;;770:10;;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;;:17:::1;::::0;;:10;:17:::1;:::i;:::-;;813:6;793;800:2;;793:10;;;;;;;:::i;:::-;;;;;;;;;;;;;:17;;:26;;;;847:5;825:6;832:2;;825:10;;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;:19:::1;;:27:::0;;;::::1;;-1:-1:-1::0;;825:27:1;;::::1;::::0;;;::::1;::::0;;;881:9;;858:6:::1;::::0;:10:::1;::::0;865:2;;;;858:10:::1;:::i;:::-;;;;;;;;;;;;;:20;;;:32;;;;;-1:-1:-1::0;;;;;858:32:1::1;;;;;-1:-1:-1::0;;;;;858:32:1::1;;;;;;915:4;897:11;909:2;;897:15;;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;;::::1;::::0;;;;;:22;;;::::1;;-1:-1:-1::0;;897:22:1;;::::1;::::0;;;::::1;::::0;;;978:74;::::1;::::0;::::1;::::0;;::::1;::::0;;::::1;::::0;;;;;;::::1;::::0;::::1;::::0;;;959:13:::1;::::0;897:15;;978:74;1000:2;;;;;;978:74;;1000:2;;;;978:74;::::1;;::::0;::::1;::::0;;;-1:-1:-1;978:74:1;;;-1:-1:-1;;;978:74:1::1;::::0;;::::1;::::0;;;;;;;;;-1:-1:-1;;;;;978:74:1;::::1;::::0;;;;;;;;959:94;;978:74;959:94;::::1;::::0;;;;;;;;;;::::1;;;::::0;;;::::1;::::0;;::::1;:::i;:::-;-1:-1:-1::0;959:94:1::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;;-1:-1:-1;;959:94:1::1;::::0;::::1;;::::0;;;::::1;::::0;;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;::::1;::::0;;::::1;::::0;::::1;::::0;;::::1;::::0;;-1:-1:-1;;;;;;959:94:1::1;-1:-1:-1::0;;;;;959:94:1;;::::1;::::0;;;::::1;::::0;;-1:-1:-1;;;;665:393:1:o;3524:131::-;3572:6;3567:84;3588:13;:20;3584:24;;3567:84;;;3623:21;3642:1;3623:18;:21::i;:::-;3610:3;;;;:::i;:::-;;;;3567:84;;1222:183:0;500:13;:11;:13::i;:::-;-1:-1:-1;;;;;1301:22:0;::::1;1293:73;;;::::0;-1:-1:-1;;;1293:73:0;;14655:2:2;1293:73:0::1;::::0;::::1;14637:21:2::0;14694:2;14674:18;;;14667:30;14733:34;14713:18;;;14706:62;-1:-1:-1;;;14784:18:2;;;14777:36;14830:19;;1293:73:0::1;14453:402:2::0;1293:73:0::1;1376:22;1389:8;1376:12;:22::i;1309:172:1:-:0;500:13:0;:11;:13::i;:::-;1390::1::1;1400:2;;1390:9;:13::i;:::-;1382:60;;;;-1:-1:-1::0;;;1382:60:1::1;;;;;;;:::i;:::-;1471:5;1449:6;1456:2;;1449:10;;;;;;;:::i;2784:259::-:0;2885:4;2905:13;2915:2;;2905:9;:13::i;:::-;2897:60;;;;-1:-1:-1;;;2897:60:1;;;;;;;:::i;:::-;2997:6;2976;2983:2;;2976:10;;;;;;;:::i;:::-;;;;;;;;;;;;;:17;;;:27;;2975:63;;;;;3033:4;-1:-1:-1;;;;;3009:28:1;:6;3016:2;;3009:10;;;;;;;:::i;:::-;;;;;;;;;;;;;;:20;;;-1:-1:-1;;;;;3009:20:1;:28;2975:63;2968:70;2784:259;-1:-1:-1;;;;;2784:259:1:o;1485:516::-;500:13:0;:11;:13::i;:::-;1566::1::1;1576:2;;1566:9;:13::i;:::-;1558:60;;;;-1:-1:-1::0;;;1558:60:1::1;;;;;;;:::i;:::-;1632:17;1646:2;;1632:13;:17::i;:::-;1624:59;;;::::0;-1:-1:-1;;;1624:59:1;;15062:2:2;1624:59:1::1;::::0;::::1;15044:21:2::0;15101:2;15081:18;;;15074:30;15140:31;15120:18;;;15113:59;15189:18;;1624:59:1::1;14860:353:2::0;1624:59:1::1;1690:23;1695:6;1702:2;;1695:10;;;;;;;:::i;:::-;;;;;;;;;;;;;:17;;;1690:4;:23::i;:::-;1727:6;1734:2;;1727:10;;;;;;;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;::::1;1720:17;1727:10:::0;;1720:17:::1;:::i;:::-;-1:-1:-1::0;1720:17:1::1;::::0;::::1;::::0;;-1:-1:-1;;1720:17:1::1;::::0;;::::1;;::::0;::::1;::::0;;;::::1;::::0;;::::1;::::0;;-1:-1:-1;;;;;;1720:17:1::1;::::0;;1743:15:::1;::::0;1720:17;;;1743:15:::1;::::0;1755:2;;;;1743:15:::1;:::i;:::-;::::0;;;::::1;::::0;;;;;::::1;::::0;;;:23;;;::::1;;-1:-1:-1::0;;1743:23:1;;::::1;::::0;;;::::1;::::0;;:15:::1;1806:191;1827:13;:20:::0;1823:24;::::1;1806:191;;;1945:2;;1928:20;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1918:31;;;;;;1893:13;1907:1;1893:16;;;;;;;;:::i;:::-;;;;;;;;;;;:19;;1876:37;;;;;;;;:::i;:::-;;;;;;;;;;;;;1866:48;;;;;;:83:::0;1862:129:::1;;1961:21;1980:1;1961:18;:21::i;:::-;1849:3:::0;::::1;::::0;::::1;:::i;:::-;;;;1806:191;;608:96:0::0;655:16;690:7;683:14;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;683:14:0;;;;;;;;;;;;;;;;;;;;;;;608:96;:::o;2219:119:1:-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2323:10:1;;:6;;:10;;2330:2;;;;2323:10;:::i;:::-;;;;;;;;;;;;;2316:17;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;2316:17:1;;;-1:-1:-1;;2316:17:1;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2316:17:1;;;;;;;2219:119;-1:-1:-1;;;2219:119:1:o;2005:99::-;2065:4;2084:11;2096:2;;2084:15;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;2005:99:1;;;;:::o;3374:110::-;3428:23;3466:13;3459:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;3459:20:1;;;-1:-1:-1;;3459:20:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3459:20:1;;;;;;;;;;;;;;;;;;;;;;;;;3374:110;:::o;777:296:0:-;832:9;864:6;859:142;880:7;:14;876:18;;859:142;;;2132:10;-1:-1:-1;;;;;919:26:0;:7;927:1;919:10;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;919:10:0;:26;915:76;;972:4;965:11;;915:76;896:3;;;;:::i;:::-;;;;859:142;;;-1:-1:-1;1026:4:0;1018:12;;;;1010:56;;;;-1:-1:-1;;;1010:56:0;;16267:2:2;1010:56:0;;;16249:21:2;16306:2;16286:18;;;16279:30;16345:33;16325:18;;;16318:61;16396:18;;1010:56:0;16065:355:2;1744:303:0;1813:6;1808:195;1829:7;:14;1825:18;;1808:195;;;1882:5;-1:-1:-1;;;;;1868:19:0;:7;1876:1;1868:10;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;1868:10:0;:19;1864:129;;1920:7;1928:14;;:18;;1945:1;;1928:18;:::i;:::-;1920:27;;;;;;;;:::i;:::-;;;;;;;;;;1907:10;;-1:-1:-1;;;;;1920:27:0;;;;;1915:1;;1907:10;;;;;;:::i;:::-;;;;;;;;;:40;;-1:-1:-1;;;;;;1907:40:0;-1:-1:-1;;;;;1907:40:0;;;;;;;;;;;1965:13;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;1965:13:0;;;;;-1:-1:-1;;;;;;1965:13:0;;;;;;1864:129;1845:3;;;;:::i;:::-;;;;1808:195;;;-1:-1:-1;2017:23:0;;-1:-1:-1;;;;;2017:23:0;;;;;;;;1744:303;:::o;1603:135::-;1670:7;:22;;;;;;;;;;;;;;-1:-1:-1;;;;;;1670:22:0;-1:-1:-1;;;;;1670:22:0;;;;;;;;1707:24;;1670:22;;1707:24;;;1603:135;:::o;-1:-1:-1:-;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:173:2:-;82:20;;-1:-1:-1;;;;;131:31:2;;121:42;;111:70;;177:1;174;167:12;111:70;14:173;;;:::o;192:186::-;251:6;304:2;292:9;283:7;279:23;275:32;272:52;;;320:1;317;310:12;272:52;343:29;362:9;343:29;:::i;:::-;333:39;192:186;-1:-1:-1;;;192:186:2:o;383:348::-;435:8;445:6;499:3;492:4;484:6;480:17;476:27;466:55;;517:1;514;507:12;466:55;-1:-1:-1;540:20:2;;583:18;572:30;;569:50;;;615:1;612;605:12;569:50;652:4;644:6;640:17;628:29;;704:3;697:4;688:6;680;676:19;672:30;669:39;666:59;;;721:1;718;711:12;666:59;383:348;;;;;:::o;736:411::-;807:6;815;868:2;856:9;847:7;843:23;839:32;836:52;;;884:1;881;874:12;836:52;924:9;911:23;957:18;949:6;946:30;943:50;;;989:1;986;979:12;943:50;1028:59;1079:7;1070:6;1059:9;1055:22;1028:59;:::i;:::-;1106:8;;1002:85;;-1:-1:-1;736:411:2;-1:-1:-1;;;;736:411:2:o;1344:180::-;1403:6;1456:2;1444:9;1435:7;1431:23;1427:32;1424:52;;;1472:1;1469;1462:12;1424:52;-1:-1:-1;1495:23:2;;1344:180;-1:-1:-1;1344:180:2:o;1529:254::-;1597:6;1605;1658:2;1646:9;1637:7;1633:23;1629:32;1626:52;;;1674:1;1671;1664:12;1626:52;1697:29;1716:9;1697:29;:::i;:::-;1687:39;1773:2;1758:18;;;;1745:32;;-1:-1:-1;;;1529:254:2:o;1788:553::-;1877:6;1885;1893;1901;1954:2;1942:9;1933:7;1929:23;1925:32;1922:52;;;1970:1;1967;1960:12;1922:52;2010:9;1997:23;2043:18;2035:6;2032:30;2029:50;;;2075:1;2072;2065:12;2029:50;2114:59;2165:7;2156:6;2145:9;2141:22;2114:59;:::i;:::-;2192:8;;-1:-1:-1;2088:85:2;-1:-1:-1;;2274:2:2;2259:18;;2246:32;;-1:-1:-1;2297:38:2;2331:2;2316:18;;2297:38;:::i;:::-;2287:48;;1788:553;;;;;;;:::o;2346:658::-;2517:2;2569:21;;;2639:13;;2542:18;;;2661:22;;;2488:4;;2517:2;2740:15;;;;2714:2;2699:18;;;2488:4;2783:195;2797:6;2794:1;2791:13;2783:195;;;2862:13;;-1:-1:-1;;;;;2858:39:2;2846:52;;2953:15;;;;2918:12;;;;2894:1;2812:9;2783:195;;;-1:-1:-1;2995:3:2;;2346:658;-1:-1:-1;;;;;;2346:658:2:o;3009:250::-;3094:1;3104:113;3118:6;3115:1;3112:13;3104:113;;;3194:11;;;3188:18;3175:11;;;3168:39;3140:2;3133:10;3104:113;;;-1:-1:-1;;3251:1:2;3233:16;;3226:27;3009:250::o;3264:586::-;3321:3;3365:5;3359:12;3392:4;3387:3;3380:17;3426:12;3420:19;3471:6;3464:4;3459:3;3455:14;3448:30;3487:82;3562:6;3556:3;3551;3547:13;3540:4;3526:12;3522:23;3487:82;:::i;:::-;3632:4;3621:16;;;3615:23;3608:31;3601:39;3585:14;;;3578:63;3690:4;3679:16;;;3673:23;3657:14;;;3650:47;3750:4;3739:16;;;3733:23;-1:-1:-1;;;;;3729:49:2;3713:14;;;3706:73;;;;-1:-1:-1;;3833:2:2;3812:15;-1:-1:-1;;3808:29:2;3799:39;3766:3;3795:49;;3264:586::o;3855:277::-;4046:2;4035:9;4028:21;4009:4;4066:60;4122:2;4111:9;4107:18;4099:6;4066:60;:::i;4137:860::-;4341:4;4370:2;4410;4399:9;4395:18;4440:2;4429:9;4422:21;4463:6;4498;4492:13;4529:6;4521;4514:22;4567:2;4556:9;4552:18;4545:25;;4629:2;4619:6;4616:1;4612:14;4601:9;4597:30;4593:39;4579:53;;4667:2;4659:6;4655:15;4688:1;4698:270;4712:6;4709:1;4706:13;4698:270;;;4805:2;4801:7;4789:9;4781:6;4777:22;4773:36;4768:3;4761:49;4833:55;4881:6;4872;4866:13;4833:55;:::i;:::-;4823:65;-1:-1:-1;4946:12:2;;;;4911:15;;;;4734:1;4727:9;4698:270;;;-1:-1:-1;4985:6:2;;4137:860;-1:-1:-1;;;;;;;4137:860:2:o;5409:273::-;5594:6;5586;5581:3;5568:33;5550:3;5620:16;;5645:13;;;5620:16;5409:273;-1:-1:-1;5409:273:2:o;6034:127::-;6095:10;6090:3;6086:20;6083:1;6076:31;6126:4;6123:1;6116:15;6150:4;6147:1;6140:15;6166:128;6233:9;;;6254:11;;;6251:37;;;6268:18;;:::i;6299:125::-;6364:9;;;6385:10;;;6382:36;;;6398:18;;:::i;6429:127::-;6490:10;6485:3;6481:20;6478:1;6471:31;6521:4;6518:1;6511:15;6545:4;6542:1;6535:15;6561:380;6640:1;6636:12;;;;6683;;;6704:61;;6758:4;6750:6;6746:17;6736:27;;6704:61;6811:2;6803:6;6800:14;6780:18;6777:38;6774:161;;6857:10;6852:3;6848:20;6845:1;6838:31;6892:4;6889:1;6882:15;6920:4;6917:1;6910:15;6774:161;;6561:380;;;:::o;6946:127::-;7007:10;7002:3;6998:20;6995:1;6988:31;7038:4;7035:1;7028:15;7062:4;7059:1;7052:15;7204:545;7306:2;7301:3;7298:11;7295:448;;;7342:1;7367:5;7363:2;7356:17;7412:4;7408:2;7398:19;7482:2;7470:10;7466:19;7463:1;7459:27;7453:4;7449:38;7518:4;7506:10;7503:20;7500:47;;;-1:-1:-1;7541:4:2;7500:47;7596:2;7591:3;7587:12;7584:1;7580:20;7574:4;7570:31;7560:41;;7651:82;7669:2;7662:5;7659:13;7651:82;;;7714:17;;;7695:1;7684:13;7651:82;;;7655:3;;;7204:545;;;:::o;7754:166::-;-1:-1:-1;;7882:1:2;7878:11;;;7874:24;7870:29;7860:40;7906:1;7902:11;;;;7857:57;;7754:166::o;7925:1431::-;8040:3;8034:4;8031:13;8028:26;;8047:5;;7925:1431::o;8028:26::-;8077:37;8109:3;8103:10;8077:37;:::i;:::-;8137:18;8129:6;8126:30;8123:56;;;8159:18;;:::i;:::-;8188:97;8278:6;8238:38;8270:4;8264:11;8238:38;:::i;:::-;8232:4;8188:97;:::i;:::-;8311:1;8339:2;8331:6;8328:14;8356:1;8351:748;;;;9143:1;9160:6;9157:89;;;-1:-1:-1;9212:19:2;;;9206:26;9157:89;9272:67;9332:6;9325:5;9272:67;:::i;:::-;9266:4;9259:81;;8321:1029;;8351:748;7151:1;7144:14;;;7188:4;7175:18;;-1:-1:-1;;8387:20:2;;;7151:1;7144:14;;;7188:4;7175:18;;8551:9;8573:251;8587:7;8584:1;8581:14;8573:251;;;8669:21;;;8663:28;8648:44;;8719:1;8792:18;;;;8747:15;;;;8610:4;8603:12;8573:251;;;8577:3;8852:6;8843:7;8840:19;8837:203;;;8913:21;;;8907:28;-1:-1:-1;;8998:1:2;8994:14;;;9010:3;8990:24;8986:37;8982:42;8967:58;8952:74;;8837:203;;;;9086:1;9077:6;9074:1;9070:14;9066:22;9060:4;9053:36;8321:1029;;;;7925:1431;;:::o;9361:135::-;9400:3;9421:17;;;9418:43;;9441:18;;:::i;:::-;-1:-1:-1;9488:1:2;9477:13;;9361:135::o;9501:127::-;9562:10;9557:3;9553:20;9550:1;9543:31;9593:4;9590:1;9583:15;9617:4;9614:1;9607:15;9912:287;10041:3;10079:6;10073:13;10095:66;10154:6;10149:3;10142:4;10134:6;10130:17;10095:66;:::i;:::-;10177:16;;;;;9912:287;-1:-1:-1;;9912:287:2:o;11076:398::-;11278:2;11260:21;;;11317:2;11297:18;;;11290:30;11356:34;11351:2;11336:18;;11329:62;-1:-1:-1;;;11422:2:2;11407:18;;11400:32;11464:3;11449:19;;11076:398::o;11885:1206::-;12009:18;12004:3;12001:27;11998:53;;;12031:18;;:::i;:::-;12060:94;12150:3;12110:38;12142:4;12136:11;12110:38;:::i;:::-;12104:4;12060:94;:::i;:::-;12180:1;12205:2;12200:3;12197:11;12222:1;12217:616;;;;12877:1;12894:3;12891:93;;;-1:-1:-1;12950:19:2;;;12937:33;12891:93;13010:64;13070:3;13063:5;13010:64;:::i;:::-;13004:4;12997:78;;12190:895;;12217:616;7151:1;7144:14;;;7188:4;7175:18;;-1:-1:-1;;12253:17:2;;;12354:9;12376:229;12390:7;12387:1;12384:14;12376:229;;;12479:19;;;12466:33;12451:49;;12586:4;12571:20;;;;12539:1;12527:14;;;;12406:12;12376:229;;;12380:3;12633;12624:7;12621:16;12618:159;;;12757:1;12753:6;12747:3;12741;12738:1;12734:11;12730:21;12726:34;12722:39;12709:9;12704:3;12700:19;12687:33;12683:79;12675:6;12668:95;12618:159;;;12820:1;12814:3;12811:1;12807:11;12803:19;12797:4;12790:33;12190:895;;11885:1206;;;:::o;13096:1352::-;13222:3;13216:10;13249:18;13241:6;13238:30;13235:56;;;13271:18;;:::i;:::-;13300:97;13390:6;13350:38;13382:4;13376:11;13350:38;:::i;13300:97::-;13452:4;;13516:2;13505:14;;13533:1;13528:663;;;;14235:1;14252:6;14249:89;;;-1:-1:-1;14304:19:2;;;14298:26;14249:89;14364:67;14424:6;14417:5;14364:67;:::i;:::-;14358:4;14351:81;;13498:944;;13528:663;7151:1;7144:14;;;7188:4;7175:18;;-1:-1:-1;;13564:20:2;;;13682:236;13696:7;13693:1;13690:14;13682:236;;;13785:19;;;13779:26;13764:42;;13877:27;;;;13845:1;13833:14;;;;13712:19;;13682:236;;;13686:3;13946:6;13937:7;13934:19;13931:201;;;14007:19;;;14001:26;-1:-1:-1;;14090:1:2;14086:14;;;14102:3;14082:24;14078:37;14074:42;14059:58;14044:74;;13931:201;-1:-1:-1;;;;;14178:1:2;14162:14;;;14158:22;14145:36;;-1:-1:-1;13096:1352:2:o;15218:842::-;15346:3;15375:1;15408:6;15402:13;15438:36;15464:9;15438:36;:::i;:::-;15493:1;15510:18;;;15537:133;;;;15684:1;15679:356;;;;15503:532;;15537:133;-1:-1:-1;;15570:24:2;;15558:37;;15643:14;;15636:22;15624:35;;15615:45;;;-1:-1:-1;15537:133:2;;15679:356;15710:6;15707:1;15700:17;15740:4;15785:2;15782:1;15772:16;15810:1;15824:165;15838:6;15835:1;15832:13;15824:165;;;15916:14;;15903:11;;;15896:35;15959:16;;;;15853:10;;15824:165;;;15828:3;;;16018:6;16013:3;16009:16;16002:23;;15503:532;-1:-1:-1;16051:3:2;;15218:842;-1:-1:-1;;;;;;15218:842:2:o", + "sourcePath": "/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol", + "compiler": { + "name": "solc", + "version": "0.8.16+commit.07a7930e" + }, + "ast": { + "absolutePath": "/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol", + "exportedSymbols": { + "AssetReference": [ + 210 + ], + "AssetReferenceContract": [ + 621 + ], + "MyOwnable": [ + 198 + ] + }, + "id": 622, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 200, + "literals": [ + "solidity", + "^", + "0.8", + ".15" + ], + "nodeType": "PragmaDirective", + "src": "37:24:1" + }, + { + "absolutePath": "/home/andre_9a/simple-cactus/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol", + "file": "./MyOwnable.sol", + "id": 201, + "nameLocation": "-1:-1:-1", + "nodeType": "ImportDirective", + "scope": 622, + "sourceUnit": 199, + "src": "63:25:1", + "symbolAliases": [], + "unitAlias": "" + }, + { + "canonicalName": "AssetReference", + "id": 210, + "members": [ + { + "constant": false, + "id": 203, + "mutability": "mutable", + "name": "id", + "nameLocation": "125:2:1", + "nodeType": "VariableDeclaration", + "scope": 210, + "src": "118:9:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + }, + "typeName": { + "id": 202, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "118:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 205, + "mutability": "mutable", + "name": "isLocked", + "nameLocation": "138:8:1", + "nodeType": "VariableDeclaration", + "scope": 210, + "src": "133:13:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 204, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "133:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 207, + "mutability": "mutable", + "name": "amount", + "nameLocation": "157:6:1", + "nodeType": "VariableDeclaration", + "scope": 210, + "src": "152:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 206, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "152:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 209, + "mutability": "mutable", + "name": "recipient", + "nameLocation": "177:9:1", + "nodeType": "VariableDeclaration", + "scope": 210, + "src": "169:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 208, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "169:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "name": "AssetReference", + "nameLocation": "97:14:1", + "nodeType": "StructDefinition", + "scope": 622, + "src": "90:99:1", + "visibility": "public" + }, + { + "abstract": false, + "baseContracts": [ + { + "baseName": { + "id": 211, + "name": "MyOwnable", + "nameLocations": [ + "226:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 198, + "src": "226:9:1" + }, + "id": 212, + "nodeType": "InheritanceSpecifier", + "src": "226:9:1" + } + ], + "canonicalName": "AssetReferenceContract", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 621, + "linearizedBaseContracts": [ + 621, + 198 + ], + "name": "AssetReferenceContract", + "nameLocation": "200:22:1", + "nodeType": "ContractDefinition", + "nodes": [ + { + "constant": false, + "id": 214, + "mutability": "mutable", + "name": "cbdc_contract", + "nameLocation": "248:13:1", + "nodeType": "VariableDeclaration", + "scope": 621, + "src": "240:21:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 213, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "240:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 219, + "mutability": "mutable", + "name": "assets", + "nameLocation": "300:6:1", + "nodeType": "VariableDeclaration", + "scope": 621, + "src": "265:41:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string => struct AssetReference)" + }, + "typeName": { + "id": 218, + "keyType": { + "id": 215, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "274:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "nodeType": "Mapping", + "src": "265:34:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string => struct AssetReference)" + }, + "valueType": { + "id": 217, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 216, + "name": "AssetReference", + "nameLocations": [ + "284:14:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 210, + "src": "284:14:1" + }, + "referencedDeclaration": 210, + "src": "284:14:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage_ptr", + "typeString": "struct AssetReference" + } + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 223, + "mutability": "mutable", + "name": "assetExists", + "nameLocation": "335:11:1", + "nodeType": "VariableDeclaration", + "scope": 621, + "src": "310:36:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_bool_$", + "typeString": "mapping(string => bool)" + }, + "typeName": { + "id": 222, + "keyType": { + "id": 220, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "319:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "nodeType": "Mapping", + "src": "310:24:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_bool_$", + "typeString": "mapping(string => bool)" + }, + "valueType": { + "id": 221, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "329:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 227, + "mutability": "mutable", + "name": "assetRefsList", + "nameLocation": "580:13:1", + "nodeType": "VariableDeclaration", + "scope": 621, + "src": "563:30:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference[]" + }, + "typeName": { + "baseType": { + "id": 225, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 224, + "name": "AssetReference", + "nameLocations": [ + "563:14:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 210, + "src": "563:14:1" + }, + "referencedDeclaration": 210, + "src": "563:14:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage_ptr", + "typeString": "struct AssetReference" + } + }, + "id": 226, + "nodeType": "ArrayTypeName", + "src": "563:16:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr", + "typeString": "struct AssetReference[]" + } + }, + "visibility": "internal" + }, + { + "body": { + "id": 236, + "nodeType": "Block", + "src": "627:34:1", + "statements": [ + { + "expression": { + "id": 234, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 232, + "name": "cbdc_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 214, + "src": "633:13:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 233, + "name": "account", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 229, + "src": "649:7:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "633:23:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 235, + "nodeType": "ExpressionStatement", + "src": "633:23:1" + } + ] + }, + "id": 237, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 230, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 229, + "mutability": "mutable", + "name": "account", + "nameLocation": "618:7:1", + "nodeType": "VariableDeclaration", + "scope": 237, + "src": "610:15:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 228, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "610:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "609:17:1" + }, + "returnParameters": { + "id": 231, + "nodeType": "ParameterList", + "parameters": [], + "src": "627:0:1" + }, + "scope": 621, + "src": "598:63:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 293, + "nodeType": "Block", + "src": "764:294:1", + "statements": [ + { + "expression": { + "id": 253, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 248, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "770:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 250, + "indexExpression": { + "id": 249, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "777:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "770:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 251, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "781:2:1", + "memberName": "id", + "nodeType": "MemberAccess", + "referencedDeclaration": 203, + "src": "770:13:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 252, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "785:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "src": "770:17:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + }, + "id": 254, + "nodeType": "ExpressionStatement", + "src": "770:17:1" + }, + { + "expression": { + "id": 260, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 255, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "793:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 257, + "indexExpression": { + "id": 256, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "800:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "793:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 258, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "804:6:1", + "memberName": "amount", + "nodeType": "MemberAccess", + "referencedDeclaration": 207, + "src": "793:17:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 259, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 241, + "src": "813:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "793:26:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 261, + "nodeType": "ExpressionStatement", + "src": "793:26:1" + }, + { + "expression": { + "id": 267, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 262, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "825:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 264, + "indexExpression": { + "id": 263, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "832:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "825:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 265, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "836:8:1", + "memberName": "isLocked", + "nodeType": "MemberAccess", + "referencedDeclaration": 205, + "src": "825:19:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "hexValue": "66616c7365", + "id": 266, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "847:5:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "false" + }, + "src": "825:27:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 268, + "nodeType": "ExpressionStatement", + "src": "825:27:1" + }, + { + "expression": { + "id": 274, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 269, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "858:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 271, + "indexExpression": { + "id": 270, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "865:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "858:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 272, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "869:9:1", + "memberName": "recipient", + "nodeType": "MemberAccess", + "referencedDeclaration": 209, + "src": "858:20:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 273, + "name": "recipient", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 243, + "src": "881:9:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "858:32:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 275, + "nodeType": "ExpressionStatement", + "src": "858:32:1" + }, + { + "expression": { + "id": 280, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "baseExpression": { + "id": 276, + "name": "assetExists", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 223, + "src": "897:11:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_bool_$", + "typeString": "mapping(string memory => bool)" + } + }, + "id": 278, + "indexExpression": { + "id": 277, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "909:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "897:15:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "hexValue": "74727565", + "id": 279, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "915:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "true" + }, + "src": "897:22:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 281, + "nodeType": "ExpressionStatement", + "src": "897:22:1" + }, + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 286, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 239, + "src": "1000:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + { + "hexValue": "66616c7365", + "id": 287, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1010:5:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "false" + }, + { + "id": 288, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 241, + "src": "1023:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "id": 289, + "name": "recipient", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 243, + "src": "1037:9:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + }, + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "id": 285, + "name": "AssetReference", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 210, + "src": "978:14:1", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_struct$_AssetReference_$210_storage_ptr_$", + "typeString": "type(struct AssetReference storage pointer)" + } + }, + "id": 290, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "structConstructorCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "978:74:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_memory_ptr", + "typeString": "struct AssetReference memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_struct$_AssetReference_$210_memory_ptr", + "typeString": "struct AssetReference memory" + } + ], + "expression": { + "id": 282, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "959:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 284, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "973:4:1", + "memberName": "push", + "nodeType": "MemberAccess", + "src": "959:18:1", + "typeDescriptions": { + "typeIdentifier": "t_function_arraypush_nonpayable$_t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr_$_t_struct$_AssetReference_$210_storage_$returns$__$bound_to$_t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr_$", + "typeString": "function (struct AssetReference storage ref[] storage pointer,struct AssetReference storage ref)" + } + }, + "id": 291, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "959:94:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 292, + "nodeType": "ExpressionStatement", + "src": "959:94:1" + } + ] + }, + "functionSelector": "663bcc92", + "id": 294, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 246, + "kind": "modifierInvocation", + "modifierName": { + "id": 245, + "name": "onlyOwner", + "nameLocations": [ + "754:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "754:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "754:9:1" + } + ], + "name": "createAssetReference", + "nameLocation": "674:20:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 244, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 239, + "mutability": "mutable", + "name": "id", + "nameLocation": "711:2:1", + "nodeType": "VariableDeclaration", + "scope": 294, + "src": "695:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 238, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "695:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 241, + "mutability": "mutable", + "name": "amount", + "nameLocation": "720:6:1", + "nodeType": "VariableDeclaration", + "scope": 294, + "src": "715:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 240, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "715:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 243, + "mutability": "mutable", + "name": "recipient", + "nameLocation": "736:9:1", + "nodeType": "VariableDeclaration", + "scope": 294, + "src": "728:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 242, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "728:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "694:52:1" + }, + "returnParameters": { + "id": 247, + "nodeType": "ParameterList", + "parameters": [], + "src": "764:0:1" + }, + "scope": 621, + "src": "665:393:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 323, + "nodeType": "Block", + "src": "1127:178:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 303, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 296, + "src": "1151:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 302, + "name": "isPresent", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 433, + "src": "1141:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 304, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1141:13:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e636520646f6573206e6f74206578697374", + "id": 305, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1156:36:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + }, + "value": "The asset reference does not exist" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + } + ], + "id": 301, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1133:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 306, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1133:60:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 307, + "nodeType": "ExpressionStatement", + "src": "1133:60:1" + }, + { + "expression": { + "arguments": [ + { + "id": 312, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "!", + "prefix": true, + "src": "1207:18:1", + "subExpression": { + "arguments": [ + { + "id": 310, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 296, + "src": "1222:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 309, + "name": "isAssetLocked", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 446, + "src": "1208:13:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 311, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1208:17:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e636520697320616c7265616479206c6f636b6564", + "id": 313, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1227:39:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_64521928c09bf02b6b5471b284b9f6dfbc7431afff7624ad621932a3f25b489f", + "typeString": "literal_string \"The asset reference is already locked\"" + }, + "value": "The asset reference is already locked" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_64521928c09bf02b6b5471b284b9f6dfbc7431afff7624ad621932a3f25b489f", + "typeString": "literal_string \"The asset reference is already locked\"" + } + ], + "id": 308, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1199:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 314, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1199:68:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 315, + "nodeType": "ExpressionStatement", + "src": "1199:68:1" + }, + { + "expression": { + "id": 321, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 316, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "1274:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 318, + "indexExpression": { + "id": 317, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 296, + "src": "1281:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1274:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 319, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "1285:8:1", + "memberName": "isLocked", + "nodeType": "MemberAccess", + "referencedDeclaration": 205, + "src": "1274:19:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "hexValue": "74727565", + "id": 320, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1296:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "true" + }, + "src": "1274:26:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 322, + "nodeType": "ExpressionStatement", + "src": "1274:26:1" + } + ] + }, + "functionSelector": "63773a6c", + "id": 324, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 299, + "kind": "modifierInvocation", + "modifierName": { + "id": 298, + "name": "onlyOwner", + "nameLocations": [ + "1117:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "1117:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "1117:9:1" + } + ], + "name": "lockAssetReference", + "nameLocation": "1071:18:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 297, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 296, + "mutability": "mutable", + "name": "id", + "nameLocation": "1106:2:1", + "nodeType": "VariableDeclaration", + "scope": 324, + "src": "1090:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 295, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "1090:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "1089:20:1" + }, + "returnParameters": { + "id": 300, + "nodeType": "ParameterList", + "parameters": [], + "src": "1127:0:1" + }, + "scope": 621, + "src": "1062:243:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 345, + "nodeType": "Block", + "src": "1376:105:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 333, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 326, + "src": "1400:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 332, + "name": "isPresent", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 433, + "src": "1390:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 334, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1390:13:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e636520646f6573206e6f74206578697374", + "id": 335, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1405:36:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + }, + "value": "The asset reference does not exist" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + } + ], + "id": 331, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1382:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 336, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1382:60:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 337, + "nodeType": "ExpressionStatement", + "src": "1382:60:1" + }, + { + "expression": { + "id": 343, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "expression": { + "baseExpression": { + "id": 338, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "1449:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 340, + "indexExpression": { + "id": 339, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 326, + "src": "1456:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1449:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 341, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "memberLocation": "1460:8:1", + "memberName": "isLocked", + "nodeType": "MemberAccess", + "referencedDeclaration": 205, + "src": "1449:19:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "hexValue": "66616c7365", + "id": 342, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1471:5:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "false" + }, + "src": "1449:27:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 344, + "nodeType": "ExpressionStatement", + "src": "1449:27:1" + } + ] + }, + "functionSelector": "8ef45293", + "id": 346, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 329, + "kind": "modifierInvocation", + "modifierName": { + "id": 328, + "name": "onlyOwner", + "nameLocations": [ + "1366:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "1366:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "1366:9:1" + } + ], + "name": "unLockAssetReference", + "nameLocation": "1318:20:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 327, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 326, + "mutability": "mutable", + "name": "id", + "nameLocation": "1355:2:1", + "nodeType": "VariableDeclaration", + "scope": 346, + "src": "1339:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 325, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "1339:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "1338:20:1" + }, + "returnParameters": { + "id": 330, + "nodeType": "ParameterList", + "parameters": [], + "src": "1376:0:1" + }, + "scope": 621, + "src": "1309:172:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 420, + "nodeType": "Block", + "src": "1552:449:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 355, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1576:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 354, + "name": "isPresent", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 433, + "src": "1566:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 356, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1566:13:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e636520646f6573206e6f74206578697374", + "id": 357, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1581:36:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + }, + "value": "The asset reference does not exist" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + } + ], + "id": 353, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1558:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 358, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1558:60:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 359, + "nodeType": "ExpressionStatement", + "src": "1558:60:1" + }, + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 362, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1646:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 361, + "name": "isAssetLocked", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 446, + "src": "1632:13:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 363, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1632:17:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e6365206973206c6f636b6564", + "id": 364, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1651:31:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_0336abc357798e0ba342f01c1301e0e34f6dedce2787c507141be0c60e4153c9", + "typeString": "literal_string \"The asset reference is locked\"" + }, + "value": "The asset reference is locked" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_0336abc357798e0ba342f01c1301e0e34f6dedce2787c507141be0c60e4153c9", + "typeString": "literal_string \"The asset reference is locked\"" + } + ], + "id": 360, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1624:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 365, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1624:59:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 366, + "nodeType": "ExpressionStatement", + "src": "1624:59:1" + }, + { + "expression": { + "arguments": [ + { + "expression": { + "baseExpression": { + "id": 368, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "1695:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 370, + "indexExpression": { + "id": 369, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1702:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1695:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 371, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1706:6:1", + "memberName": "amount", + "nodeType": "MemberAccess", + "referencedDeclaration": 207, + "src": "1695:17:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 367, + "name": "burn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 510, + "src": "1690:4:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$returns$__$", + "typeString": "function (uint256)" + } + }, + "id": 372, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1690:23:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 373, + "nodeType": "ExpressionStatement", + "src": "1690:23:1" + }, + { + "expression": { + "id": 377, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "delete", + "prefix": true, + "src": "1720:17:1", + "subExpression": { + "baseExpression": { + "id": 374, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "1727:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 376, + "indexExpression": { + "id": 375, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1734:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "1727:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 378, + "nodeType": "ExpressionStatement", + "src": "1720:17:1" + }, + { + "expression": { + "id": 383, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "baseExpression": { + "id": 379, + "name": "assetExists", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 223, + "src": "1743:11:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_bool_$", + "typeString": "mapping(string memory => bool)" + } + }, + "id": 381, + "indexExpression": { + "id": 380, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1755:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "1743:15:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "hexValue": "66616c7365", + "id": 382, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1761:5:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "false" + }, + "src": "1743:23:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 384, + "nodeType": "ExpressionStatement", + "src": "1743:23:1" + }, + { + "body": { + "id": 418, + "nodeType": "Block", + "src": "1854:143:1", + "statements": [ + { + "condition": { + "commonType": { + "typeIdentifier": "t_bytes32", + "typeString": "bytes32" + }, + "id": 411, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "arguments": [ + { + "arguments": [ + { + "expression": { + "baseExpression": { + "id": 399, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "1893:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 401, + "indexExpression": { + "id": 400, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 386, + "src": "1907:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1893:16:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 402, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1910:2:1", + "memberName": "id", + "nodeType": "MemberAccess", + "referencedDeclaration": 203, + "src": "1893:19:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_storage", + "typeString": "string storage ref" + } + ], + "expression": { + "id": 397, + "name": "abi", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967295, + "src": "1876:3:1", + "typeDescriptions": { + "typeIdentifier": "t_magic_abi", + "typeString": "abi" + } + }, + "id": 398, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "1880:12:1", + "memberName": "encodePacked", + "nodeType": "MemberAccess", + "src": "1876:16:1", + "typeDescriptions": { + "typeIdentifier": "t_function_abiencodepacked_pure$__$returns$_t_bytes_memory_ptr_$", + "typeString": "function () pure returns (bytes memory)" + } + }, + "id": 403, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1876:37:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + ], + "id": 396, + "name": "keccak256", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967288, + "src": "1866:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$", + "typeString": "function (bytes memory) pure returns (bytes32)" + } + }, + "id": 404, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1866:48:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes32", + "typeString": "bytes32" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "arguments": [ + { + "arguments": [ + { + "id": 408, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 348, + "src": "1945:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "expression": { + "id": 406, + "name": "abi", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967295, + "src": "1928:3:1", + "typeDescriptions": { + "typeIdentifier": "t_magic_abi", + "typeString": "abi" + } + }, + "id": 407, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "1932:12:1", + "memberName": "encodePacked", + "nodeType": "MemberAccess", + "src": "1928:16:1", + "typeDescriptions": { + "typeIdentifier": "t_function_abiencodepacked_pure$__$returns$_t_bytes_memory_ptr_$", + "typeString": "function () pure returns (bytes memory)" + } + }, + "id": 409, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1928:20:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + ], + "id": 405, + "name": "keccak256", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967288, + "src": "1918:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_keccak256_pure$_t_bytes_memory_ptr_$returns$_t_bytes32_$", + "typeString": "function (bytes memory) pure returns (bytes32)" + } + }, + "id": 410, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1918:31:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes32", + "typeString": "bytes32" + } + }, + "src": "1866:83:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 417, + "nodeType": "IfStatement", + "src": "1862:129:1", + "trueBody": { + "id": 416, + "nodeType": "Block", + "src": "1951:40:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 413, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 386, + "src": "1980:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 412, + "name": "removeItemFromList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 589, + "src": "1961:18:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$returns$__$", + "typeString": "function (uint256)" + } + }, + "id": 414, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1961:21:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 415, + "nodeType": "ExpressionStatement", + "src": "1961:21:1" + } + ] + } + } + ] + }, + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 392, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 389, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 386, + "src": "1823:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "<", + "rightExpression": { + "expression": { + "id": 390, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "1827:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 391, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1841:6:1", + "memberName": "length", + "nodeType": "MemberAccess", + "src": "1827:20:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "1823:24:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 419, + "initializationExpression": { + "assignments": [ + 386 + ], + "declarations": [ + { + "constant": false, + "id": 386, + "mutability": "mutable", + "name": "i", + "nameLocation": "1816:1:1", + "nodeType": "VariableDeclaration", + "scope": 419, + "src": "1811:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 385, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1811:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 388, + "initialValue": { + "hexValue": "30", + "id": 387, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1820:1:1", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "nodeType": "VariableDeclarationStatement", + "src": "1811:10:1" + }, + "loopExpression": { + "expression": { + "id": 394, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "1849:3:1", + "subExpression": { + "id": 393, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 386, + "src": "1849:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 395, + "nodeType": "ExpressionStatement", + "src": "1849:3:1" + }, + "nodeType": "ForStatement", + "src": "1806:191:1" + } + ] + }, + "functionSelector": "ade3222b", + "id": 421, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 351, + "kind": "modifierInvocation", + "modifierName": { + "id": 350, + "name": "onlyOwner", + "nameLocations": [ + "1542:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "1542:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "1542:9:1" + } + ], + "name": "deleteAssetReference", + "nameLocation": "1494:20:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 349, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 348, + "mutability": "mutable", + "name": "id", + "nameLocation": "1531:2:1", + "nodeType": "VariableDeclaration", + "scope": 421, + "src": "1515:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 347, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "1515:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "1514:20:1" + }, + "returnParameters": { + "id": 352, + "nodeType": "ParameterList", + "parameters": [], + "src": "1552:0:1" + }, + "scope": 621, + "src": "1485:516:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 432, + "nodeType": "Block", + "src": "2071:33:1", + "statements": [ + { + "expression": { + "baseExpression": { + "id": 428, + "name": "assetExists", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 223, + "src": "2084:11:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_bool_$", + "typeString": "mapping(string memory => bool)" + } + }, + "id": 430, + "indexExpression": { + "id": 429, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 423, + "src": "2096:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2084:15:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "functionReturnParameters": 427, + "id": 431, + "nodeType": "Return", + "src": "2077:22:1" + } + ] + }, + "functionSelector": "bc548275", + "id": 433, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "isPresent", + "nameLocation": "2014:9:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 424, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 423, + "mutability": "mutable", + "name": "id", + "nameLocation": "2040:2:1", + "nodeType": "VariableDeclaration", + "scope": 433, + "src": "2024:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 422, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "2024:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "2023:20:1" + }, + "returnParameters": { + "id": 427, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 426, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 433, + "src": "2065:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 425, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "2065:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + } + ], + "src": "2064:6:1" + }, + "scope": 621, + "src": "2005:99:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 445, + "nodeType": "Block", + "src": "2178:37:1", + "statements": [ + { + "expression": { + "expression": { + "baseExpression": { + "id": 440, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "2191:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 442, + "indexExpression": { + "id": 441, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 435, + "src": "2198:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2191:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 443, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "memberLocation": "2202:8:1", + "memberName": "isLocked", + "nodeType": "MemberAccess", + "referencedDeclaration": 205, + "src": "2191:19:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "functionReturnParameters": 439, + "id": 444, + "nodeType": "Return", + "src": "2184:26:1" + } + ] + }, + "functionSelector": "1ae4eb68", + "id": 446, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "isAssetLocked", + "nameLocation": "2117:13:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 436, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 435, + "mutability": "mutable", + "name": "id", + "nameLocation": "2147:2:1", + "nodeType": "VariableDeclaration", + "scope": 446, + "src": "2131:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 434, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "2131:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "2130:20:1" + }, + "returnParameters": { + "id": 439, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 438, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 446, + "src": "2172:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 437, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "2172:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + } + ], + "src": "2171:6:1" + }, + "scope": 621, + "src": "2108:107:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 458, + "nodeType": "Block", + "src": "2310:28:1", + "statements": [ + { + "expression": { + "baseExpression": { + "id": 454, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "2323:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 456, + "indexExpression": { + "id": 455, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 448, + "src": "2330:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2323:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "functionReturnParameters": 453, + "id": 457, + "nodeType": "Return", + "src": "2316:17:1" + } + ] + }, + "functionSelector": "b1fa2580", + "id": 459, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "getAssetReference", + "nameLocation": "2228:17:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 449, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 448, + "mutability": "mutable", + "name": "id", + "nameLocation": "2262:2:1", + "nodeType": "VariableDeclaration", + "scope": 459, + "src": "2246:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 447, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "2246:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "2245:20:1" + }, + "returnParameters": { + "id": 453, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 452, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 459, + "src": "2287:21:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_memory_ptr", + "typeString": "struct AssetReference" + }, + "typeName": { + "id": 451, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 450, + "name": "AssetReference", + "nameLocations": [ + "2287:14:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 210, + "src": "2287:14:1" + }, + "referencedDeclaration": 210, + "src": "2287:14:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage_ptr", + "typeString": "struct AssetReference" + } + }, + "visibility": "internal" + } + ], + "src": "2286:23:1" + }, + "scope": 621, + "src": "2219:119:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 485, + "nodeType": "Block", + "src": "2406:170:1", + "statements": [ + { + "assignments": [ + 469, + null + ], + "declarations": [ + { + "constant": false, + "id": 469, + "mutability": "mutable", + "name": "success", + "nameLocation": "2418:7:1", + "nodeType": "VariableDeclaration", + "scope": 485, + "src": "2413:12:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 468, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "2413:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + }, + null + ], + "id": 479, + "initialValue": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "6d696e7428616464726573732c75696e7432353629", + "id": 474, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "2481:23:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_40c10f19c047ae7dfa66d6312b683d2ea3dfbcb4159e96b967c5f4b0a86f2842", + "typeString": "literal_string \"mint(address,uint256)\"" + }, + "value": "mint(address,uint256)" + }, + { + "id": 475, + "name": "account", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 461, + "src": "2506:7:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 476, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 463, + "src": "2515:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_40c10f19c047ae7dfa66d6312b683d2ea3dfbcb4159e96b967c5f4b0a86f2842", + "typeString": "literal_string \"mint(address,uint256)\"" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 472, + "name": "abi", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967295, + "src": "2457:3:1", + "typeDescriptions": { + "typeIdentifier": "t_magic_abi", + "typeString": "abi" + } + }, + "id": 473, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "2461:19:1", + "memberName": "encodeWithSignature", + "nodeType": "MemberAccess", + "src": "2457:23:1", + "typeDescriptions": { + "typeIdentifier": "t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$", + "typeString": "function (string memory) pure returns (bytes memory)" + } + }, + "id": 477, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2457:65:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + ], + "expression": { + "id": 470, + "name": "cbdc_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 214, + "src": "2431:13:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 471, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "2445:4:1", + "memberName": "call", + "nodeType": "MemberAccess", + "src": "2431:18:1", + "typeDescriptions": { + "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "function (bytes memory) payable returns (bool,bytes memory)" + } + }, + "id": 478, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2431:97:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "tuple(bool,bytes memory)" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "2412:116:1" + }, + { + "expression": { + "arguments": [ + { + "id": 481, + "name": "success", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 469, + "src": "2543:7:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "6d696e742063616c6c206661696c6564", + "id": 482, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "2552:18:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_bf9c519459adea86d8a83bfe255ca2736ba7c8625940375bb2fc6e318ee9051b", + "typeString": "literal_string \"mint call failed\"" + }, + "value": "mint call failed" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_bf9c519459adea86d8a83bfe255ca2736ba7c8625940375bb2fc6e318ee9051b", + "typeString": "literal_string \"mint call failed\"" + } + ], + "id": 480, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "2535:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 483, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2535:36:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 484, + "nodeType": "ExpressionStatement", + "src": "2535:36:1" + } + ] + }, + "functionSelector": "40c10f19", + "id": 486, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 466, + "kind": "modifierInvocation", + "modifierName": { + "id": 465, + "name": "onlyOwner", + "nameLocations": [ + "2396:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "2396:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "2396:9:1" + } + ], + "name": "mint", + "nameLocation": "2351:4:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 464, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 461, + "mutability": "mutable", + "name": "account", + "nameLocation": "2364:7:1", + "nodeType": "VariableDeclaration", + "scope": 486, + "src": "2356:15:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 460, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2356:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 463, + "mutability": "mutable", + "name": "amount", + "nameLocation": "2381:6:1", + "nodeType": "VariableDeclaration", + "scope": 486, + "src": "2373:14:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 462, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2373:7:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "2355:33:1" + }, + "returnParameters": { + "id": 467, + "nodeType": "ParameterList", + "parameters": [], + "src": "2406:0:1" + }, + "scope": 621, + "src": "2342:234:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 509, + "nodeType": "Block", + "src": "2627:153:1", + "statements": [ + { + "assignments": [ + 494, + null + ], + "declarations": [ + { + "constant": false, + "id": 494, + "mutability": "mutable", + "name": "success", + "nameLocation": "2639:7:1", + "nodeType": "VariableDeclaration", + "scope": 509, + "src": "2634:12:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 493, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "2634:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + }, + null + ], + "id": 503, + "initialValue": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "6275726e2875696e7432353629", + "id": 499, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "2702:15:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_42966c689b5afe9b9b3f8a7103b2a19980d59629bfd6a20a60972312ed41d836", + "typeString": "literal_string \"burn(uint256)\"" + }, + "value": "burn(uint256)" + }, + { + "id": 500, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 488, + "src": "2719:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_42966c689b5afe9b9b3f8a7103b2a19980d59629bfd6a20a60972312ed41d836", + "typeString": "literal_string \"burn(uint256)\"" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "expression": { + "id": 497, + "name": "abi", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967295, + "src": "2678:3:1", + "typeDescriptions": { + "typeIdentifier": "t_magic_abi", + "typeString": "abi" + } + }, + "id": 498, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "2682:19:1", + "memberName": "encodeWithSignature", + "nodeType": "MemberAccess", + "src": "2678:23:1", + "typeDescriptions": { + "typeIdentifier": "t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$", + "typeString": "function (string memory) pure returns (bytes memory)" + } + }, + "id": 501, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2678:48:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + ], + "expression": { + "id": 495, + "name": "cbdc_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 214, + "src": "2652:13:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 496, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "2666:4:1", + "memberName": "call", + "nodeType": "MemberAccess", + "src": "2652:18:1", + "typeDescriptions": { + "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "function (bytes memory) payable returns (bool,bytes memory)" + } + }, + "id": 502, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2652:80:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "tuple(bool,bytes memory)" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "2633:99:1" + }, + { + "expression": { + "arguments": [ + { + "id": 505, + "name": "success", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 494, + "src": "2747:7:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "6275726e2063616c6c206661696c6564", + "id": 506, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "2756:18:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_bd2737dababb3f3aa60c0b3e63ed3f2a6dbe45608e370c8c6df354f1d0f7216c", + "typeString": "literal_string \"burn call failed\"" + }, + "value": "burn call failed" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_bd2737dababb3f3aa60c0b3e63ed3f2a6dbe45608e370c8c6df354f1d0f7216c", + "typeString": "literal_string \"burn call failed\"" + } + ], + "id": 504, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "2739:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 507, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2739:36:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 508, + "nodeType": "ExpressionStatement", + "src": "2739:36:1" + } + ] + }, + "functionSelector": "42966c68", + "id": 510, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 491, + "kind": "modifierInvocation", + "modifierName": { + "id": 490, + "name": "onlyOwner", + "nameLocations": [ + "2617:9:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 30, + "src": "2617:9:1" + }, + "nodeType": "ModifierInvocation", + "src": "2617:9:1" + } + ], + "name": "burn", + "nameLocation": "2589:4:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 489, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 488, + "mutability": "mutable", + "name": "amount", + "nameLocation": "2602:6:1", + "nodeType": "VariableDeclaration", + "scope": 510, + "src": "2594:14:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 487, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2594:7:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "2593:16:1" + }, + "returnParameters": { + "id": 492, + "nodeType": "ParameterList", + "parameters": [], + "src": "2627:0:1" + }, + "scope": 621, + "src": "2580:200:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 544, + "nodeType": "Block", + "src": "2891:152:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "arguments": [ + { + "id": 523, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 512, + "src": "2915:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + ], + "id": 522, + "name": "isPresent", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 433, + "src": "2905:9:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_string_calldata_ptr_$returns$_t_bool_$", + "typeString": "function (string calldata) view returns (bool)" + } + }, + "id": 524, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2905:13:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "546865206173736574207265666572656e636520646f6573206e6f74206578697374", + "id": 525, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "2920:36:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + }, + "value": "The asset reference does not exist" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_2bdeab7725adc4624b2ab32753504234c5d4b44110e9199db6e764e1c5f2e4ec", + "typeString": "literal_string \"The asset reference does not exist\"" + } + ], + "id": 521, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "2897:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 526, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "2897:60:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 527, + "nodeType": "ExpressionStatement", + "src": "2897:60:1" + }, + { + "expression": { + "commonType": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "id": 542, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "components": [ + { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 533, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "expression": { + "baseExpression": { + "id": 528, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "2976:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 530, + "indexExpression": { + "id": 529, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 512, + "src": "2983:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "2976:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 531, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "memberLocation": "2987:6:1", + "memberName": "amount", + "nodeType": "MemberAccess", + "referencedDeclaration": 207, + "src": "2976:17:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": ">=", + "rightExpression": { + "id": 532, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 514, + "src": "2997:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "2976:27:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + ], + "id": 534, + "isConstant": false, + "isInlineArray": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "TupleExpression", + "src": "2975:29:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "nodeType": "BinaryOperation", + "operator": "&&", + "rightExpression": { + "components": [ + { + "commonType": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "id": 540, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "expression": { + "baseExpression": { + "id": 535, + "name": "assets", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 219, + "src": "3009:6:1", + "typeDescriptions": { + "typeIdentifier": "t_mapping$_t_string_memory_ptr_$_t_struct$_AssetReference_$210_storage_$", + "typeString": "mapping(string memory => struct AssetReference storage ref)" + } + }, + "id": 537, + "indexExpression": { + "id": 536, + "name": "id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 512, + "src": "3016:2:1", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "3009:10:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 538, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "memberLocation": "3020:9:1", + "memberName": "recipient", + "nodeType": "MemberAccess", + "referencedDeclaration": 209, + "src": "3009:20:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "BinaryOperation", + "operator": "==", + "rightExpression": { + "id": 539, + "name": "user", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 516, + "src": "3033:4:1", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "3009:28:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + } + ], + "id": 541, + "isConstant": false, + "isInlineArray": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "TupleExpression", + "src": "3008:30:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "src": "2975:63:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "functionReturnParameters": 520, + "id": 543, + "nodeType": "Return", + "src": "2968:70:1" + } + ] + }, + "functionSelector": "9ca27299", + "id": 545, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "checkValidBridgeBack", + "nameLocation": "2793:20:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 517, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 512, + "mutability": "mutable", + "name": "id", + "nameLocation": "2830:2:1", + "nodeType": "VariableDeclaration", + "scope": 545, + "src": "2814:18:1", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 511, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "2814:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 514, + "mutability": "mutable", + "name": "amount", + "nameLocation": "2842:6:1", + "nodeType": "VariableDeclaration", + "scope": 545, + "src": "2834:14:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 513, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "2834:7:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 516, + "mutability": "mutable", + "name": "user", + "nameLocation": "2858:4:1", + "nodeType": "VariableDeclaration", + "scope": 545, + "src": "2850:12:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 515, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "2850:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "2813:50:1" + }, + "returnParameters": { + "id": 520, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 519, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 545, + "src": "2885:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 518, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "2885:4:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + } + ], + "src": "2884:6:1" + }, + "scope": 621, + "src": "2784:259:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 588, + "nodeType": "Block", + "src": "3126:213:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 554, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 551, + "name": "_index", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 547, + "src": "3140:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "<", + "rightExpression": { + "expression": { + "id": 552, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3149:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 553, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "3163:6:1", + "memberName": "length", + "nodeType": "MemberAccess", + "src": "3149:20:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3140:29:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "696e646578206f7574206f6620626f756e64", + "id": 555, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3171:20:1", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_9bd393a5b80babd34996e9cb4b5d65af0ee420fc5edf77dbd7c6d563caa3bff2", + "typeString": "literal_string \"index out of bound\"" + }, + "value": "index out of bound" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_9bd393a5b80babd34996e9cb4b5d65af0ee420fc5edf77dbd7c6d563caa3bff2", + "typeString": "literal_string \"index out of bound\"" + } + ], + "id": 550, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "3132:7:1", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 556, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "3132:60:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 557, + "nodeType": "ExpressionStatement", + "src": "3132:60:1" + }, + { + "body": { + "id": 581, + "nodeType": "Block", + "src": "3254:56:1", + "statements": [ + { + "expression": { + "id": 579, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "baseExpression": { + "id": 571, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3264:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 573, + "indexExpression": { + "id": 572, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 559, + "src": "3278:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": true, + "nodeType": "IndexAccess", + "src": "3264:16:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "baseExpression": { + "id": 574, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3283:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 578, + "indexExpression": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 577, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 575, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 559, + "src": "3297:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "+", + "rightExpression": { + "hexValue": "31", + "id": 576, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3301:1:1", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + }, + "src": "3297:5:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "isConstant": false, + "isLValue": true, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "3283:20:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "src": "3264:39:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage", + "typeString": "struct AssetReference storage ref" + } + }, + "id": 580, + "nodeType": "ExpressionStatement", + "src": "3264:39:1" + } + ] + }, + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 567, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 562, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 559, + "src": "3221:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "<", + "rightExpression": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 566, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "expression": { + "id": 563, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3225:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 564, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "3239:6:1", + "memberName": "length", + "nodeType": "MemberAccess", + "src": "3225:20:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "-", + "rightExpression": { + "hexValue": "31", + "id": 565, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3246:1:1", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + }, + "src": "3225:22:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3221:26:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 582, + "initializationExpression": { + "assignments": [ + 559 + ], + "declarations": [ + { + "constant": false, + "id": 559, + "mutability": "mutable", + "name": "i", + "nameLocation": "3209:1:1", + "nodeType": "VariableDeclaration", + "scope": 582, + "src": "3204:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 558, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "3204:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 561, + "initialValue": { + "id": 560, + "name": "_index", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 547, + "src": "3213:6:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "3204:15:1" + }, + "loopExpression": { + "expression": { + "id": 569, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "3249:3:1", + "subExpression": { + "id": 568, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 559, + "src": "3249:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 570, + "nodeType": "ExpressionStatement", + "src": "3249:3:1" + }, + "nodeType": "ForStatement", + "src": "3199:111:1" + }, + { + "expression": { + "arguments": [], + "expression": { + "argumentTypes": [], + "expression": { + "id": 583, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3315:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 585, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "3329:3:1", + "memberName": "pop", + "nodeType": "MemberAccess", + "src": "3315:17:1", + "typeDescriptions": { + "typeIdentifier": "t_function_arraypop_nonpayable$_t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr_$returns$__$bound_to$_t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr_$", + "typeString": "function (struct AssetReference storage ref[] storage pointer)" + } + }, + "id": 586, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "3315:19:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 587, + "nodeType": "ExpressionStatement", + "src": "3315:19:1" + } + ] + }, + "functionSelector": "21db00b3", + "id": 589, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "removeItemFromList", + "nameLocation": "3087:18:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 548, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 547, + "mutability": "mutable", + "name": "_index", + "nameLocation": "3111:6:1", + "nodeType": "VariableDeclaration", + "scope": 589, + "src": "3106:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 546, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "3106:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "3105:13:1" + }, + "returnParameters": { + "id": 549, + "nodeType": "ParameterList", + "parameters": [], + "src": "3126:0:1" + }, + "scope": 621, + "src": "3078:261:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 598, + "nodeType": "Block", + "src": "3453:31:1", + "statements": [ + { + "expression": { + "id": 596, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3466:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "functionReturnParameters": 595, + "id": 597, + "nodeType": "Return", + "src": "3459:20:1" + } + ] + }, + "functionSelector": "c8da01af", + "id": 599, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "getAllAssetReferences", + "nameLocation": "3383:21:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 590, + "nodeType": "ParameterList", + "parameters": [], + "src": "3404:2:1" + }, + "returnParameters": { + "id": 595, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 594, + "mutability": "mutable", + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "VariableDeclaration", + "scope": 599, + "src": "3428:23:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_memory_ptr_$dyn_memory_ptr", + "typeString": "struct AssetReference[]" + }, + "typeName": { + "baseType": { + "id": 592, + "nodeType": "UserDefinedTypeName", + "pathNode": { + "id": 591, + "name": "AssetReference", + "nameLocations": [ + "3428:14:1" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 210, + "src": "3428:14:1" + }, + "referencedDeclaration": 210, + "src": "3428:14:1", + "typeDescriptions": { + "typeIdentifier": "t_struct$_AssetReference_$210_storage_ptr", + "typeString": "struct AssetReference" + } + }, + "id": 593, + "nodeType": "ArrayTypeName", + "src": "3428:16:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage_ptr", + "typeString": "struct AssetReference[]" + } + }, + "visibility": "internal" + } + ], + "src": "3427:25:1" + }, + "scope": 621, + "src": "3374:110:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 619, + "nodeType": "Block", + "src": "3561:94:1", + "statements": [ + { + "body": { + "id": 617, + "nodeType": "Block", + "src": "3615:36:1", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 614, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 603, + "src": "3642:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 613, + "name": "removeItemFromList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 589, + "src": "3623:18:1", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_uint256_$returns$__$", + "typeString": "function (uint256)" + } + }, + "id": 615, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "3623:21:1", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 616, + "nodeType": "ExpressionStatement", + "src": "3623:21:1" + } + ] + }, + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 609, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 606, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 603, + "src": "3584:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "<", + "rightExpression": { + "expression": { + "id": 607, + "name": "assetRefsList", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 227, + "src": "3588:13:1", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_struct$_AssetReference_$210_storage_$dyn_storage", + "typeString": "struct AssetReference storage ref[] storage ref" + } + }, + "id": 608, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "3602:6:1", + "memberName": "length", + "nodeType": "MemberAccess", + "src": "3588:20:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "3584:24:1", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 618, + "initializationExpression": { + "assignments": [ + 603 + ], + "declarations": [ + { + "constant": false, + "id": 603, + "mutability": "mutable", + "name": "i", + "nameLocation": "3577:1:1", + "nodeType": "VariableDeclaration", + "scope": 618, + "src": "3572:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 602, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "3572:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 605, + "initialValue": { + "hexValue": "30", + "id": 604, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "3581:1:1", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "nodeType": "VariableDeclarationStatement", + "src": "3572:10:1" + }, + "loopExpression": { + "expression": { + "id": 611, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "3610:3:1", + "subExpression": { + "id": 610, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 603, + "src": "3610:1:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 612, + "nodeType": "ExpressionStatement", + "src": "3610:3:1" + }, + "nodeType": "ForStatement", + "src": "3567:84:1" + } + ] + }, + "functionSelector": "6b4bd58c", + "id": 620, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "resetAssetRefsList", + "nameLocation": "3533:18:1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 600, + "nodeType": "ParameterList", + "parameters": [], + "src": "3551:2:1" + }, + "returnParameters": { + "id": 601, + "nodeType": "ParameterList", + "parameters": [], + "src": "3561:0:1" + }, + "scope": 621, + "src": "3524:131:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 622, + "src": "191:3466:1", + "usedErrors": [] + } + ], + "src": "37:3621:1" + }, + "functionHashes": { + "addOwner(address)": "7065cb48", + "burn(uint256)": "42966c68", + "checkValidBridgeBack(string,uint256,address)": "9ca27299", + "createAssetReference(string,uint256,address)": "663bcc92", + "deleteAssetReference(string)": "ade3222b", + "getAllAssetReferences()": "c8da01af", + "getAssetReference(string)": "b1fa2580", + "isAssetLocked(string)": "1ae4eb68", + "isPresent(string)": "bc548275", + "lockAssetReference(string)": "63773a6c", + "mint(address,uint256)": "40c10f19", + "owners()": "affe39c1", + "removeItemFromList(uint256)": "21db00b3", + "removeOwner(address)": "173825d9", + "resetAssetRefsList()": "6b4bd58c", + "unLockAssetReference(string)": "8ef45293" + }, + "gasEstimates": { + "creation": { + "codeDepositCost": "1279400", + "executionCost": "infinite", + "totalCost": "infinite" + }, + "external": { + "addOwner(address)": "infinite", + "burn(uint256)": "infinite", + "checkValidBridgeBack(string,uint256,address)": "infinite", + "createAssetReference(string,uint256,address)": "infinite", + "deleteAssetReference(string)": "infinite", + "getAllAssetReferences()": "infinite", + "getAssetReference(string)": "infinite", + "isAssetLocked(string)": "infinite", + "isPresent(string)": "infinite", + "lockAssetReference(string)": "infinite", + "mint(address,uint256)": "infinite", + "owners()": "infinite", + "removeItemFromList(uint256)": "infinite", + "removeOwner(address)": "infinite", + "resetAssetRefsList()": "infinite", + "unLockAssetReference(string)": "infinite" + } + } +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol new file mode 100644 index 0000000000..1ee2f8e52d --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/MyOwnable.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.15; + +contract MyOwnable { + address[] private _owners; + + event OwnershipRemoved(address indexed previousOwner); + event OwnershipAdded(address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _addNewOwner(_msgSender()); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owners. + */ + function owners() public view virtual returns (address[] memory) { + return _owners; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + bool flag = false; + for (uint i = 0; i < _owners.length; i++) { + if (_owners[i] == _msgSender()) { + flag = true; + } + } + require(flag == true, "Ownable: caller is not an owner"); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function addOwner(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _addNewOwner(newOwner); + } + + function removeOwner(address oldOwner) public virtual onlyOwner { + require(oldOwner != address(0), "Ownable: old owner is the zero address"); + _removeOwner(oldOwner); + } + + function _addNewOwner(address newOwner) internal virtual { + _owners.push(newOwner); + emit OwnershipAdded(newOwner); + } + + function _removeOwner(address owner) internal virtual { + for (uint i = 0; i < _owners.length; i++) { + if (_owners[i] == owner) { + _owners[i] = _owners[_owners.length - 1]; + _owners.pop(); + } + } + emit OwnershipRemoved(owner); + } + + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract-test.sol b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract-test.sol new file mode 100644 index 0000000000..ad03bbac0d --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract-test.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_testassetRefContract.sol"; +import "../contracts/CBDCcontract.sol"; +import "../contracts/AssetReferenceContract.sol"; + +contract AssetReferenceContractTest { + + AssetReferenceContract assetRefContract; + CBDCcontract cbdc_contract; + + function beforeEach () public { + cbdc_contract = new CBDCcontract(); + assetRefContract = new AssetReferenceContract(address(cbdc_contract)); + + cbdc_contract.setAssetReferenceContract(address(assetRefContract)); + cbdc_contract.transferOwnership(address(assetRefContract)); + } + + function createAssetReferenceSuccessfully () public { + assetRefContract.createAssetReference("id1", 123, address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)); + + AssetReference memory asset = assetRefContract.getAssetReference("id1"); + Assert.equal(asset.id, "id1", "asset reference id did not match"); + Assert.equal(asset.isLocked, false, "asset reference lock state did not match"); + Assert.equal(asset.amount, 123, "asset reference amount did not match"); + Assert.equal(asset.recipient, address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2), "asset reference recipient did not match"); + } + + function lockAndUnlockAssetReferenceSuccessfully () public { + assetRefContract.createAssetReference("id1", 123, address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)); + + assetRefContract.lockAssetReference("id1"); + AssetReference memory asset1 = assetRefContract.getAssetReference("id1"); + Assert.equal(asset1.id, "id1", "asset reference id did not match"); + Assert.equal(asset1.isLocked, true, "asset reference lock state did not match"); + + assetRefContract.unLockAssetReference("id1"); + asset1 = assetRefContract.getAssetReference("id1"); + Assert.equal(asset1.id, "id1", "asset reference id did not match"); + Assert.equal(asset1.isLocked, false, "asset reference lock state did not match"); + } + + function deleteAssetReferenceSuccessfully () public { + assetRefContract.createAssetReference("id1", 123, address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)); + assetRefContract.deleteAssetReference("id1"); + + bool exists = assetRefContract.isPresent("id1"); + Assert.equal(exists, false, "asset reference did not match"); + } + + function mintTokens () public { + assetRefContract.mint(address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2), 123); + + uint256 balance = cbdc_contract.balanceOf(address(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)); + Assert.equal(balance, 123, "tokens minted did not match"); + } + +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol new file mode 100644 index 0000000000..cb041b11cb --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/asset-reference-contract/asset-reference-contract.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.15; + +import "./MyOwnable.sol"; + +struct AssetReference { + string id; + bool isLocked; + uint amount; + address recipient; +} + +contract AssetReferenceContract is MyOwnable { + address cbdc_contract; + mapping (string => AssetReference) assets; + mapping (string => bool) assetExists; + + // only used for UI purposes. The list is not updated in each operation + // so it should not be used for other ends than this + // The main use for this is to demonstrate in the UI the asset references list + AssetReference[] assetRefsList; + + constructor(address account) { + cbdc_contract = account; + } + + function createAssetReference(string calldata id, uint amount, address recipient) public onlyOwner { + assets[id].id= id; + assets[id].amount = amount; + assets[id].isLocked = false; + assets[id].recipient = recipient; + + assetExists[id] = true; + + // used for UI purposes only + assetRefsList.push(AssetReference( + id, + false, + amount, + recipient + )); + } + + function lockAssetReference(string calldata id) public onlyOwner { + require(isPresent(id), "The asset reference does not exist"); + require(!isAssetLocked(id), "The asset reference is already locked"); + + assets[id].isLocked = true; + } + + function unLockAssetReference(string calldata id) public onlyOwner { + require(isPresent(id), "The asset reference does not exist"); + + assets[id].isLocked = false; + } + + function deleteAssetReference(string calldata id) public onlyOwner { + require(isPresent(id), "The asset reference does not exist"); + require(isAssetLocked(id), "The asset reference is locked"); + + burn(assets[id].amount); + + delete assets[id]; + assetExists[id] = false; + + // used for UI purposes only + for (uint i = 0; i < assetRefsList.length; i++) { + if (keccak256(abi.encodePacked(assetRefsList[i].id)) == keccak256(abi.encodePacked(id))) { + removeItemFromList(i); + } + } + } + + function isPresent(string calldata id) public view returns (bool) { + return assetExists[id]; + } + + function isAssetLocked(string calldata id) public view returns (bool) { + return assets[id].isLocked; + } + + function getAssetReference(string calldata id) public view returns (AssetReference memory) { + return assets[id]; + } + + function mint(address account, uint256 amount) public onlyOwner { + (bool success, ) = cbdc_contract.call( + abi.encodeWithSignature("mint(address,uint256)", account, amount) + ); + + require(success, "mint call failed"); + } + + function burn(uint256 amount) public onlyOwner { + (bool success, ) = cbdc_contract.call( + abi.encodeWithSignature("burn(uint256)", amount) + ); + + require(success, "burn call failed"); + } + + function checkValidBridgeBack(string calldata id, uint256 amount, address user) public view returns (bool) { + require(isPresent(id), "The asset reference does not exist"); + + return (assets[id].amount >= amount) && (assets[id].recipient == user); + } + + // used for UI purposes only + function removeItemFromList(uint _index) public { + require(_index < assetRefsList.length, "index out of bound"); + + for (uint i = _index; i < assetRefsList.length-1; i++) { + assetRefsList[i] = assetRefsList[i + 1]; + } + assetRefsList.pop(); + } + + // used for UI purposes only + function getAllAssetReferences() public view returns (AssetReference[] memory) { + return assetRefsList; + } + + // used for testing purposes only + function resetAssetRefsList() public { + for (uint i = 0; i < assetRefsList.length; i++) { + removeItemFromList(i); + } + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/CBDCcontract.json b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/CBDCcontract.json new file mode 100644 index 0000000000..506ad749f3 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/CBDCcontract.json @@ -0,0 +1,2495 @@ +{ + "contractName": "CBDCcontract", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "asset_ref_id", + "type": "string" + } + ], + "name": "escrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + } + ], + "name": "resetBalanceOf", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contract_address", + "type": "address" + } + ], + "name": "setAssetReferenceContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "metadata": "{\"compiler\":{\"version\":\"0.8.16+commit.07a7930e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"asset_ref_id\",\"type\":\"string\"}],\"name\":\"escrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"}],\"name\":\"resetBalanceOf\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"contract_address\",\"type\":\"address\"}],\"name\":\"setAssetReferenceContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Create a sample ERC20 standard token\",\"kind\":\"dev\",\"methods\":{\"allowance(address,address)\":{\"details\":\"See {IERC20-allowance}.\"},\"approve(address,uint256)\":{\"details\":\"See {IERC20-approve}. Requirements: - `spender` cannot be the zero address.\"},\"balanceOf(address)\":{\"details\":\"See {IERC20-balanceOf}.\"},\"decimals()\":{\"details\":\"Returns the number of decimals used to get its user representation. For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`). Tokens usually opt for a value of 18, imitating the relationship between Ether and Wei. This is the value {ERC20} uses, unless this function is overridden; NOTE: This information is only used for _display_ purposes: it in no way affects any of the arithmetic of the contract, including {IERC20-balanceOf} and {IERC20-transfer}.\"},\"decreaseAllowance(address,uint256)\":{\"details\":\"Atomically decreases the allowance granted to `spender` by the caller. This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. Emits an {Approval} event indicating the updated allowance. Requirements: - `spender` cannot be the zero address. - `spender` must have allowance for the caller of at least `subtractedValue`.\"},\"increaseAllowance(address,uint256)\":{\"details\":\"Atomically increases the allowance granted to `spender` by the caller. This is an alternative to {approve} that can be used as a mitigation for problems described in {IERC20-approve}. Emits an {Approval} event indicating the updated allowance. Requirements: - `spender` cannot be the zero address.\"},\"name()\":{\"details\":\"Returns the name of the token.\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"renounceOwnership()\":{\"details\":\"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.\"},\"symbol()\":{\"details\":\"Returns the symbol of the token, usually a shorter version of the name.\"},\"totalSupply()\":{\"details\":\"See {IERC20-totalSupply}.\"},\"transfer(address,uint256)\":{\"details\":\"See {IERC20-transfer}. Requirements: - `recipient` cannot be the zero address. - the caller must have a balance of at least `amount`.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"See {IERC20-transferFrom}. Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}. Requirements: - `sender` and `recipient` cannot be the zero address. - `sender` must have a balance of at least `amount`. - the caller must have allowance for ``sender``'s tokens of at least `amount`.\"},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"}},\"title\":\"SampleERC20\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"/home/andre_9a/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol\":\"CBDCcontract\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/andre_9a/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol\":{\"keccak256\":\"0x0b74316abddd0f7e38c33034c987eb21560c8ef86d7b03c194a1050c09fee74e\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://b0a6ec93f638b1304b1e2ff5fc3dbaa0f5c98ee988c7d8e0a8d4c4eba35cb223\",\"dweb:/ipfs/QmQwRUJN7XPMpTod9SPiNyC6nmKiCHS7pgmmQjAsjYTLVo\"]},\"/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/access/Ownable.sol\":{\"keccak256\":\"0x24e0364e503a9bbde94c715d26573a76f14cd2a202d45f96f52134ab806b67b9\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://e12cbaa7378fd9b62280e4e1d164bedcb4399ce238f5f98fc0eefb7e50577981\",\"dweb:/ipfs/QmXRoFGUgfsaRkoPT5bxNMtSayKTQ8GZATLPXf69HcRA51\"]},\"/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol\":{\"keccak256\":\"0xd1d8caaeb45f78e0b0715664d56c220c283c89bf8b8c02954af86404d6b367f8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://300a0cc7be3b26c96c22a47ffa9530a585e1c4f2dba3021d9bf309dc63007487\",\"dweb:/ipfs/QmQmxsvxK6CaJmQ4D8vDCYPLHMqcMmZLcBqedG4GFAbzu9\"]},\"/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x61437cb513a887a1bbad006e7b1c8b414478427d33de47c5600af3c748f108da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2c3d0973630ed74f2b5ce3944677a885dc70ec32fc83b35f55045a10224da32b\",\"dweb:/ipfs/QmbefZ5RoEZKNHXCALfh683PnaNYzKPcKMFjyY1DVAgq8A\"]},\"/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"keccak256\":\"0x8de418a5503946cabe331f35fe242d3201a73f67f77aaeb7110acb1f30423aca\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5a376d3dda2cb70536c0a45c208b29b34ac560c4cb4f513a42079f96ba47d2dd\",\"dweb:/ipfs/QmZQg6gn1sUpM8wHzwNvSnihumUCAhxD119MpXeKp8B9s8\"]},\"/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92\",\"dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3\"]}},\"version\":1}", + "bytecode": "6080604052600680546001600160a01b031990811673f28d5769171bfbd2b3628d722e58129a6ae15022179091556007805490911690553480156200004357600080fd5b506040518060400160405280601a81526020017f43656e7472616c42616e6b4469676974616c43757272656e6379000000000000815250604051806040016040528060048152602001634342444360e01b815250620000b1620000ab620000d760201b60201c565b620000db565b6004620000bf8382620001d0565b506005620000ce8282620001d0565b5050506200029c565b3390565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200015657607f821691505b6020821081036200017757634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620001cb57600081815260208120601f850160051c81016020861015620001a65750805b601f850160051c820191505b81811015620001c757828155600101620001b2565b5050505b505050565b81516001600160401b03811115620001ec57620001ec6200012b565b6200020481620001fd845462000141565b846200017d565b602080601f8311600181146200023c5760008415620002235750858301515b600019600386901b1c1916600185901b178555620001c7565b600085815260208120601f198616915b828110156200026d578886015182559484019460019091019084016200024c565b50858210156200028c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6111c980620002ac6000396000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c806342966c68116100ad57806395d89b411161007157806395d89b411461025b578063a457c2d714610263578063a9059cbb14610276578063dd62ed3e14610289578063f2fde38b146102c257600080fd5b806342966c68146101e957806370a08231146101fc578063715018a6146102255780638d1a450d1461022d5780638da5cb5b1461024057600080fd5b8063313ce567116100f4578063313ce5671461018c5780633459feb21461019b57806339509351146101b057806340c10f19146101c3578063417d11af146101d657600080fd5b806306fdde0314610126578063095ea7b31461014457806318160ddd1461016757806323b872dd14610179575b600080fd5b61012e6102d5565b60405161013b9190610e40565b60405180910390f35b610157610152366004610e8f565b610367565b604051901515815260200161013b565b6003545b60405190815260200161013b565b610157610187366004610eb9565b61037e565b6040516012815260200161013b565b6101ae6101a9366004610ef5565b61042d565b005b6101576101be366004610e8f565b6104ad565b6101ae6101d1366004610e8f565b6104e9565b6101ae6101e4366004610f6a565b610521565b6101ae6101f7366004610fe6565b61062f565b61016b61020a366004610fff565b6001600160a01b031660009081526001602052604090205490565b6101ae610672565b6101ae61023b366004610fff565b6106a8565b6000546040516001600160a01b03909116815260200161013b565b61012e6106f4565b610157610271366004610e8f565b610703565b610157610284366004610e8f565b61079c565b61016b610297366004611021565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b6101ae6102d0366004610fff565b6107a9565b6060600480546102e490611054565b80601f016020809104026020016040519081016040528092919081815260200182805461031090611054565b801561035d5780601f106103325761010080835404028352916020019161035d565b820191906000526020600020905b81548152906001019060200180831161034057829003601f168201915b5050505050905090565b6000610374338484610841565b5060015b92915050565b600061038b848484610966565b6001600160a01b0384166000908152600260209081526040808320338452909152902054828110156104155760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b6104228533858403610841565b506001949350505050565b6000805b828110156104a75761046384848381811061044e5761044e61108e565b905060200201602081019061020a9190610fff565b915061049584848381811061047a5761047a61108e565b905060200201602081019061048f9190610fff565b83610b34565b8061049f816110ba565b915050610431565b50505050565b3360008181526002602090815260408083206001600160a01b038716845290915281205490916103749185906104e49086906110d3565b610841565b6000546001600160a01b031633146105135760405162461bcd60e51b815260040161040c906110e6565b61051d8282610c7a565b5050565b610529610d59565b60065461053f906001600160a01b03168461079c565b506007546040516000916001600160a01b03169061056790859085908890339060240161111b565b60408051601f198184030181529181526020820180516001600160e01b031663331de64960e11b1790525161059c9190611164565b6000604051808303816000865af19150503d80600081146105d9576040519150601f19603f3d011682016040523d82523d6000602084013e6105de565b606091505b50509050806104a75760405162461bcd60e51b815260206004820181905260248201527f63726561746541737365745265666572656e63652063616c6c206661696c6564604482015260640161040c565b6000546001600160a01b031633146106595760405162461bcd60e51b815260040161040c906110e6565b60065461066f906001600160a01b031682610b34565b50565b6000546001600160a01b0316331461069c5760405162461bcd60e51b815260040161040c906110e6565b6106a66000610dcc565b565b6000546001600160a01b031633146106d25760405162461bcd60e51b815260040161040c906110e6565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6060600580546102e490611054565b3360009081526002602090815260408083206001600160a01b0386168452909152812054828110156107855760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b606482015260840161040c565b6107923385858403610841565b5060019392505050565b6000610374338484610966565b6000546001600160a01b031633146107d35760405162461bcd60e51b815260040161040c906110e6565b6001600160a01b0381166108385760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161040c565b61066f81610dcc565b6001600160a01b0383166108a35760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161040c565b6001600160a01b0382166109045760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161040c565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166109ca5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161040c565b6001600160a01b038216610a2c5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161040c565b6001600160a01b03831660009081526001602052604090205481811015610aa45760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161040c565b6001600160a01b03808516600090815260016020526040808220858503905591851681529081208054849290610adb9084906110d3565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b2791815260200190565b60405180910390a36104a7565b6001600160a01b038216610b945760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161040c565b6001600160a01b03821660009081526001602052604090205481811015610c085760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161040c565b6001600160a01b0383166000908152600160205260408120838303905560038054849290610c37908490611180565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610959565b6001600160a01b038216610cd05760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161040c565b8060036000828254610ce291906110d3565b90915550506001600160a01b03821660009081526001602052604081208054839290610d0f9084906110d3565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6007546001600160a01b03166106a65760405162461bcd60e51b815260206004820152603260248201527f43424443636f6e74726163743a206173736574207265666572656e636520636f6044820152711b9d1c9858dd081b9bdd081919599a5b995960721b606482015260840161040c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60005b83811015610e37578181015183820152602001610e1f565b50506000910152565b6020815260008251806020840152610e5f816040850160208701610e1c565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114610e8a57600080fd5b919050565b60008060408385031215610ea257600080fd5b610eab83610e73565b946020939093013593505050565b600080600060608486031215610ece57600080fd5b610ed784610e73565b9250610ee560208501610e73565b9150604084013590509250925092565b60008060208385031215610f0857600080fd5b823567ffffffffffffffff80821115610f2057600080fd5b818501915085601f830112610f3457600080fd5b813581811115610f4357600080fd5b8660208260051b8501011115610f5857600080fd5b60209290920196919550909350505050565b600080600060408486031215610f7f57600080fd5b83359250602084013567ffffffffffffffff80821115610f9e57600080fd5b818601915086601f830112610fb257600080fd5b813581811115610fc157600080fd5b876020828501011115610fd357600080fd5b6020830194508093505050509250925092565b600060208284031215610ff857600080fd5b5035919050565b60006020828403121561101157600080fd5b61101a82610e73565b9392505050565b6000806040838503121561103457600080fd5b61103d83610e73565b915061104b60208401610e73565b90509250929050565b600181811c9082168061106857607f821691505b60208210810361108857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016110cc576110cc6110a4565b5060010190565b80820180821115610378576103786110a4565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60608152836060820152838560808301376000608085830181019190915260208201939093526001600160a01b03919091166040820152601f909201601f191690910101919050565b60008251611176818460208701610e1c565b9190910192915050565b81810381811115610378576103786110a456fea26469706673582212208e0d27b796e78bc197361bd99b802d95e06f14b4ad47842414086729e5f6ad7c64736f6c63430008100033", + "deployedBytecode": "608060405234801561001057600080fd5b50600436106101215760003560e01c806342966c68116100ad57806395d89b411161007157806395d89b411461025b578063a457c2d714610263578063a9059cbb14610276578063dd62ed3e14610289578063f2fde38b146102c257600080fd5b806342966c68146101e957806370a08231146101fc578063715018a6146102255780638d1a450d1461022d5780638da5cb5b1461024057600080fd5b8063313ce567116100f4578063313ce5671461018c5780633459feb21461019b57806339509351146101b057806340c10f19146101c3578063417d11af146101d657600080fd5b806306fdde0314610126578063095ea7b31461014457806318160ddd1461016757806323b872dd14610179575b600080fd5b61012e6102d5565b60405161013b9190610e40565b60405180910390f35b610157610152366004610e8f565b610367565b604051901515815260200161013b565b6003545b60405190815260200161013b565b610157610187366004610eb9565b61037e565b6040516012815260200161013b565b6101ae6101a9366004610ef5565b61042d565b005b6101576101be366004610e8f565b6104ad565b6101ae6101d1366004610e8f565b6104e9565b6101ae6101e4366004610f6a565b610521565b6101ae6101f7366004610fe6565b61062f565b61016b61020a366004610fff565b6001600160a01b031660009081526001602052604090205490565b6101ae610672565b6101ae61023b366004610fff565b6106a8565b6000546040516001600160a01b03909116815260200161013b565b61012e6106f4565b610157610271366004610e8f565b610703565b610157610284366004610e8f565b61079c565b61016b610297366004611021565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b6101ae6102d0366004610fff565b6107a9565b6060600480546102e490611054565b80601f016020809104026020016040519081016040528092919081815260200182805461031090611054565b801561035d5780601f106103325761010080835404028352916020019161035d565b820191906000526020600020905b81548152906001019060200180831161034057829003601f168201915b5050505050905090565b6000610374338484610841565b5060015b92915050565b600061038b848484610966565b6001600160a01b0384166000908152600260209081526040808320338452909152902054828110156104155760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084015b60405180910390fd5b6104228533858403610841565b506001949350505050565b6000805b828110156104a75761046384848381811061044e5761044e61108e565b905060200201602081019061020a9190610fff565b915061049584848381811061047a5761047a61108e565b905060200201602081019061048f9190610fff565b83610b34565b8061049f816110ba565b915050610431565b50505050565b3360008181526002602090815260408083206001600160a01b038716845290915281205490916103749185906104e49086906110d3565b610841565b6000546001600160a01b031633146105135760405162461bcd60e51b815260040161040c906110e6565b61051d8282610c7a565b5050565b610529610d59565b60065461053f906001600160a01b03168461079c565b506007546040516000916001600160a01b03169061056790859085908890339060240161111b565b60408051601f198184030181529181526020820180516001600160e01b031663331de64960e11b1790525161059c9190611164565b6000604051808303816000865af19150503d80600081146105d9576040519150601f19603f3d011682016040523d82523d6000602084013e6105de565b606091505b50509050806104a75760405162461bcd60e51b815260206004820181905260248201527f63726561746541737365745265666572656e63652063616c6c206661696c6564604482015260640161040c565b6000546001600160a01b031633146106595760405162461bcd60e51b815260040161040c906110e6565b60065461066f906001600160a01b031682610b34565b50565b6000546001600160a01b0316331461069c5760405162461bcd60e51b815260040161040c906110e6565b6106a66000610dcc565b565b6000546001600160a01b031633146106d25760405162461bcd60e51b815260040161040c906110e6565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6060600580546102e490611054565b3360009081526002602090815260408083206001600160a01b0386168452909152812054828110156107855760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b606482015260840161040c565b6107923385858403610841565b5060019392505050565b6000610374338484610966565b6000546001600160a01b031633146107d35760405162461bcd60e51b815260040161040c906110e6565b6001600160a01b0381166108385760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161040c565b61066f81610dcc565b6001600160a01b0383166108a35760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161040c565b6001600160a01b0382166109045760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161040c565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166109ca5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161040c565b6001600160a01b038216610a2c5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161040c565b6001600160a01b03831660009081526001602052604090205481811015610aa45760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161040c565b6001600160a01b03808516600090815260016020526040808220858503905591851681529081208054849290610adb9084906110d3565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b2791815260200190565b60405180910390a36104a7565b6001600160a01b038216610b945760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161040c565b6001600160a01b03821660009081526001602052604090205481811015610c085760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161040c565b6001600160a01b0383166000908152600160205260408120838303905560038054849290610c37908490611180565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610959565b6001600160a01b038216610cd05760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161040c565b8060036000828254610ce291906110d3565b90915550506001600160a01b03821660009081526001602052604081208054839290610d0f9084906110d3565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6007546001600160a01b03166106a65760405162461bcd60e51b815260206004820152603260248201527f43424443636f6e74726163743a206173736574207265666572656e636520636f6044820152711b9d1c9858dd081b9bdd081919599a5b995960721b606482015260840161040c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60005b83811015610e37578181015183820152602001610e1f565b50506000910152565b6020815260008251806020840152610e5f816040850160208701610e1c565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114610e8a57600080fd5b919050565b60008060408385031215610ea257600080fd5b610eab83610e73565b946020939093013593505050565b600080600060608486031215610ece57600080fd5b610ed784610e73565b9250610ee560208501610e73565b9150604084013590509250925092565b60008060208385031215610f0857600080fd5b823567ffffffffffffffff80821115610f2057600080fd5b818501915085601f830112610f3457600080fd5b813581811115610f4357600080fd5b8660208260051b8501011115610f5857600080fd5b60209290920196919550909350505050565b600080600060408486031215610f7f57600080fd5b83359250602084013567ffffffffffffffff80821115610f9e57600080fd5b818601915086601f830112610fb257600080fd5b813581811115610fc157600080fd5b876020828501011115610fd357600080fd5b6020830194508093505050509250925092565b600060208284031215610ff857600080fd5b5035919050565b60006020828403121561101157600080fd5b61101a82610e73565b9392505050565b6000806040838503121561103457600080fd5b61103d83610e73565b915061104b60208401610e73565b90509250929050565b600181811c9082168061106857607f821691505b60208210810361108857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016110cc576110cc6110a4565b5060010190565b80820180821115610378576103786110a4565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60608152836060820152838560808301376000608085830181019190915260208201939093526001600160a01b03919091166040820152601f909201601f191690910101919050565b60008251611176818460208701610e1c565b9190910192915050565b81810381811115610378576103786110a456fea26469706673582212208e0d27b796e78bc197361bd99b802d95e06f14b4ad47842414086729e5f6ad7c64736f6c63430008100033", + "sourceMap": "319:1524:0:-:0;;;366:76;;;-1:-1:-1;;;;;;366:76:0;;;399:42;366:76;;;;448:39;;;;;;;;494:65;;;;;;;;;;1963:113:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1963:113:2;;;921:32:1;940:12;:10;;;:12;;:::i;:::-;921:18;:32::i;:::-;2029:5:2;:13;2037:5;2029;:13;:::i;:::-;-1:-1:-1;2052:7:2;:17;2062:7;2052;:17;:::i;:::-;;1963:113;;319:1524:0;;640:96:5;719:10;;640:96::o;2270:187:1:-;2343:16;2362:6;;-1:-1:-1;;;;;2378:17:1;;;-1:-1:-1;;;;;;2378:17:1;;;;;;2410:40;;2362:6;;;;;;;2410:40;;2343:16;2410:40;2333:124;2270:187;:::o;14:127:6:-;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:380;225:1;221:12;;;;268;;;289:61;;343:4;335:6;331:17;321:27;;289:61;396:2;388:6;385:14;365:18;362:38;359:161;;442:10;437:3;433:20;430:1;423:31;477:4;474:1;467:15;505:4;502:1;495:15;359:161;;146:380;;;:::o;657:545::-;759:2;754:3;751:11;748:448;;;795:1;820:5;816:2;809:17;865:4;861:2;851:19;935:2;923:10;919:19;916:1;912:27;906:4;902:38;971:4;959:10;956:20;953:47;;;-1:-1:-1;994:4:6;953:47;1049:2;1044:3;1040:12;1037:1;1033:20;1027:4;1023:31;1013:41;;1104:82;1122:2;1115:5;1112:13;1104:82;;;1167:17;;;1148:1;1137:13;1104:82;;;1108:3;;;748:448;657:545;;;:::o;1378:1352::-;1498:10;;-1:-1:-1;;;;;1520:30:6;;1517:56;;;1553:18;;:::i;:::-;1582:97;1672:6;1632:38;1664:4;1658:11;1632:38;:::i;:::-;1626:4;1582:97;:::i;:::-;1734:4;;1798:2;1787:14;;1815:1;1810:663;;;;2517:1;2534:6;2531:89;;;-1:-1:-1;2586:19:6;;;2580:26;2531:89;-1:-1:-1;;1335:1:6;1331:11;;;1327:24;1323:29;1313:40;1359:1;1355:11;;;1310:57;2633:81;;1780:944;;1810:663;604:1;597:14;;;641:4;628:18;;-1:-1:-1;;1846:20:6;;;1964:236;1978:7;1975:1;1972:14;1964:236;;;2067:19;;;2061:26;2046:42;;2159:27;;;;2127:1;2115:14;;;;1994:19;;1964:236;;;1968:3;2228:6;2219:7;2216:19;2213:201;;;2289:19;;;2283:26;-1:-1:-1;;2372:1:6;2368:14;;;2384:3;2364:24;2360:37;2356:42;2341:58;2326:74;;2213:201;-1:-1:-1;;;;;2460:1:6;2444:14;;;2440:22;2427:36;;-1:-1:-1;1378:1352:6:o;:::-;319:1524:0;;;;;;", + "deployedSourceMap": "319:1524:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2141:98:2;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;4238:166;;;;;;:::i;:::-;;:::i;:::-;;;1272:14:6;;1265:22;1247:41;;1235:2;1220:18;4238:166:2;1107:187:6;3229:106:2;3316:12;;3229:106;;;1445:25:6;;;1433:2;1418:18;3229:106:2;1299:177:6;4871:478:2;;;;;;:::i;:::-;;:::i;3078:91::-;;;3160:2;1956:36:6;;1944:2;1929:18;3078:91:2;1814:184:6;1600:241:0;;;;;;:::i;:::-;;:::i;:::-;;5744:212:2;;;;;;:::i;:::-;;:::i;705:105:0:-;;;;;;:::i;:::-;;:::i;917:381::-;;;;;;:::i;:::-;;:::i;816:95::-;;;;;;:::i;:::-;;:::i;3393:125:2:-;;;;;;:::i;:::-;-1:-1:-1;;;;;3493:18:2;3467:7;3493:18;;;:9;:18;;;;;;;3393:125;1668:101:1;;;:::i;565:134:0:-;;;;;;:::i;:::-;;:::i;1036:85:1:-;1082:7;1108:6;1036:85;;-1:-1:-1;;;;;1108:6:1;;;3810:51:6;;3798:2;3783:18;1036:85:1;3664:203:6;2352:102:2;;;:::i;6443:405::-;;;;;;:::i;:::-;;:::i;3721:172::-;;;;;;:::i;:::-;;:::i;3951:149::-;;;;;;:::i;:::-;-1:-1:-1;;;;;4066:18:2;;;4040:7;4066:18;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;3951:149;1918:198:1;;;;;;:::i;:::-;;:::i;2141:98:2:-;2195:13;2227:5;2220:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2141:98;:::o;4238:166::-;4321:4;4337:39;719:10:5;4360:7:2;4369:6;4337:8;:39::i;:::-;-1:-1:-1;4393:4:2;4238:166;;;;;:::o;4871:478::-;5007:4;5023:36;5033:6;5041:9;5052:6;5023:9;:36::i;:::-;-1:-1:-1;;;;;5097:19:2;;5070:24;5097:19;;;:11;:19;;;;;;;;719:10:5;5097:33:2;;;;;;;;5148:26;;;;5140:79;;;;-1:-1:-1;;;5140:79:2;;4724:2:6;5140:79:2;;;4706:21:6;4763:2;4743:18;;;4736:30;4802:34;4782:18;;;4775:62;-1:-1:-1;;;4853:18:6;;;4846:38;4901:19;;5140:79:2;;;;;;;;;5253:57;5262:6;719:10:5;5303:6:2;5284:16;:25;5253:8;:57::i;:::-;-1:-1:-1;5338:4:2;;4871:478;-1:-1:-1;;;;4871:478:2:o;1600:241:0:-;1672:14;;1696:139;1713:19;;;1696:139;;;1762:22;1772:8;;1781:1;1772:11;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;1762:22::-;1753:31;;1798:26;1804:8;;1813:1;1804:11;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;1817:6;1798:5;:26::i;:::-;1734:3;;;;:::i;:::-;;;;1696:139;;;;1662:179;1600:241;;:::o;5744:212:2:-;719:10:5;5832:4:2;5880:25;;;:11;:25;;;;;;;;-1:-1:-1;;;;;5880:34:2;;;;;;;;;;5832:4;;5848:80;;5871:7;;5880:47;;5917:10;;5880:47;:::i;:::-;5848:8;:80::i;705:105:0:-;1082:7:1;1108:6;-1:-1:-1;;;;;1108:6:1;719:10:5;1248:23:1;1240:68;;;;-1:-1:-1;;;1240:68:1;;;;;;;:::i;:::-;781:22:0::1;787:7;796:6;781:5;:22::i;:::-;705:105:::0;;:::o;917:381::-;1515:24;:22;:24::i;:::-;1023:14:::1;::::0;1014:32:::1;::::0;-1:-1:-1;;;;;1023:14:0::1;1039:6:::0;1014:8:::1;:32::i;:::-;-1:-1:-1::0;1076:18:0::1;::::0;1113:105:::1;::::0;1058:12:::1;::::0;-1:-1:-1;;;;;1076:18:0::1;::::0;1113:105:::1;::::0;1185:12;;;;1199:6;;1207:10:::1;::::0;1113:105:::1;;;:::i;:::-;;::::0;;-1:-1:-1;;1113:105:0;;::::1;::::0;;;;;;::::1;::::0;::::1;::::0;;-1:-1:-1;;;;;1113:105:0::1;-1:-1:-1::0;;;1113:105:0::1;::::0;;1076:152;::::1;::::0;1113:105;1076:152:::1;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1057:171;;;1247:7;1239:52;;;::::0;-1:-1:-1;;;1239:52:0;;6888:2:6;1239:52:0::1;::::0;::::1;6870:21:6::0;;;6907:18;;;6900:30;6966:34;6946:18;;;6939:62;7018:18;;1239:52:0::1;6686:356:6::0;816:95:0;1082:7:1;1108:6;-1:-1:-1;;;;;1108:6:1;719:10:5;1248:23:1;1240:68;;;;-1:-1:-1;;;1240:68:1;;;;;;;:::i;:::-;881:14:0::1;::::0;875:29:::1;::::0;-1:-1:-1;;;;;881:14:0::1;897:6:::0;875:5:::1;:29::i;:::-;816:95:::0;:::o;1668:101:1:-;1082:7;1108:6;-1:-1:-1;;;;;1108:6:1;719:10:5;1248:23:1;1240:68;;;;-1:-1:-1;;;1240:68:1;;;;;;;:::i;:::-;1732:30:::1;1759:1;1732:18;:30::i;:::-;1668:101::o:0;565:134:0:-;1082:7:1;1108:6;-1:-1:-1;;;;;1108:6:1;719:10:5;1248:23:1;1240:68;;;;-1:-1:-1;;;1240:68:1;;;;;;;:::i;:::-;655:18:0::1;:37:::0;;-1:-1:-1;;;;;;655:37:0::1;-1:-1:-1::0;;;;;655:37:0;;;::::1;::::0;;;::::1;::::0;;565:134::o;2352:102:2:-;2408:13;2440:7;2433:14;;;;;:::i;6443:405::-;719:10:5;6536:4:2;6579:25;;;:11;:25;;;;;;;;-1:-1:-1;;;;;6579:34:2;;;;;;;;;;6631:35;;;;6623:85;;;;-1:-1:-1;;;6623:85:2;;7249:2:6;6623:85:2;;;7231:21:6;7288:2;7268:18;;;7261:30;7327:34;7307:18;;;7300:62;-1:-1:-1;;;7378:18:6;;;7371:35;7423:19;;6623:85:2;7047:401:6;6623:85:2;6742:67;719:10:5;6765:7:2;6793:15;6774:16;:34;6742:8;:67::i;:::-;-1:-1:-1;6837:4:2;;6443:405;-1:-1:-1;;;6443:405:2:o;3721:172::-;3807:4;3823:42;719:10:5;3847:9:2;3858:6;3823:9;:42::i;1918:198:1:-;1082:7;1108:6;-1:-1:-1;;;;;1108:6:1;719:10:5;1248:23:1;1240:68;;;;-1:-1:-1;;;1240:68:1;;;;;;;:::i;:::-;-1:-1:-1;;;;;2006:22:1;::::1;1998:73;;;::::0;-1:-1:-1;;;1998:73:1;;7655:2:6;1998:73:1::1;::::0;::::1;7637:21:6::0;7694:2;7674:18;;;7667:30;7733:34;7713:18;;;7706:62;-1:-1:-1;;;7784:18:6;;;7777:36;7830:19;;1998:73:1::1;7453:402:6::0;1998:73:1::1;2081:28;2100:8;2081:18;:28::i;10019:370:2:-:0;-1:-1:-1;;;;;10150:19:2;;10142:68;;;;-1:-1:-1;;;10142:68:2;;8062:2:6;10142:68:2;;;8044:21:6;8101:2;8081:18;;;8074:30;8140:34;8120:18;;;8113:62;-1:-1:-1;;;8191:18:6;;;8184:34;8235:19;;10142:68:2;7860:400:6;10142:68:2;-1:-1:-1;;;;;10228:21:2;;10220:68;;;;-1:-1:-1;;;10220:68:2;;8467:2:6;10220:68:2;;;8449:21:6;8506:2;8486:18;;;8479:30;8545:34;8525:18;;;8518:62;-1:-1:-1;;;8596:18:6;;;8589:32;8638:19;;10220:68:2;8265:398:6;10220:68:2;-1:-1:-1;;;;;10299:18:2;;;;;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;:36;;;10350:32;;1445:25:6;;;10350:32:2;;1418:18:6;10350:32:2;;;;;;;;10019:370;;;:::o;7322:713::-;-1:-1:-1;;;;;7457:20:2;;7449:70;;;;-1:-1:-1;;;7449:70:2;;8870:2:6;7449:70:2;;;8852:21:6;8909:2;8889:18;;;8882:30;8948:34;8928:18;;;8921:62;-1:-1:-1;;;8999:18:6;;;8992:35;9044:19;;7449:70:2;8668:401:6;7449:70:2;-1:-1:-1;;;;;7537:23:2;;7529:71;;;;-1:-1:-1;;;7529:71:2;;9276:2:6;7529:71:2;;;9258:21:6;9315:2;9295:18;;;9288:30;9354:34;9334:18;;;9327:62;-1:-1:-1;;;9405:18:6;;;9398:33;9448:19;;7529:71:2;9074:399:6;7529:71:2;-1:-1:-1;;;;;7693:17:2;;7669:21;7693:17;;;:9;:17;;;;;;7728:23;;;;7720:74;;;;-1:-1:-1;;;7720:74:2;;9680:2:6;7720:74:2;;;9662:21:6;9719:2;9699:18;;;9692:30;9758:34;9738:18;;;9731:62;-1:-1:-1;;;9809:18:6;;;9802:36;9855:19;;7720:74:2;9478:402:6;7720:74:2;-1:-1:-1;;;;;7828:17:2;;;;;;;:9;:17;;;;;;7848:22;;;7828:42;;7890:20;;;;;;;;:30;;7864:6;;7828:17;7890:30;;7864:6;;7890:30;:::i;:::-;;;;;;;;7953:9;-1:-1:-1;;;;;7936:35:2;7945:6;-1:-1:-1;;;;;7936:35:2;;7964:6;7936:35;;;;1445:25:6;;1433:2;1418:18;;1299:177;7936:35:2;;;;;;;;7982:46;9020:576;;-1:-1:-1;;;;;9103:21:2;;9095:67;;;;-1:-1:-1;;;9095:67:2;;10087:2:6;9095:67:2;;;10069:21:6;10126:2;10106:18;;;10099:30;10165:34;10145:18;;;10138:62;-1:-1:-1;;;10216:18:6;;;10209:31;10257:19;;9095:67:2;9885:397:6;9095:67:2;-1:-1:-1;;;;;9258:18:2;;9233:22;9258:18;;;:9;:18;;;;;;9294:24;;;;9286:71;;;;-1:-1:-1;;;9286:71:2;;10489:2:6;9286:71:2;;;10471:21:6;10528:2;10508:18;;;10501:30;10567:34;10547:18;;;10540:62;-1:-1:-1;;;10618:18:6;;;10611:32;10660:19;;9286:71:2;10287:398:6;9286:71:2;-1:-1:-1;;;;;9391:18:2;;;;;;:9;:18;;;;;9412:23;;;9391:44;;9455:12;:22;;9429:6;;9391:18;9455:22;;9429:6;;9455:22;:::i;:::-;;;;-1:-1:-1;;9493:37:2;;1445:25:6;;;9519:1:2;;-1:-1:-1;;;;;9493:37:2;;;;;1433:2:6;1418:18;9493:37:2;1299:177:6;8311:389:2;-1:-1:-1;;;;;8394:21:2;;8386:65;;;;-1:-1:-1;;;8386:65:2;;11025:2:6;8386:65:2;;;11007:21:6;11064:2;11044:18;;;11037:30;11103:33;11083:18;;;11076:61;11154:18;;8386:65:2;10823:355:6;8386:65:2;8538:6;8522:12;;:22;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;;;8554:18:2;;;;;;:9;:18;;;;;:28;;8576:6;;8554:18;:28;;8576:6;;8554:28;:::i;:::-;;;;-1:-1:-1;;8597:37:2;;1445:25:6;;;-1:-1:-1;;;;;8597:37:2;;;8614:1;;8597:37;;1433:2:6;1418:18;8597:37:2;;;;;;;705:105:0;;:::o;1304:168::-;1378:18;;-1:-1:-1;;;;;1378:18:0;1370:95;;;;-1:-1:-1;;;1370:95:0;;11385:2:6;1370:95:0;;;11367:21:6;11424:2;11404:18;;;11397:30;11463:34;11443:18;;;11436:62;-1:-1:-1;;;11514:18:6;;;11507:48;11572:19;;1370:95:0;11183:414:6;2270:187:1;2343:16;2362:6;;-1:-1:-1;;;;;2378:17:1;;;-1:-1:-1;;;;;;2378:17:1;;;;;;2410:40;;2362:6;;;;;;;2410:40;;2343:16;2410:40;2333:124;2270:187;:::o;14:250:6:-;99:1;109:113;123:6;120:1;117:13;109:113;;;199:11;;;193:18;180:11;;;173:39;145:2;138:10;109:113;;;-1:-1:-1;;256:1:6;238:16;;231:27;14:250::o;269:396::-;418:2;407:9;400:21;381:4;450:6;444:13;493:6;488:2;477:9;473:18;466:34;509:79;581:6;576:2;565:9;561:18;556:2;548:6;544:15;509:79;:::i;:::-;649:2;628:15;-1:-1:-1;;624:29:6;609:45;;;;656:2;605:54;;269:396;-1:-1:-1;;269:396:6:o;670:173::-;738:20;;-1:-1:-1;;;;;787:31:6;;777:42;;767:70;;833:1;830;823:12;767:70;670:173;;;:::o;848:254::-;916:6;924;977:2;965:9;956:7;952:23;948:32;945:52;;;993:1;990;983:12;945:52;1016:29;1035:9;1016:29;:::i;:::-;1006:39;1092:2;1077:18;;;;1064:32;;-1:-1:-1;;;848:254:6:o;1481:328::-;1558:6;1566;1574;1627:2;1615:9;1606:7;1602:23;1598:32;1595:52;;;1643:1;1640;1633:12;1595:52;1666:29;1685:9;1666:29;:::i;:::-;1656:39;;1714:38;1748:2;1737:9;1733:18;1714:38;:::i;:::-;1704:48;;1799:2;1788:9;1784:18;1771:32;1761:42;;1481:328;;;;;:::o;2003:615::-;2089:6;2097;2150:2;2138:9;2129:7;2125:23;2121:32;2118:52;;;2166:1;2163;2156:12;2118:52;2206:9;2193:23;2235:18;2276:2;2268:6;2265:14;2262:34;;;2292:1;2289;2282:12;2262:34;2330:6;2319:9;2315:22;2305:32;;2375:7;2368:4;2364:2;2360:13;2356:27;2346:55;;2397:1;2394;2387:12;2346:55;2437:2;2424:16;2463:2;2455:6;2452:14;2449:34;;;2479:1;2476;2469:12;2449:34;2532:7;2527:2;2517:6;2514:1;2510:14;2506:2;2502:23;2498:32;2495:45;2492:65;;;2553:1;2550;2543:12;2492:65;2584:2;2576:11;;;;;2606:6;;-1:-1:-1;2003:615:6;;-1:-1:-1;;;;2003:615:6:o;2623:660::-;2703:6;2711;2719;2772:2;2760:9;2751:7;2747:23;2743:32;2740:52;;;2788:1;2785;2778:12;2740:52;2824:9;2811:23;2801:33;;2885:2;2874:9;2870:18;2857:32;2908:18;2949:2;2941:6;2938:14;2935:34;;;2965:1;2962;2955:12;2935:34;3003:6;2992:9;2988:22;2978:32;;3048:7;3041:4;3037:2;3033:13;3029:27;3019:55;;3070:1;3067;3060:12;3019:55;3110:2;3097:16;3136:2;3128:6;3125:14;3122:34;;;3152:1;3149;3142:12;3122:34;3197:7;3192:2;3183:6;3179:2;3175:15;3171:24;3168:37;3165:57;;;3218:1;3215;3208:12;3165:57;3249:2;3245;3241:11;3231:21;;3271:6;3261:16;;;;;2623:660;;;;;:::o;3288:180::-;3347:6;3400:2;3388:9;3379:7;3375:23;3371:32;3368:52;;;3416:1;3413;3406:12;3368:52;-1:-1:-1;3439:23:6;;3288:180;-1:-1:-1;3288:180:6:o;3473:186::-;3532:6;3585:2;3573:9;3564:7;3560:23;3556:32;3553:52;;;3601:1;3598;3591:12;3553:52;3624:29;3643:9;3624:29;:::i;:::-;3614:39;3473:186;-1:-1:-1;;;3473:186:6:o;3872:260::-;3940:6;3948;4001:2;3989:9;3980:7;3976:23;3972:32;3969:52;;;4017:1;4014;4007:12;3969:52;4040:29;4059:9;4040:29;:::i;:::-;4030:39;;4088:38;4122:2;4111:9;4107:18;4088:38;:::i;:::-;4078:48;;3872:260;;;;;:::o;4137:380::-;4216:1;4212:12;;;;4259;;;4280:61;;4334:4;4326:6;4322:17;4312:27;;4280:61;4387:2;4379:6;4376:14;4356:18;4353:38;4350:161;;4433:10;4428:3;4424:20;4421:1;4414:31;4468:4;4465:1;4458:15;4496:4;4493:1;4486:15;4350:161;;4137:380;;;:::o;4931:127::-;4992:10;4987:3;4983:20;4980:1;4973:31;5023:4;5020:1;5013:15;5047:4;5044:1;5037:15;5063:127;5124:10;5119:3;5115:20;5112:1;5105:31;5155:4;5152:1;5145:15;5179:4;5176:1;5169:15;5195:135;5234:3;5255:17;;;5252:43;;5275:18;;:::i;:::-;-1:-1:-1;5322:1:6;5311:13;;5195:135::o;5335:125::-;5400:9;;;5421:10;;;5418:36;;;5434:18;;:::i;5465:356::-;5667:2;5649:21;;;5686:18;;;5679:30;5745:34;5740:2;5725:18;;5718:62;5812:2;5797:18;;5465:356::o;5826:563::-;6041:2;6030:9;6023:21;6080:6;6075:2;6064:9;6060:18;6053:34;6138:6;6130;6124:3;6113:9;6109:19;6096:49;6195:1;6189:3;6165:22;;;6161:32;;6154:43;;;;6300:4;6285:20;;6278:36;;;;-1:-1:-1;;;;;6350:32:6;;;;6345:2;6330:18;;6323:60;6258:2;6237:15;;;-1:-1:-1;;6233:29:6;6218:45;;;6214:55;;;-1:-1:-1;5826:563:6:o;6394:287::-;6523:3;6561:6;6555:13;6577:66;6636:6;6631:3;6624:4;6616:6;6612:17;6577:66;:::i;:::-;6659:16;;;;;6394:287;-1:-1:-1;;6394:287:6:o;10690:128::-;10757:9;;;10778:11;;;10775:37;;;10792:18;;:::i", + "sourcePath": "/home/andre_9a/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol", + "compiler": { + "name": "solc", + "version": "0.8.16+commit.07a7930e" + }, + "ast": { + "absolutePath": "/home/andre_9a/cactus/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol", + "exportedSymbols": { + "CBDCcontract": [ + 162 + ], + "Context": [ + 938 + ], + "ERC20": [ + 813 + ], + "IERC20": [ + 891 + ], + "IERC20Metadata": [ + 916 + ], + "Ownable": [ + 267 + ] + }, + "id": 163, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": [ + { + "id": 1, + "literals": [ + "solidity", + "^", + "0.8", + ".15" + ], + "nodeType": "PragmaDirective", + "src": "37:24:0" + }, + { + "absolutePath": "/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/access/Ownable.sol", + "file": "/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/access/Ownable.sol", + "id": 2, + "nameLocation": "-1:-1:-1", + "nodeType": "ImportDirective", + "scope": 163, + "sourceUnit": 268, + "src": "63:87:0", + "symbolAliases": [], + "unitAlias": "" + }, + { + "absolutePath": "/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol", + "file": "/home/andre_9a/cactus/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol", + "id": 3, + "nameLocation": "-1:-1:-1", + "nodeType": "ImportDirective", + "scope": 163, + "sourceUnit": 814, + "src": "151:90:0", + "symbolAliases": [], + "unitAlias": "" + }, + { + "abstract": false, + "baseContracts": [ + { + "baseName": { + "id": 5, + "name": "Ownable", + "nameLocations": [ + "344:7:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 267, + "src": "344:7:0" + }, + "id": 6, + "nodeType": "InheritanceSpecifier", + "src": "344:7:0" + }, + { + "baseName": { + "id": 7, + "name": "ERC20", + "nameLocations": [ + "353:5:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 813, + "src": "353:5:0" + }, + "id": 8, + "nodeType": "InheritanceSpecifier", + "src": "353:5:0" + } + ], + "canonicalName": "CBDCcontract", + "contractDependencies": [], + "contractKind": "contract", + "documentation": { + "id": 4, + "nodeType": "StructuredDocumentation", + "src": "243:74:0", + "text": " @title SampleERC20\n @dev Create a sample ERC20 standard token" + }, + "fullyImplemented": true, + "id": 162, + "linearizedBaseContracts": [ + 162, + 813, + 916, + 891, + 267, + 938 + ], + "name": "CBDCcontract", + "nameLocation": "328:12:0", + "nodeType": "ContractDefinition", + "nodes": [ + { + "constant": false, + "id": 14, + "mutability": "mutable", + "name": "bridge_address", + "nameLocation": "374:14:0", + "nodeType": "VariableDeclaration", + "scope": 162, + "src": "366:76:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 9, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "366:7:0", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "value": { + "arguments": [ + { + "hexValue": "307866323864353736393137316266624432423336323864373232653538313239613661453135303232", + "id": 12, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "399:42:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "value": "0xf28d5769171bfbD2B3628d722e58129a6aE15022" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "id": 11, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "391:7:0", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 10, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "391:7:0", + "typeDescriptions": {} + } + }, + "id": 13, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "391:51:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 20, + "mutability": "mutable", + "name": "asset_ref_contract", + "nameLocation": "456:18:0", + "nodeType": "VariableDeclaration", + "scope": 162, + "src": "448:39:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 15, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "448:7:0", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "value": { + "arguments": [ + { + "hexValue": "30", + "id": 18, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "485:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + } + ], + "id": 17, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "477:7:0", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 16, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "477:7:0", + "typeDescriptions": {} + } + }, + "id": 19, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "477:10:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "body": { + "id": 27, + "nodeType": "Block", + "src": "552:7:0", + "statements": [] + }, + "id": 28, + "implemented": true, + "kind": "constructor", + "modifiers": [ + { + "arguments": [ + { + "hexValue": "43656e7472616c42616e6b4469676974616c43757272656e6379", + "id": 23, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "514:28:0", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_1e522a826804464981a3e2267dfbb8df03bd96487a0a7d8d35033a23e211146a", + "typeString": "literal_string \"CentralBankDigitalCurrency\"" + }, + "value": "CentralBankDigitalCurrency" + }, + { + "hexValue": "43424443", + "id": 24, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "544:6:0", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_c71aa5a70a4f17bfd28bb6a719471c9c3eaa418d8bb831941073ae5e16c15076", + "typeString": "literal_string \"CBDC\"" + }, + "value": "CBDC" + } + ], + "id": 25, + "kind": "baseConstructorSpecifier", + "modifierName": { + "id": 22, + "name": "ERC20", + "nameLocations": [ + "508:5:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 813, + "src": "508:5:0" + }, + "nodeType": "ModifierInvocation", + "src": "508:43:0" + } + ], + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 21, + "nodeType": "ParameterList", + "parameters": [], + "src": "505:2:0" + }, + "returnParameters": { + "id": 26, + "nodeType": "ParameterList", + "parameters": [], + "src": "552:0:0" + }, + "scope": 162, + "src": "494:65:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 39, + "nodeType": "Block", + "src": "645:54:0", + "statements": [ + { + "expression": { + "id": 37, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 35, + "name": "asset_ref_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 20, + "src": "655:18:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "id": 36, + "name": "contract_address", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 30, + "src": "676:16:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "655:37:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 38, + "nodeType": "ExpressionStatement", + "src": "655:37:0" + } + ] + }, + "functionSelector": "8d1a450d", + "id": 40, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 33, + "kind": "modifierInvocation", + "modifierName": { + "id": 32, + "name": "onlyOwner", + "nameLocations": [ + "635:9:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 209, + "src": "635:9:0" + }, + "nodeType": "ModifierInvocation", + "src": "635:9:0" + } + ], + "name": "setAssetReferenceContract", + "nameLocation": "574:25:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 31, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 30, + "mutability": "mutable", + "name": "contract_address", + "nameLocation": "608:16:0", + "nodeType": "VariableDeclaration", + "scope": 40, + "src": "600:24:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 29, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "600:7:0", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + } + ], + "src": "599:26:0" + }, + "returnParameters": { + "id": 34, + "nodeType": "ParameterList", + "parameters": [], + "src": "645:0:0" + }, + "scope": 162, + "src": "565:134:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + }, + { + "body": { + "id": 54, + "nodeType": "Block", + "src": "771:39:0", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 50, + "name": "account", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 42, + "src": "787:7:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 51, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 44, + "src": "796:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 49, + "name": "_mint", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 673, + "src": "781:5:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 52, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "781:22:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 53, + "nodeType": "ExpressionStatement", + "src": "781:22:0" + } + ] + }, + "functionSelector": "40c10f19", + "id": 55, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 47, + "kind": "modifierInvocation", + "modifierName": { + "id": 46, + "name": "onlyOwner", + "nameLocations": [ + "761:9:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 209, + "src": "761:9:0" + }, + "nodeType": "ModifierInvocation", + "src": "761:9:0" + } + ], + "name": "mint", + "nameLocation": "714:4:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 45, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 42, + "mutability": "mutable", + "name": "account", + "nameLocation": "727:7:0", + "nodeType": "VariableDeclaration", + "scope": 55, + "src": "719:15:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 41, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "719:7:0", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 44, + "mutability": "mutable", + "name": "amount", + "nameLocation": "744:6:0", + "nodeType": "VariableDeclaration", + "scope": 55, + "src": "736:14:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 43, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "736:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "718:33:0" + }, + "returnParameters": { + "id": 48, + "nodeType": "ParameterList", + "parameters": [], + "src": "771:0:0" + }, + "scope": 162, + "src": "705:105:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + }, + { + "body": { + "id": 67, + "nodeType": "Block", + "src": "865:46:0", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 63, + "name": "bridge_address", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 14, + "src": "881:14:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 64, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 57, + "src": "897:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 62, + "name": "_burn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 745, + "src": "875:5:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 65, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "875:29:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 66, + "nodeType": "ExpressionStatement", + "src": "875:29:0" + } + ] + }, + "functionSelector": "42966c68", + "id": 68, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 60, + "kind": "modifierInvocation", + "modifierName": { + "id": 59, + "name": "onlyOwner", + "nameLocations": [ + "855:9:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 209, + "src": "855:9:0" + }, + "nodeType": "ModifierInvocation", + "src": "855:9:0" + } + ], + "name": "burn", + "nameLocation": "825:4:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 58, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 57, + "mutability": "mutable", + "name": "amount", + "nameLocation": "838:6:0", + "nodeType": "VariableDeclaration", + "scope": 68, + "src": "830:14:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 56, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "830:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "829:16:0" + }, + "returnParameters": { + "id": 61, + "nodeType": "ParameterList", + "parameters": [], + "src": "865:0:0" + }, + "scope": 162, + "src": "816:95:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + }, + { + "body": { + "id": 101, + "nodeType": "Block", + "src": "1004:294:0", + "statements": [ + { + "expression": { + "arguments": [ + { + "id": 78, + "name": "bridge_address", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 14, + "src": "1023:14:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 79, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 70, + "src": "1039:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 77, + "name": "transfer", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 387, + "src": "1014:8:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$_t_bool_$", + "typeString": "function (address,uint256) returns (bool)" + } + }, + "id": 80, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1014:32:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 81, + "nodeType": "ExpressionStatement", + "src": "1014:32:0" + }, + { + "assignments": [ + 83, + null + ], + "declarations": [ + { + "constant": false, + "id": 83, + "mutability": "mutable", + "name": "success", + "nameLocation": "1063:7:0", + "nodeType": "VariableDeclaration", + "scope": 101, + "src": "1058:12:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 82, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "1058:4:0", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + }, + null + ], + "id": 95, + "initialValue": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "63726561746541737365745265666572656e636528737472696e672c75696e743235362c6164647265737329", + "id": 88, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1137:46:0", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_663bcc920598c723df9b3a760a5a7d3abac3eae3f6190deac4bbb6aba0477d08", + "typeString": "literal_string \"createAssetReference(string,uint256,address)\"" + }, + "value": "createAssetReference(string,uint256,address)" + }, + { + "id": 89, + "name": "asset_ref_id", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 72, + "src": "1185:12:0", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + } + }, + { + "id": 90, + "name": "amount", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 70, + "src": "1199:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + { + "expression": { + "id": 91, + "name": "msg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967281, + "src": "1207:3:0", + "typeDescriptions": { + "typeIdentifier": "t_magic_message", + "typeString": "msg" + } + }, + "id": 92, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1211:6:0", + "memberName": "sender", + "nodeType": "MemberAccess", + "src": "1207:10:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_663bcc920598c723df9b3a760a5a7d3abac3eae3f6190deac4bbb6aba0477d08", + "typeString": "literal_string \"createAssetReference(string,uint256,address)\"" + }, + { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string calldata" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "expression": { + "id": 86, + "name": "abi", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 4294967295, + "src": "1113:3:0", + "typeDescriptions": { + "typeIdentifier": "t_magic_abi", + "typeString": "abi" + } + }, + "id": 87, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "1117:19:0", + "memberName": "encodeWithSignature", + "nodeType": "MemberAccess", + "src": "1113:23:0", + "typeDescriptions": { + "typeIdentifier": "t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$", + "typeString": "function (string memory) pure returns (bytes memory)" + } + }, + "id": 93, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1113:105:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bytes_memory_ptr", + "typeString": "bytes memory" + } + ], + "expression": { + "id": 84, + "name": "asset_ref_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 20, + "src": "1076:18:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 85, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1095:4:0", + "memberName": "call", + "nodeType": "MemberAccess", + "src": "1076:23:0", + "typeDescriptions": { + "typeIdentifier": "t_function_barecall_payable$_t_bytes_memory_ptr_$returns$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "function (bytes memory) payable returns (bool,bytes memory)" + } + }, + "id": 94, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1076:152:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$_t_bool_$_t_bytes_memory_ptr_$", + "typeString": "tuple(bool,bytes memory)" + } + }, + "nodeType": "VariableDeclarationStatement", + "src": "1057:171:0" + }, + { + "expression": { + "arguments": [ + { + "id": 97, + "name": "success", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 83, + "src": "1247:7:0", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "63726561746541737365745265666572656e63652063616c6c206661696c6564", + "id": 98, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1256:34:0", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_93169b227269284795eb472a6a53f2c082e0e5a5494d86528976ebe95cb05d72", + "typeString": "literal_string \"createAssetReference call failed\"" + }, + "value": "createAssetReference call failed" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_93169b227269284795eb472a6a53f2c082e0e5a5494d86528976ebe95cb05d72", + "typeString": "literal_string \"createAssetReference call failed\"" + } + ], + "id": 96, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1239:7:0", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 99, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1239:52:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 100, + "nodeType": "ExpressionStatement", + "src": "1239:52:0" + } + ] + }, + "functionSelector": "417d11af", + "id": 102, + "implemented": true, + "kind": "function", + "modifiers": [ + { + "id": 75, + "kind": "modifierInvocation", + "modifierName": { + "id": 74, + "name": "checkARContract", + "nameLocations": [ + "988:15:0" + ], + "nodeType": "IdentifierPath", + "referencedDeclaration": 123, + "src": "988:15:0" + }, + "nodeType": "ModifierInvocation", + "src": "988:15:0" + } + ], + "name": "escrow", + "nameLocation": "926:6:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 73, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 70, + "mutability": "mutable", + "name": "amount", + "nameLocation": "941:6:0", + "nodeType": "VariableDeclaration", + "scope": 102, + "src": "933:14:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 69, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "933:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 72, + "mutability": "mutable", + "name": "asset_ref_id", + "nameLocation": "965:12:0", + "nodeType": "VariableDeclaration", + "scope": 102, + "src": "949:28:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_string_calldata_ptr", + "typeString": "string" + }, + "typeName": { + "id": 71, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "949:6:0", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "932:46:0" + }, + "returnParameters": { + "id": 76, + "nodeType": "ParameterList", + "parameters": [], + "src": "1004:0:0" + }, + "scope": 162, + "src": "917:381:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + }, + { + "body": { + "id": 115, + "nodeType": "Block", + "src": "1360:112:0", + "statements": [ + { + "expression": { + "arguments": [ + { + "commonType": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "id": 111, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 106, + "name": "asset_ref_contract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 20, + "src": "1378:18:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "nodeType": "BinaryOperation", + "operator": "!=", + "rightExpression": { + "arguments": [ + { + "hexValue": "30", + "id": 109, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1408:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + } + ], + "id": 108, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "1400:7:0", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 107, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "1400:7:0", + "typeDescriptions": {} + } + }, + "id": 110, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1400:10:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "src": "1378:32:0", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + { + "hexValue": "43424443636f6e74726163743a206173736574207265666572656e636520636f6e7472616374206e6f7420646566696e6564", + "id": 112, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1412:52:0", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_81032450132c43b9f8878276153b2ea85a9c962c1591b18b864a02232e1beff8", + "typeString": "literal_string \"CBDCcontract: asset reference contract not defined\"" + }, + "value": "CBDCcontract: asset reference contract not defined" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + { + "typeIdentifier": "t_stringliteral_81032450132c43b9f8878276153b2ea85a9c962c1591b18b864a02232e1beff8", + "typeString": "literal_string \"CBDCcontract: asset reference contract not defined\"" + } + ], + "id": 105, + "name": "require", + "nodeType": "Identifier", + "overloadedDeclarations": [ + 4294967278, + 4294967278 + ], + "referencedDeclaration": 4294967278, + "src": "1370:7:0", + "typeDescriptions": { + "typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$", + "typeString": "function (bool,string memory) pure" + } + }, + "id": 113, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1370:95:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 114, + "nodeType": "ExpressionStatement", + "src": "1370:95:0" + } + ] + }, + "id": 116, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "_checkAssetRefContract", + "nameLocation": "1313:22:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 103, + "nodeType": "ParameterList", + "parameters": [], + "src": "1335:2:0" + }, + "returnParameters": { + "id": 104, + "nodeType": "ParameterList", + "parameters": [], + "src": "1360:0:0" + }, + "scope": 162, + "src": "1304:168:0", + "stateMutability": "view", + "virtual": true, + "visibility": "internal" + }, + { + "body": { + "id": 122, + "nodeType": "Block", + "src": "1505:52:0", + "statements": [ + { + "expression": { + "arguments": [], + "expression": { + "argumentTypes": [], + "id": 118, + "name": "_checkAssetRefContract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 116, + "src": "1515:22:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$__$returns$__$", + "typeString": "function () view" + } + }, + "id": 119, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1515:24:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 120, + "nodeType": "ExpressionStatement", + "src": "1515:24:0" + }, + { + "id": 121, + "nodeType": "PlaceholderStatement", + "src": "1549:1:0" + } + ] + }, + "id": 123, + "name": "checkARContract", + "nameLocation": "1487:15:0", + "nodeType": "ModifierDefinition", + "parameters": { + "id": 117, + "nodeType": "ParameterList", + "parameters": [], + "src": "1502:2:0" + }, + "src": "1478:79:0", + "virtual": false, + "visibility": "internal" + }, + { + "body": { + "id": 160, + "nodeType": "Block", + "src": "1662:179:0", + "statements": [ + { + "assignments": [ + 130 + ], + "declarations": [ + { + "constant": false, + "id": 130, + "mutability": "mutable", + "name": "toBurn", + "nameLocation": "1680:6:0", + "nodeType": "VariableDeclaration", + "scope": 160, + "src": "1672:14:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 129, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "1672:7:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 131, + "nodeType": "VariableDeclarationStatement", + "src": "1672:14:0" + }, + { + "body": { + "id": 158, + "nodeType": "Block", + "src": "1739:96:0", + "statements": [ + { + "expression": { + "id": 149, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": { + "id": 143, + "name": "toBurn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 130, + "src": "1753:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": { + "arguments": [ + { + "baseExpression": { + "id": 145, + "name": "accounts", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 126, + "src": "1772:8:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", + "typeString": "address[] calldata" + } + }, + "id": 147, + "indexExpression": { + "id": 146, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 133, + "src": "1781:1:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1772:11:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + } + ], + "id": 144, + "name": "balanceOf", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 366, + "src": "1762:9:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_view$_t_address_$returns$_t_uint256_$", + "typeString": "function (address) view returns (uint256)" + } + }, + "id": 148, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1762:22:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "1753:31:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 150, + "nodeType": "ExpressionStatement", + "src": "1753:31:0" + }, + { + "expression": { + "arguments": [ + { + "baseExpression": { + "id": 152, + "name": "accounts", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 126, + "src": "1804:8:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", + "typeString": "address[] calldata" + } + }, + "id": 154, + "indexExpression": { + "id": 153, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 133, + "src": "1813:1:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "IndexAccess", + "src": "1804:11:0", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "id": 155, + "name": "toBurn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 130, + "src": "1817:6:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + ], + "id": 151, + "name": "_burn", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 745, + "src": "1798:5:0", + "typeDescriptions": { + "typeIdentifier": "t_function_internal_nonpayable$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,uint256)" + } + }, + "id": 156, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "1798:26:0", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 157, + "nodeType": "ExpressionStatement", + "src": "1798:26:0" + } + ] + }, + "condition": { + "commonType": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "id": 139, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftExpression": { + "id": 136, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 133, + "src": "1713:1:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "BinaryOperation", + "operator": "<", + "rightExpression": { + "expression": { + "id": 137, + "name": "accounts", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 126, + "src": "1717:8:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", + "typeString": "address[] calldata" + } + }, + "id": 138, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "1726:6:0", + "memberName": "length", + "nodeType": "MemberAccess", + "src": "1717:15:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "src": "1713:19:0", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "id": 159, + "initializationExpression": { + "assignments": [ + 133 + ], + "declarations": [ + { + "constant": false, + "id": 133, + "mutability": "mutable", + "name": "i", + "nameLocation": "1706:1:0", + "nodeType": "VariableDeclaration", + "scope": 159, + "src": "1701:6:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 132, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1701:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "id": 135, + "initialValue": { + "hexValue": "30", + "id": 134, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1710:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "nodeType": "VariableDeclarationStatement", + "src": "1701:10:0" + }, + "loopExpression": { + "expression": { + "id": 141, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "1734:3:0", + "subExpression": { + "id": 140, + "name": "i", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 133, + "src": "1734:1:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 142, + "nodeType": "ExpressionStatement", + "src": "1734:3:0" + }, + "nodeType": "ForStatement", + "src": "1696:139:0" + } + ] + }, + "functionSelector": "3459feb2", + "id": 161, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "resetBalanceOf", + "nameLocation": "1609:14:0", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 127, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 126, + "mutability": "mutable", + "name": "accounts", + "nameLocation": "1643:8:0", + "nodeType": "VariableDeclaration", + "scope": 161, + "src": "1624:27:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", + "typeString": "address[]" + }, + "typeName": { + "baseType": { + "id": 124, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "1624:7:0", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "id": 125, + "nodeType": "ArrayTypeName", + "src": "1624:9:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", + "typeString": "address[]" + } + }, + "visibility": "internal" + } + ], + "src": "1623:29:0" + }, + "returnParameters": { + "id": 128, + "nodeType": "ParameterList", + "parameters": [], + "src": "1662:0:0" + }, + "scope": 162, + "src": "1600:241:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + } + ], + "scope": 163, + "src": "319:1524:0", + "usedErrors": [] + } + ], + "src": "37:1807:0" + }, + "functionHashes": { + "allowance(address,address)": "dd62ed3e", + "approve(address,uint256)": "095ea7b3", + "balanceOf(address)": "70a08231", + "burn(uint256)": "42966c68", + "decimals()": "313ce567", + "decreaseAllowance(address,uint256)": "a457c2d7", + "escrow(uint256,string)": "417d11af", + "increaseAllowance(address,uint256)": "39509351", + "mint(address,uint256)": "40c10f19", + "name()": "06fdde03", + "owner()": "8da5cb5b", + "renounceOwnership()": "715018a6", + "resetBalanceOf(address[])": "3459feb2", + "setAssetReferenceContract(address)": "8d1a450d", + "symbol()": "95d89b41", + "totalSupply()": "18160ddd", + "transfer(address,uint256)": "a9059cbb", + "transferFrom(address,address,uint256)": "23b872dd", + "transferOwnership(address)": "f2fde38b" + }, + "gasEstimates": { + "creation": { + "codeDepositCost": "910600", + "executionCost": "infinite", + "totalCost": "infinite" + }, + "external": { + "allowance(address,address)": "infinite", + "approve(address,uint256)": "24644", + "balanceOf(address)": "2582", + "burn(uint256)": "55148", + "decimals()": "200", + "decreaseAllowance(address,uint256)": "26933", + "escrow(uint256,string)": "infinite", + "increaseAllowance(address,uint256)": "infinite", + "mint(address,uint256)": "infinite", + "name()": "infinite", + "owner()": "2412", + "renounceOwnership()": "28181", + "resetBalanceOf(address[])": "infinite", + "setAssetReferenceContract(address)": "26800", + "symbol()": "infinite", + "totalSupply()": "2349", + "transfer(address,uint256)": "infinite", + "transferFrom(address,address,uint256)": "infinite", + "transferOwnership(address)": "28419" + }, + "internal": { + "_checkAssetRefContract()": "infinite" + } + } +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-contract-test.sol b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-contract-test.sol new file mode 100644 index 0000000000..61b1d6e250 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-contract-test.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; +import "../contracts/CBDCcontract.sol"; +import "../contracts/AssetReferenceContract.sol"; + +contract CBDCcontractTest { + + address bridge_address = address(0xf28d5769171bfbD2B3628d722e58129a6aE15022); + AssetReferenceContract assetRefContract; + CBDCcontract cbdcContract; + + function beforeEach () public { + cbdcContract = new CBDCcontract(); + assetRefContract = new AssetReferenceContract(address(cbdcContract)); + + cbdcContract.setAssetReferenceContract(address(assetRefContract)); + cbdcContract.setAssetReferenceContract(address(assetRefContract)); + assetRefContract.addOwner(address(cbdcContract)); + } + + function testTokenNameAndSymbol () public { + Assert.equal(cbdcContract.name(), "CentralBankDigitalCurrency", "token name did not match"); + Assert.equal(cbdcContract.symbol(), "CBDC", "token symbol did not match"); + } + + function mintEscrowAndBurnTokens () public { + cbdcContract.mint(address(this), 222); + uint256 balance = cbdcContract.balanceOf(address(this)); + Assert.equal(balance, 222, "tokens minted did not match"); + } + + function escrowTokens() public { + cbdcContract.mint(address(this), 222); + cbdcContract.escrow(222, "id1"); + + Assert.equal(assetRefContract.isPresent("id1"), true, "asset reference should be present"); + Assert.equal(cbdcContract.balanceOf(bridge_address), 222, "bridge balance not updated"); + } + + function burnTokens() public { + cbdcContract.mint(address(this), 222); + cbdcContract.escrow(222, "id1"); + cbdcContract.burn(222); + + Assert.equal(cbdcContract.balanceOf(bridge_address), 0, "bridge balance not updated"); + } +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol new file mode 100644 index 0000000000..99d5d71154 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/solidity/cbdc-erc-20/cbdc-erc-20.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.15; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/** + * @title SampleERC20 + * @dev Create a sample ERC20 standard token + */ + +contract CBDCcontract is Ownable, ERC20 { + + address bridge_address = address(0xf28d5769171bfbD2B3628d722e58129a6aE15022); + address asset_ref_contract = address(0); + + constructor() ERC20("CentralBankDigitalCurrency", "CBDC") { + } + + function setAssetReferenceContract(address contract_address) external onlyOwner { + asset_ref_contract = contract_address; + } + + function mint(address account, uint256 amount) external onlyOwner { + _mint(account, amount); + } + + function burn(uint256 amount) external onlyOwner { + _burn(bridge_address, amount); + } + + function escrow(uint256 amount, string calldata asset_ref_id) external checkARContract { + transfer(bridge_address, amount); + + (bool success, ) = asset_ref_contract.call( + abi.encodeWithSignature("createAssetReference(string,uint256,address)", asset_ref_id, amount, msg.sender) + ); + + require(success, "createAssetReference call failed"); + } + + function _checkAssetRefContract() internal view virtual { + require(asset_ref_contract != address(0), "CBDCcontract: asset reference contract not defined"); + } + + modifier checkARContract() { + _checkAssetRefContract(); + _; + } + + // function for testing purposes + function resetBalanceOf(address[] calldata accounts) external { + uint256 toBurn; + for (uint i = 0; i < accounts.length; i++) { + toBurn = balanceOf(accounts[i]); + _burn(accounts[i], toBurn); + } + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/besu-helper.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/besu-helper.ts new file mode 100644 index 0000000000..dfc71ded39 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/besu-helper.ts @@ -0,0 +1,116 @@ +import axios from "axios"; +import CryptoMaterial from "../../../crypto-material/crypto-material.json"; +import AssetReferenceContractJson from "../../../solidity/asset-reference-contract/AssetReferenceContract.json"; +import CBDCcontractJson from "../../../solidity/cbdc-erc-20/CBDCcontract.json"; + +const BESU_CONTRACT_CBDC_ERC20_NAME = CBDCcontractJson.contractName; +const BESU_CONTRACT_ASSET_REF_NAME = AssetReferenceContractJson.contractName; + +export async function lockBesuAssetReference( + userAddress: string, + prkAddress: string, + assetRefID: string, +): Promise { + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "SEND", + methodName: "lockAssetReference", + gas: 1000000, + params: [assetRefID], + signingCredential: { + ethAccount: userAddress, + secret: prkAddress, + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); +} + +export async function getBesuBalance(address: string): Promise { + const response = await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_CBDC_ERC20_NAME, + invocationType: "CALL", + methodName: "balanceOf", + gas: 1000000, + params: [address], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + + return parseInt(response.data.callOutput); +} + +export async function isBesuAssetReference( + assetRefID: string, +): Promise { + const response = await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "CALL", + methodName: "isPresent", + gas: 1000000, + params: [assetRefID], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + + return response.data.callOutput; +} + +export async function resetBesu(): Promise { + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_CBDC_ERC20_NAME, + invocationType: "SEND", + methodName: "resetBalanceOf", + gas: 1000000, + params: [ + [ + CryptoMaterial.accounts["userA"].ethAddress, + CryptoMaterial.accounts["userB"].ethAddress, + CryptoMaterial.accounts["bridge"].ethAddress, + ], + ], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "SEND", + methodName: "resetAssetRefsList", + gas: 1000000, + params: [], + signingCredential: { + ethAccount: CryptoMaterial.accounts["bridge"].ethAddress, + secret: CryptoMaterial.accounts["bridge"].privateKey, + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/fabric-helper.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/fabric-helper.ts new file mode 100644 index 0000000000..64b768f5d1 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/fabric-helper.ts @@ -0,0 +1,161 @@ +import axios from "axios"; +import { getUserFromPseudonim } from "./steps/common"; +import CryptoMaterial from "../../../crypto-material/crypto-material.json"; + +const FABRIC_CHANNEL_NAME = "mychannel"; +const FABRIC_CONTRACT_CBDC_ERC20_NAME = "cbdc"; +const FABRIC_CONTRACT_ASSET_REF_NAME = "asset-reference-contract"; + +export async function getFabricBalance(identity: string): Promise { + const response = await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [identity], + methodName: "BalanceOf", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }, + ); + + return parseInt(response.data.functionOutput); +} + +export async function readFabricAssetReference( + assetRefID: string, +): Promise { + const response = await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [assetRefID], + methodName: "ReadAssetReference", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }, + ); + + return JSON.parse(response.data.functionOutput); +} + +export async function fabricAssetReferenceExists( + assetRefID: string, +): Promise { + const response = await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [assetRefID], + methodName: "AssetReferenceExists", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }, + ); + + return response.data.functionOutput; +} + +export async function lockFabricAssetReference( + user: string, + assetRefID: string, +): Promise { + return axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [assetRefID], + methodName: "LockAssetReference", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim(user), + }, + }, + ); +} + +export async function deleteFabricAssetReference( + user: string, + assetRefID: string, +): Promise { + return axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [assetRefID], + methodName: "DeleteAssetReference", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim(user), + }, + }, + ); +} + +export async function refundFabricTokens( + finalUserFabricID: string, + amount: number, + finalUserEthAddress: string, +): Promise { + return axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [amount.toString(), finalUserFabricID, finalUserEthAddress], + methodName: "Refund", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "bridge", + }, + }, + ); +} + +export async function resetFabric(): Promise { + await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [], + methodName: "ResetState", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }, + ); + + await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [], + methodName: "ResetState", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: "userA", + }, + }, + ); +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/besu-gateway.feature b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/besu-gateway.feature new file mode 100644 index 0000000000..28d48164cf --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/besu-gateway.feature @@ -0,0 +1,40 @@ +@besu +Feature: Hyperledger Besu gateway is working properly + + Scenario: Alice successfully escrows CBDC + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then "alice" has 0 CBDC available in the sidechain + Then "bob" has 500 CBDC available in the sidechain + + Scenario: Alice successfully creates an asset reference in the Besu network + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then the asset reference smart contract has an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" + + Scenario: Alice successfully locks an asset reference in the Besu network + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob locks the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" is locked in the sidechain + + Scenario: Alice successfully locks an asset reference in the Besu network + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob locks the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then "charlie" fails to lock the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + + Scenario: Alice successfully deletes an asset reference in the Besu network + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob locks the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob deletes the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then the asset reference smart contract has no asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" + + Scenario: BridgeEntity deletes an asset reference and burns tokens in the Besu network + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob locks the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + When bob deletes the asset reference with id "889242f8-58ae-449e-b938-fa28fdca65b6" in the sidechain + Then "alice" has 0 CBDC available in the sidechain + Then "bob" has 0 CBDC available in the sidechain diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-back.feature b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-back.feature new file mode 100644 index 0000000000..1ee3af40a6 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-back.feature @@ -0,0 +1,87 @@ +Feature: Client successfully bridges back CBDC + + @bridgeBack + @fabric + @besu + Scenario: Client successfully initiates bridge back of CBDC to own address in the source chain + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "alice" initiates bridge back of 500 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "alice" address in the source chain + + @bridgeBack + @fabric + @besu + Scenario: Client initiates bridge back of CBDC and accounts have consistent balances in both chains + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "alice" initiates bridge back of 500 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "alice" address in the source chain + Then "alice" has 0 CBDC available in the sidechain + Then "bob" has 0 CBDC available in the sidechain + Then "alice" has 500 CBDC available in the source chain + Then "bob" has 0 CBDC available in the source chain + + @bridgeBack + @fabric + @besu + Scenario: Client successfully initiates bridge back half of the escrowed CBDC to own address in the source chain + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 250 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "alice" initiates bridge back of 250 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "alice" address in the source chain + Then "alice" has 250 CBDC available in the sidechain + Then "bob" has 0 CBDC available in the sidechain + Then "alice" has 250 CBDC available in the source chain + Then "bob" has 250 CBDC available in the source chain + + @bridgeBack + @fabric + @besu + Scenario: Client fails to initiate bridge back of double the escrowed CBDC to own address in the source chain + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "alice" fails to initiate bridge back of 10000 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "alice" address in the source chain + + @bridgeBack + @fabric + @besu + Scenario: Client fails to initiate bridge back of CBDC to another user address in the source chain but transfer + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "alice" fails to initiate bridge back of 500 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "charlie" address in the source chain + Then "alice" has 0 CBDC available in the source chain + Then "alice" has 0 CBDC available in the sidechain + Then "bob" has 500 CBDC available in the source chain + Then "bob" has 500 CBDC available in the sidechain + Then "charlie" has 0 CBDC available in the source chain + Then "charlie" has 0 CBDC available in the sidechain + + @bridgeBack + @fabric + @besu + Scenario: Impersonator fails to initiate bridge back of CBDC escrowed by another user address but transfer + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Given "alice" with 500 CBDC available in the sidechain smart contract + When "alice" escrows 500 CBDC and creates an asset reference with id "d25fbcbb-0895-4905-b8d5-502d5e83b122" in the sidechain + Then "charlie" fails to initiate bridge back of 500 CBDC referenced by id "d25fbcbb-0895-4905-b8d5-502d5e83b122" to "alice" address in the source chain + Then "alice" has 0 CBDC available in the source chain + Then "alice" has 0 CBDC available in the sidechain + Then "bob" has 500 CBDC available in the source chain + Then "bob" has 500 CBDC available in the sidechain + Then "charlie" has 0 CBDC available in the source chain + Then "charlie" has 0 CBDC available in the sidechain diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-out.feature b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-out.feature new file mode 100644 index 0000000000..04106f15a3 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/bridge-out.feature @@ -0,0 +1,42 @@ +Feature: Client successfully bridges out CBDC + + @bridgeOut + @fabric + @besu + Scenario: Chaincode correctly tracks amount of bridged out CBDC when running bridging protocol + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + When "alice" initiates bridge out of 500 CBDC referenced by id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" to "alice" address in the sidechain + Then the bridged out amount in the chaincode is 500 CBDC + + @bridgeOut + @fabric + @besu + Scenario: Client successfully initiates bridge out of CBDC to own address in the sidechain + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + When "alice" initiates bridge out of 500 CBDC referenced by id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" to "alice" address in the sidechain + Then "alice" has 0 CBDC available in the source chain + Then "bob" has 500 CBDC available in the source chain + Then "alice" has 500 CBDC available in the sidechain + + @bridgeOut + @fabric + Scenario: Client initiates bridge out of CBDC to another user address and the operation fails + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Then "alice" tries to initiate bridge out of 500 CBDC referenced by id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" to "charlie" address in the sidechain and operation fails because "it is not possible to bridge CBDC to another user" + + @bridgeOut + @fabric + Scenario: Impersonator tries initiates bridge out of other user CBDC and the operation fails + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Then "charlie" tries to initiate bridge out of 500 CBDC referenced by id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" to "charlie" address in the sidechain and operation fails because "it is not possible to transfer tokens escrowed by another user" + + @bridgeOut + @fabric + Scenario: Client tries initiates bridge out of more CBDC than the amount escrowed and the operation fails + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Then "alice" tries to initiate bridge out of 1000 CBDC referenced by id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" to "alice" address in the sidechain and operation fails because "it is not possible to transfer a different amount of CBDC than the ones escrowed" diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/fabric-gateway.feature b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/fabric-gateway.feature new file mode 100644 index 0000000000..1d299be865 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/features/fabric-gateway.feature @@ -0,0 +1,57 @@ +@fabric +Feature: Hyperledger Fabric gateway is working properly + + Scenario: Alice successfully escrows CBDC in the source chain + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then "alice" has 0 CBDC available in the source chain + Then "bob" has 500 CBDC available in the source chain + + Scenario: Alice successfully creates an asset reference + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then the asset reference chaincode has an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" + + Scenario: Alice successfully locks an asset reference + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + When "bob" locks the asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then the asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" is locked in the source chain + + Scenario: Alice successfully locks an asset reference and Bob tries to lock the same + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + When "bob" locks the asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then "charlie" fails to lock the asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + + Scenario: Alice successfully escrows 500 CBDC and tries to transfer to Bob + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then "alice" fails to transfer 500 CBDC to "charlie" + + Scenario: Alice successfully deletes an asset reference + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + When "bob" locks and deletes an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + Then the asset reference chaincode has no asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" + + Scenario: BridgeEntity successfully refunds CBDC + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + When "bob" locks and deletes an asset reference with id "00ed12e4-044e-46ff-98ef-a4e25f519b57" in the source chain + When bob refunds 500 CBDC to "alice" in the source chain + Then "bob" has 0 CBDC available in the source chain + Then "alice" has 500 CBDC available in the source chain + + Scenario: Chaincode correctly tracks amount of bridged out CBDC (1) + Given "alice" with 500 CBDC available in the source chain + When "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + When "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + Then the bridged out amount in the chaincode is 500 CBDC + + Scenario: Chaincode correctly tracks amount of bridged out CBDC (2) + Given "alice" with 500 CBDC available in the source chain + Given "alice" escrows 500 CBDC and creates an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + When "bob" locks and deletes an asset reference with id "c5dfbd04-a71b-4848-92d1-78cd1fafaaf1" in the source chain + When bob refunds 500 CBDC to "alice" in the source chain + Then the bridged out amount in the chaincode is 0 CBDC \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/besu-gateway.steps.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/besu-gateway.steps.ts new file mode 100644 index 0000000000..8a8e04c864 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/besu-gateway.steps.ts @@ -0,0 +1,161 @@ +import { Given, When, Then, Before, After } from "cucumber"; +import { expect } from "chai"; +import axios from "axios"; +import CryptoMaterial from "../../../../crypto-material/crypto-material.json"; +import { + getBesuBalance, + isBesuAssetReference, + lockBesuAssetReference, + resetBesu, +} from "../besu-helper"; +import AssetReferenceContractJson from "../../../../solidity/asset-reference-contract/AssetReferenceContract.json"; +import CBDCcontractJson from "../../../../solidity/cbdc-erc-20/CBDCcontract.json"; +import { getEthAddress, getPrvKey } from "./common"; + +const BESU_CONTRACT_CBDC_ERC20_NAME = CBDCcontractJson.contractName; +const BESU_CONTRACT_ASSET_REF_NAME = AssetReferenceContractJson.contractName; + +Before({ timeout: 20 * 1000, tags: "@besu" }, async function () { + await resetBesu(); +}); + +After({ timeout: 20 * 1000, tags: "@besu" }, async function () { + await resetBesu(); +}); + +Given( + "{string} with {int} CBDC available in the sidechain smart contract", + async function (user: string, amount: number) { + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "SEND", + methodName: "mint", + gas: 1000000, + params: [getEthAddress(user), amount], + signingCredential: { + ethAccount: getEthAddress(user), + secret: getPrvKey("bob"), + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + }, +); + +When( + "{string} escrows {int} CBDC and creates an asset reference with id {string} in the sidechain", + async function (user: string, amount: number, assetRefID: string) { + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_CBDC_ERC20_NAME, + invocationType: "SEND", + methodName: "escrow", + gas: 1000000, + params: [amount, assetRefID], + signingCredential: { + ethAccount: getEthAddress(user), + secret: getPrvKey(user), + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + }, +); + +When( + "bob locks the asset reference with id {string} in the sidechain", + async function (assetRefID: string) { + await lockBesuAssetReference( + getEthAddress("bob"), + getPrvKey("bob"), + assetRefID, + ); + }, +); + +When( + "bob deletes the asset reference with id {string} in the sidechain", + async function (assetRefID: string) { + await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "SEND", + methodName: "deleteAssetReference", + gas: 1000000, + params: [assetRefID], + signingCredential: { + ethAccount: getEthAddress("bob"), + secret: getPrvKey("bob"), + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + }, +); + +Then( + "the asset reference smart contract has an asset reference with id {string}", + async function (assetRefID: string) { + expect(await isBesuAssetReference(assetRefID)).to.be.true; + }, +); + +Then( + "the asset reference smart contract has no asset reference with id {string}", + async function (assetRefID: string) { + expect(await isBesuAssetReference(assetRefID)).to.be.false; + }, +); + +Then("{string} has {int} CBDC available in the sidechain", async function ( + user: string, + amount: number, +) { + expect(await getBesuBalance(getEthAddress(user))).to.equal(amount); +}); + +Then( + "the asset reference with id {string} is locked in the sidechain", + async function (assetRefID: string) { + const response = await axios.post( + "http://localhost:4100/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/invoke-contract", + { + contractName: BESU_CONTRACT_ASSET_REF_NAME, + invocationType: "CALL", + methodName: "isAssetLocked", + gas: 1000000, + params: [assetRefID], + signingCredential: { + ethAccount: getEthAddress("alice"), + secret: getPrvKey("alice"), + type: "PRIVATE_KEY_HEX", + }, + keychainId: CryptoMaterial.keychains.keychain2.id, + }, + ); + + expect(response.data.callOutput).to.equal(true); + }, +); + +Then( + "{string} fails to lock the asset reference with id {string} in the sidechain", + async function (user: string, assetRefID: string) { + await lockBesuAssetReference( + getEthAddress(user), + getPrvKey(user), + assetRefID, + ).catch((err) => { + expect(err.response.data.error).to.contain( + `Transaction has been reverted by the EVM`, + ); + }); + }, +); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-back.steps.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-back.steps.ts new file mode 100644 index 0000000000..f2466348d0 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-back.steps.ts @@ -0,0 +1,132 @@ +import { Then } from "cucumber"; +import { expect } from "chai"; +import axios from "axios"; +import CryptoMaterial from "../../../../crypto-material/crypto-material.json"; +import { getFabricId, getEthAddress } from "./common"; + +const MAX_RETRIES = 5; +const MAX_TIMEOUT = 5000; + +Then( + "{string} initiates bridge back of {int} CBDC referenced by id {string} to {string} address in the source chain", + { timeout: 60 * 1000 }, + async function ( + user: string, + amount: number, + assetRefID: string, + finalUser: string, + ) { + const address = getEthAddress(user); + const fabricID = getFabricId(finalUser); + + const assetProfile = { + expirationDate: new Date(2060, 11, 24).toString(), + issuer: "CB1", + assetCode: "CBDC1", + // since there is no link with the asset information, + // we are just passing the asset parameters like this + // [amountBeingTransferred, fabricID, ethAddress] + keyInformationLink: [amount.toString(), fabricID, address], + }; + + const response = await axios.post( + "http://localhost:4100/api/v1/@hyperledger/cactus-plugin-odap-hermes/clientrequest", + { + clientGatewayConfiguration: { + apiHost: `http://localhost:4100`, + }, + serverGatewayConfiguration: { + apiHost: `http://localhost:4000`, + }, + version: "0.0.0", + loggingProfile: "dummyLoggingProfile", + accessControlProfile: "dummyAccessControlProfile", + applicationProfile: "dummyApplicationProfile", + payloadProfile: { + assetProfile, + capabilities: "", + }, + assetProfile: assetProfile, + assetControlProfile: "dummyAssetControlProfile", + beneficiaryPubkey: "dummyPubKey", + clientDltSystem: "DLT2", + originatorPubkey: "dummyPubKey", + recipientGatewayDltSystem: "DLT1", + recipientGatewayPubkey: CryptoMaterial.gateways["gateway1"].publicKey, + serverDltSystem: "DLT1", + sourceGatewayDltSystem: "DLT2", + clientIdentityPubkey: "", + serverIdentityPubkey: "", + maxRetries: MAX_RETRIES, + maxTimeout: MAX_TIMEOUT, + sourceLedgerAssetID: assetRefID, + recipientLedgerAssetID: "FABRIC_ASSET_ID", + }, + ); + + expect(response.status).to.equal(200); + }, +); + +Then( + "{string} fails to initiate bridge back of {int} CBDC referenced by id {string} to {string} address in the source chain", + { timeout: 60 * 1000 }, + async function ( + user: string, + amount: number, + assetRefID: string, + finalUser: string, + ) { + const address = getEthAddress(user); + const fabricID = getFabricId(finalUser); + + const assetProfile = { + expirationDate: new Date(2060, 11, 24).toString(), + issuer: "CB1", + assetCode: "CBDC1", + // since there is no link with the asset information, + // we are just passing the asset parameters like this + // [amountBeingTransferred, fabricID, ethAddress] + keyInformationLink: [amount.toString(), fabricID, address], + }; + + await axios + .post( + "http://localhost:4100/api/v1/@hyperledger/cactus-plugin-odap-hermes/clientrequest", + { + clientGatewayConfiguration: { + apiHost: `http://localhost:4100`, + }, + serverGatewayConfiguration: { + apiHost: `http://localhost:4000`, + }, + version: "0.0.0", + loggingProfile: "dummyLoggingProfile", + accessControlProfile: "dummyAccessControlProfile", + applicationProfile: "dummyApplicationProfile", + payloadProfile: { + assetProfile, + capabilities: "", + }, + assetProfile: assetProfile, + assetControlProfile: "dummyAssetControlProfile", + beneficiaryPubkey: "dummyPubKey", + clientDltSystem: "DLT2", + originatorPubkey: "dummyPubKey", + recipientGatewayDltSystem: "DLT1", + recipientGatewayPubkey: CryptoMaterial.gateways["gateway1"].publicKey, + serverDltSystem: "DLT1", + sourceGatewayDltSystem: "DLT2", + clientIdentityPubkey: "", + serverIdentityPubkey: "", + maxRetries: MAX_RETRIES, + maxTimeout: MAX_TIMEOUT, + sourceLedgerAssetID: assetRefID, + recipientLedgerAssetID: "FABRIC_ASSET_ID", + }, + ) + .catch((err) => { + expect(err.response.status).to.equal(500); + }); + }, +); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-out.steps.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-out.steps.ts new file mode 100644 index 0000000000..0f5c3f75c2 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/bridge-out.steps.ts @@ -0,0 +1,157 @@ +import { When, Then } from "cucumber"; +import { expect } from "chai"; +import axios from "axios"; +import CryptoMaterial from "../../../../crypto-material/crypto-material.json"; +import { getUserFromPseudonim, getFabricId, getEthAddress } from "./common"; + +const MAX_RETRIES = 5; +const MAX_TIMEOUT = 5000; + +const FABRIC_CHANNEL_NAME = "mychannel"; +const FABRIC_CONTRACT_ASSET_REF_NAME = "asset-reference-contract"; + +Then("the bridged out amount in the chaincode is {int} CBDC", async function ( + amount: string, +) { + const response = await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [], + methodName: "GetBridgedOutAmount", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim("bob"), + }, + }, + ); + + expect(parseInt(response.data.functionOutput)).to.equal(amount); +}); + +When( + "{string} initiates bridge out of {int} CBDC referenced by id {string} to {string} address in the sidechain", + { timeout: 60 * 1000 }, + async function ( + user: string, + amount: number, + assetRefID: string, + finalUser: string, + ) { + const fabricID = getFabricId(user); + const address = getEthAddress(finalUser); + + const assetProfile = { + expirationDate: new Date(2060, 11, 24).toString(), + issuer: "CB1", + assetCode: "CBDC1", + // since there is no link with the asset information, + // we are just passing the asset parameters like this + // [amountBeingTransferred, fabricID, ethAddress] + keyInformationLink: [amount.toString(), fabricID, address], + }; + + const response = await axios.post( + "http://localhost:4000/api/v1/@hyperledger/cactus-plugin-odap-hermes/clientrequest", + { + clientGatewayConfiguration: { + apiHost: `http://localhost:4000`, + }, + serverGatewayConfiguration: { + apiHost: `http://localhost:4100`, + }, + version: "0.0.0", + loggingProfile: "dummyLoggingProfile", + accessControlProfile: "dummyAccessControlProfile", + applicationProfile: "dummyApplicationProfile", + payloadProfile: { + assetProfile, + capabilities: "", + }, + assetProfile: assetProfile, + assetControlProfile: "dummyAssetControlProfile", + beneficiaryPubkey: "dummyPubKey", + clientDltSystem: "DLT1", + originatorPubkey: "dummyPubKey", + recipientGatewayDltSystem: "DLT2", + recipientGatewayPubkey: CryptoMaterial.gateways["gateway2"].publicKey, + serverDltSystem: "DLT2", + sourceGatewayDltSystem: "DLT1", + clientIdentityPubkey: "", + serverIdentityPubkey: "", + maxRetries: MAX_RETRIES, + maxTimeout: MAX_TIMEOUT, + sourceLedgerAssetID: assetRefID, + recipientLedgerAssetID: "FABRIC_ASSET_ID", + }, + ); + + expect(response.status).to.equal(200); + }, +); + +Then( + "{string} tries to initiate bridge out of {int} CBDC referenced by id {string} to {string} address in the sidechain and operation fails because {string}", + { timeout: 60 * 1000 }, + async function ( + user: string, + amount: number, + assetRefID: string, + finalUser: string, + failureReason: string, + ) { + const fabricID = getFabricId(user); + const address = getEthAddress(finalUser); + + const assetProfile = { + expirationDate: new Date(2060, 11, 24).toString(), + issuer: "CB1", + assetCode: "CBDC1", + // since there is no link with the asset information, + // we are just passing the asset parameters like this + // [amountBeingTransferred, fabricID, ethAddress] + keyInformationLink: [amount.toString(), fabricID, address], + }; + + await axios + .post( + "http://localhost:4000/api/v1/@hyperledger/cactus-plugin-odap-hermes/clientrequest", + { + clientGatewayConfiguration: { + apiHost: `http://localhost:4000`, + }, + serverGatewayConfiguration: { + apiHost: `http://localhost:4100`, + }, + version: "0.0.0", + loggingProfile: "dummyLoggingProfile", + accessControlProfile: "dummyAccessControlProfile", + applicationProfile: "dummyApplicationProfile", + payloadProfile: { + assetProfile, + capabilities: "", + }, + assetProfile: assetProfile, + assetControlProfile: "dummyAssetControlProfile", + beneficiaryPubkey: "dummyPubKey", + clientDltSystem: "DLT1", + originatorPubkey: "dummyPubKey", + recipientGatewayDltSystem: "DLT2", + recipientGatewayPubkey: CryptoMaterial.gateways["gateway2"].publicKey, + serverDltSystem: "DLT2", + sourceGatewayDltSystem: "DLT1", + clientIdentityPubkey: "", + serverIdentityPubkey: "", + maxRetries: MAX_RETRIES, + maxTimeout: MAX_TIMEOUT, + sourceLedgerAssetID: assetRefID, + recipientLedgerAssetID: "BESU_ASSET_ID", + }, + ) + .catch((err) => { + expect(err.response.data.error).to.contain(failureReason); + }); + }, +); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/common.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/common.ts new file mode 100644 index 0000000000..ff53bc0fce --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/common.ts @@ -0,0 +1,54 @@ +import { assert } from "chai"; +import CryptoMaterial from "../../../../crypto-material/crypto-material.json"; + +export function getUserFromPseudonim(user: string): string { + switch (user) { + case "alice": + return "userA"; + case "charlie": + return "userB"; + case "bob": + return "bridge"; + default: + assert.fail(0, 1, "Invalid user provided"); + } +} + +export function getFabricId(user: string): string { + switch (getUserFromPseudonim(user)) { + case "userA": + return CryptoMaterial.accounts["userA"].fabricID; + case "userB": + return CryptoMaterial.accounts["userB"].fabricID; + case "bridge": + return CryptoMaterial.accounts["bridge"].fabricID; + default: + assert.fail(0, 1, "Invalid user provided"); + } +} + +export function getEthAddress(user: string): string { + switch (getUserFromPseudonim(user)) { + case "userA": + return CryptoMaterial.accounts["userA"].ethAddress; + case "userB": + return CryptoMaterial.accounts["userB"].ethAddress; + case "bridge": + return CryptoMaterial.accounts["bridge"].ethAddress; + default: + assert.fail(0, 1, "Invalid user provided"); + } +} + +export function getPrvKey(user: string): string { + switch (getUserFromPseudonim(user)) { + case "userA": + return CryptoMaterial.accounts["userA"].privateKey; + case "userB": + return CryptoMaterial.accounts["userB"].privateKey; + case "bridge": + return CryptoMaterial.accounts["bridge"].privateKey; + default: + assert.fail(0, 1, "Invalid user provided"); + } +} diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/fabric-gateway.steps.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/fabric-gateway.steps.ts new file mode 100644 index 0000000000..e4e57cc0e8 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/cucumber/steps/fabric-gateway.steps.ts @@ -0,0 +1,177 @@ +import { Given, When, Then, Before, After } from "cucumber"; +import { expect } from "chai"; +import axios from "axios"; +import CryptoMaterial from "../../../../crypto-material/crypto-material.json"; +import { getEthAddress, getFabricId, getUserFromPseudonim } from "./common"; +import { + deleteFabricAssetReference, + fabricAssetReferenceExists, + getFabricBalance, + lockFabricAssetReference, + readFabricAssetReference, + resetFabric, + refundFabricTokens, +} from "../fabric-helper"; + +const FABRIC_CHANNEL_NAME = "mychannel"; +const FABRIC_CONTRACT_CBDC_ERC20_NAME = "cbdc"; +const FABRIC_CONTRACT_ASSET_REF_NAME = "asset-reference-contract"; + +Before({ timeout: 20 * 1000, tags: "@fabric" }, async function () { + await resetFabric(); +}); + +After({ timeout: 20 * 1000, tags: "@fabric" }, async function () { + await resetFabric(); +}); + +Given("{string} with {int} CBDC available in the source chain", async function ( + user: string, + amount: number, +) { + await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [amount.toString()], + methodName: "Mint", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim("alice"), + }, + }, + ); + + expect(await getFabricBalance(getFabricId(user))).to.equal(amount); +}); + +When( + "{string} escrows {int} CBDC and creates an asset reference with id {string} in the source chain", + async function (user: string, amount: number, assetRefID: string) { + await axios.post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [amount.toString(), assetRefID], + methodName: "Escrow", + invocationType: "FabricContractInvocationType.SEND", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim(user), + }, + }, + ); + }, +); + +When( + "{string} locks the asset reference with id {string} in the source chain", + async function (user: string, assetRefID: string) { + await lockFabricAssetReference(user, assetRefID); + }, +); + +When( + "{string} locks and deletes an asset reference with id {string} in the source chain", + { timeout: 10 * 1000 }, + async function (user: string, assetRefID: string) { + await lockFabricAssetReference(user, assetRefID); + await deleteFabricAssetReference(user, assetRefID); + }, +); + +When("bob refunds {int} CBDC to {string} in the source chain", async function ( + amount: number, + userTo: string, +) { + const finalUserFabricID = getFabricId(userTo); + const finalUserEthAddress = getEthAddress(userTo); + + await refundFabricTokens(finalUserFabricID, amount, finalUserEthAddress); +}); + +Then( + "{string} fails to lock the asset reference with id {string} in the source chain", + async function (user: string, assetRefID: string) { + return axios + .post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_ASSET_REF_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [assetRefID], + methodName: "LockAssetReference", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim(user), + }, + }, + ) + .catch((err) => { + expect(err.response.statusText).to.contain( + `client is not authorized to perform the operation`, + ); + }); + }, +); + +Then("{string} fails to transfer {int} CBDC to {string}", async function ( + userFrom: string, + amount: number, + userTo: string, +) { + const recipient = getFabricId(userTo); + + axios + .post( + "http://localhost:4000/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-fabric/run-transaction", + { + contractName: FABRIC_CONTRACT_CBDC_ERC20_NAME, + channelName: FABRIC_CHANNEL_NAME, + params: [recipient, amount.toString()], + methodName: "Transfer", + invocationType: "FabricContractInvocationType.CALL", + signingCredential: { + keychainId: CryptoMaterial.keychains.keychain1.id, + keychainRef: getUserFromPseudonim(userFrom), + }, + }, + ) + .catch((err) => { + expect(err.response.statusText).to.contain("has insufficient funds"); + }); +}); + +Then("{string} has {int} CBDC available in the source chain", async function ( + user: string, + amount: number, +) { + expect(await getFabricBalance(getFabricId(user))).to.equal(amount); +}); + +Then( + "the asset reference chaincode has an asset reference with id {string}", + async function (assetRefID: string) { + expect(await readFabricAssetReference(assetRefID)).to.not.be.undefined; + }, +); + +Then( + "the asset reference with id {string} is locked in the source chain", + async function (assetRefID: string) { + expect((await readFabricAssetReference(assetRefID)).isLocked).to.equal( + true, + ); + }, +); + +Then( + "the asset reference chaincode has no asset reference with id {string}", + async function (assetRefID: string) { + expect(await fabricAssetReferenceExists(assetRefID)).to.equal("false"); + }, +); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/integration/api-surface.test.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/integration/api-surface.test.ts new file mode 100644 index 0000000000..34aba3a0ae --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/integration/api-surface.test.ts @@ -0,0 +1,6 @@ +import * as apiSurface from "../../../main/typescript/public-api"; +import "jest-extended"; + +test("Library can be loaded", async () => { + expect(apiSurface).toBeTruthy(); +}); diff --git a/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/unit/api-surface.test.ts b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/unit/api-surface.test.ts new file mode 100644 index 0000000000..34aba3a0ae --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/src/test/typescript/unit/api-surface.test.ts @@ -0,0 +1,6 @@ +import * as apiSurface from "../../../main/typescript/public-api"; +import "jest-extended"; + +test("Library can be loaded", async () => { + expect(apiSurface).toBeTruthy(); +}); diff --git a/examples/cactus-example-cbdc-bridging-backend/supervisord.conf b/examples/cactus-example-cbdc-bridging-backend/supervisord.conf new file mode 100644 index 0000000000..8a836ce1b4 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/supervisord.conf @@ -0,0 +1,25 @@ +[supervisord] +logfile = /usr/src/app/cactus/log/supervisord.log +logfile_maxbytes = 50MB +logfile_backups=10 +loglevel = info + +[program:dockerd] +command=/usr/local/bin/dockerd +autostart=true +autorestart=true +stderr_logfile=/usr/src/app/cactus/log/dockerd.err.log +stdout_logfile=/usr/src/app/cactus/log/dockerd.out.log + +[program:cbdc-bridge-app-backend] +command=/run-cbdc-app.sh +autostart=true +autorestart=unexpected +exitcodes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 + +[inet_http_server] +port = 0.0.0.0:9001 \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging-backend/tsconfig.json b/examples/cactus-example-cbdc-bridging-backend/tsconfig.json new file mode 100644 index 0000000000..0dc5c643ee --- /dev/null +++ b/examples/cactus-example-cbdc-bridging-backend/tsconfig.json @@ -0,0 +1,52 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/lib/", + "declarationDir": "dist/lib", + "rootDir": "./src", + "tsBuildInfoFile": "../../.build-cache/cactus-example-cbdc-bridging-backend.tsbuildinfo", + }, + "references": [ + { + "path": "../../extensions/cactus-plugin-object-store-ipfs" + }, + { + "path": "../../packages/cactus-api-client/tsconfig.json" + }, + { + "path": "../../packages/cactus-cmd-api-server/tsconfig.json" + }, + { + "path": "../../packages/cactus-common/tsconfig.json" + }, + { + "path": "../../packages/cactus-core/tsconfig.json" + }, + { + "path": "../../packages/cactus-core-api/tsconfig.json" + }, + { + "path": "../../packages/cactus-plugin-keychain-memory/tsconfig.json" + }, + { + "path": "../../packages/cactus-plugin-ledger-connector-fabric/tsconfig.json" + }, + { + "path": "../../packages/cactus-plugin-ledger-connector-besu/tsconfig.json" + }, + { + "path": "../../packages/cactus-plugin-odap-hermes/tsconfig.json" + }, + { + "path": "../../packages/cactus-test-tooling/tsconfig.json" + } + ], + "include": [ + "./src", + "src/**/*.json" + ], + "exclude": [ + "./src/fabric-contracts" + ] +} \ No newline at end of file diff --git a/examples/cactus-example-cbdc-bridging/README.md b/examples/cactus-example-cbdc-bridging/README.md new file mode 100644 index 0000000000..7c6745d575 --- /dev/null +++ b/examples/cactus-example-cbdc-bridging/README.md @@ -0,0 +1,22 @@ +# Hyperledger Cactus Example - CBDC Bridging between Fabric and Besu + +## Running the backend + +> Make sure you have all the dependencies set up as explained in `BUILD.md` + +On the terminal, issue the following commands in the project root: + +1. `npm run configure` +2. `npm run start:example-cbdc-bridging-app` + +Wait for the output to show the message `CbdcBridgingApp running...` + +## Running the frontend + +The source code of the frontend is not yet available, however, we provide a Docker image. + +In a second terminal run: + +`docker run -p 2000:2000 aaugusto11/cactus-example-cbdc-bridging-frontend` + +Visit `localhost:2000` and interact with the application. Do not change the port in your local machine, otherwise, the api servers might reject the requests. \ No newline at end of file diff --git a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts index 9b7eed2152..7a83c3aad3 100644 --- a/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts +++ b/examples/cactus-example-supply-chain-backend/src/main/typescript/supply-chain-app.ts @@ -207,9 +207,13 @@ export class SupplyChainApp { const quorumAccount = await this.ledgers.quorum.createEthTestAccount(); await this.keychain.set(quorumAccount.address, quorumAccount.privateKey); - const enrollAdminOut = await this.ledgers.fabric.enrollAdmin(); + const enrollAdminOut = await this.ledgers.fabric.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await this.ledgers.fabric.enrollUser(adminWallet); + const [userIdentity] = await this.ledgers.fabric.enrollUser( + adminWallet, + "user", + "org1", + ); const fabricUserKeychainKey = "user2"; const fabricUserKeychainValue = JSON.stringify(userIdentity); await this.keychain.set(fabricUserKeychainKey, fabricUserKeychainValue); diff --git a/package.json b/package.json index 951f1a13c9..ce711141a7 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "start:api-server": "node ./packages/cactus-cmd-api-server/dist/lib/main/typescript/cmd/cactus-api.js --config-file=.config.json", "start:example-supply-chain": "yarn build:dev && cd ./examples/supply-chain-app/ && yarn --no-lockfile && yarn start", "start:example-carbon-accounting": "CONFIG_FILE=examples/cactus-example-carbon-accounting-backend/example-config.json node examples/cactus-example-carbon-accounting-backend/dist/lib/main/typescript/carbon-accounting-app-cli.js", + "start:example-cbdc-bridging-app": "node -r ts-node/register examples/cactus-example-cbdc-bridging-backend/dist/lib/main/typescript/cbdc-bridging-app-cli.js dotenv_config_path=examples/cactus-example-cbdc-bridging-backend/process.env", "purge-build-cache": "del-cli .build-cache/*", "clean": "npm run purge-build-cache && del-cli \"./{packages,examples,extensions}/cactus-*/{dist,.nyc_output,src/main/proto/generated/*,src/main/typescript/generated/proto/protoc-gen-ts/*,src/main/typescript/generated/openapi/typescript-axios/*,src/main-server/kotlin/gen/kotlin-spring/src/**/{model,api}/*}\"", "lint": "eslint '*/*/src/**/*.{js,ts}' --quiet --fix && cspell \"*/*/src/**/*.{js,ts}\"", diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/deploy-cc-from-golang-source.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/deploy-cc-from-golang-source.test.ts index ab0ba55160..ebc995fabc 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/deploy-cc-from-golang-source.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/deploy-cc-from-golang-source.test.ts @@ -77,9 +77,9 @@ test.skip(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/run-transaction-endpoint-v1.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/run-transaction-endpoint-v1.test.ts index bba7ebde5a..2fe872cc14 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/run-transaction-endpoint-v1.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v1-4-x/run-transaction-endpoint-v1.test.ts @@ -77,9 +77,9 @@ test.skip(testCase, async (t: Test) => { await ledger.start(); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const connectionProfile = await ledger.getConnectionProfileOrg1(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts index 53a21bab67..24044b9903 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source-private-data.test.ts @@ -98,9 +98,9 @@ describe(testCase, () => { expect(connectionProfile).toBeTruthy(); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source.test.ts index b78e92457a..7b165d21e3 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-golang-source.test.ts @@ -75,9 +75,9 @@ test(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-javascript-source.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-javascript-source.test.ts index 67a5c13590..872f942cf6 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-javascript-source.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-javascript-source.test.ts @@ -77,9 +77,9 @@ test.skip(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts index 2a7457109f..a82b490790 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-cc-from-typescript-source.test.ts @@ -75,9 +75,9 @@ test(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts index 20e9f16314..fac59b67f8 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/deploy-lock-asset.test.ts @@ -80,9 +80,9 @@ test(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user2", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/get-block.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/get-block.test.ts index e2668aa8fc..96a21347ee 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/get-block.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/get-block.test.ts @@ -102,9 +102,13 @@ describe("Get Block endpoint tests", () => { expect(connectionProfile).toBeTruthy(); // Enroll admin and user - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser( + adminWallet, + "user2", + "org1", + ); // Create Keychain Plugin const keychainId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-endpoint-v1.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-endpoint-v1.test.ts index 1bf94d54cb..f3cbdd773b 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-endpoint-v1.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-endpoint-v1.test.ts @@ -109,9 +109,9 @@ describe(testCase, () => { }); test(testCase, async () => { - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const connectionProfile = await ledger.getConnectionProfileOrg1(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/openapi/openapi-validation.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/openapi/openapi-validation.test.ts index 9ba9a0548b..c89d91b37d 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/openapi/openapi-validation.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/openapi/openapi-validation.test.ts @@ -70,9 +70,9 @@ test(testCase, async (t: Test) => { const connectionProfile = await ledger.getConnectionProfileOrg1(); t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const enrollAdminOut = await ledger.enrollAdmin(); + const enrollAdminOut = await ledger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await ledger.enrollUser(adminWallet); + const [userIdentity] = await ledger.enrollUser(adminWallet, "user", "org1"); const sshConfig = await ledger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/backup-gateway-after-client-crash.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/backup-gateway-after-client-crash.test.ts index 0be8d60cfd..cc900ae7d8 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/backup-gateway-after-client-crash.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/backup-gateway-after-client-crash.test.ts @@ -195,9 +195,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-delete-asset.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-delete-asset.test.ts index 6923119464..9ee7172cad 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-delete-asset.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-delete-asset.test.ts @@ -192,9 +192,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-lock-asset.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-lock-asset.test.ts index 1a339d9f8a..3717bf1f9b 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-lock-asset.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/client-crash-after-lock-asset.test.ts @@ -192,9 +192,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-api-call-with-ledger-connector.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-api-call-with-ledger-connector.test.ts index 41118d563f..683b6ec963 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-api-call-with-ledger-connector.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-api-call-with-ledger-connector.test.ts @@ -186,9 +186,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-rollback.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-rollback.test.ts index 9f7de17744..2a4ee56a6e 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-rollback.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/odap-rollback.test.ts @@ -189,9 +189,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/server-crash-after-create-asset.test.ts b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/server-crash-after-create-asset.test.ts index 58c03ce6fe..a5fdfd58c5 100644 --- a/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/server-crash-after-create-asset.test.ts +++ b/packages/cactus-plugin-odap-hermes/src/test/typescript/integration/server-crash-after-create-asset.test.ts @@ -190,9 +190,13 @@ beforeAll(async () => { const connectionProfile = await fabricLedger.getConnectionProfileOrg1(); expect(connectionProfile).not.toBeUndefined(); - const enrollAdminOut = await fabricLedger.enrollAdmin(); + const enrollAdminOut = await fabricLedger.enrollAdmin("org1"); const adminWallet = enrollAdminOut[1]; - const [userIdentity] = await fabricLedger.enrollUser(adminWallet); + const [userIdentity] = await fabricLedger.enrollUser( + adminWallet, + "user2", + "org1", + ); const sshConfig = await fabricLedger.getSshConfig(); const keychainInstanceId = uuidv4(); diff --git a/packages/cactus-test-tooling/src/main/typescript/besu/besu-test-ledger.ts b/packages/cactus-test-tooling/src/main/typescript/besu/besu-test-ledger.ts index bf9cb41e00..a064bcf9df 100644 --- a/packages/cactus-test-tooling/src/main/typescript/besu/besu-test-ledger.ts +++ b/packages/cactus-test-tooling/src/main/typescript/besu/besu-test-ledger.ts @@ -4,7 +4,7 @@ import Joi from "joi"; import tar from "tar-stream"; import { EventEmitter } from "events"; import Web3 from "web3"; -import { Account } from "web3-core"; +import { Account, TransactionReceipt } from "web3-core"; import { LogLevelDesc, Logger, @@ -164,16 +164,33 @@ export class BesuTestLedger implements ITestLedger { * @param [seedMoney=10e8] The amount of money to seed the new test account with. */ public async createEthTestAccount(seedMoney = 10e8): Promise { - const fnTag = `BesuTestLedger#getEthTestAccount()`; - const rpcApiHttpHost = await this.getRpcApiHttpHost(); const web3 = new Web3(rpcApiHttpHost); const ethTestAccount = web3.eth.accounts.create(uuidv4()); + this.sendEthToAccount(ethTestAccount.address, seedMoney); + + return ethTestAccount; + } + + /** + * Sends seed money to a provided address to get things started. + * + * @param [seedMoney=10e8] The amount of money to seed the new test account with. + */ + public async sendEthToAccount( + address: string, + seedMoney = 10e8, + ): Promise { + const fnTag = `BesuTestLedger#sendEthToAccount()`; + + const rpcApiHttpHost = await this.getRpcApiHttpHost(); + const web3 = new Web3(rpcApiHttpHost); + const tx = await web3.eth.accounts.signTransaction( { from: this.getGenesisAccountPubKey(), - to: ethTestAccount.address, + to: address, value: seedMoney, gas: 1000000, }, @@ -188,9 +205,8 @@ export class BesuTestLedger implements ITestLedger { if (receipt instanceof Error) { throw receipt; - } else { - return ethTestAccount; } + return receipt; } public async getBesuKeyPair(): Promise { diff --git a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts index e10ca92fe4..f6ba06db06 100644 --- a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts +++ b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts @@ -166,15 +166,16 @@ export class FabricTestLedgerV1 implements ITestLedger { return `${this.envVars.get("FABRIC_VERSION")}`; } - public getDefaultMspId(): string { - return "Org1MSP"; + public getDefaultMspId(organization: string): string { + return organization.charAt(0).toUpperCase() + organization.slice(1) + "MSP"; } - public async createCaClient(): Promise { + public async createCaClient(organization: string): Promise { const fnTag = `${this.className}#createCaClient()`; try { - const ccp = await this.getConnectionProfileOrg1(); - const caInfo = ccp.certificateAuthorities["ca.org1.example.com"]; + const ccp = await this.getConnectionProfileOrgX(organization); + const caInfo = + ccp.certificateAuthorities["ca." + organization + ".example.com"]; const { tlsCACerts, url: caUrl, caName } = caInfo; const { pem: caTLSCACertPem } = tlsCACerts; const tlsOptions = { trustedRoots: caTLSCACertPem, verify: false }; @@ -187,12 +188,17 @@ export class FabricTestLedgerV1 implements ITestLedger { } } - public async enrollUser(wallet: Wallet): Promise { + public async enrollUser( + wallet: Wallet, + enrollmentID: string, + organization: string, + ): Promise { const fnTag = `${this.className}#enrollUser()`; try { - const mspId = this.getDefaultMspId(); - const enrollmentID = "user"; - const connectionProfile = await this.getConnectionProfileOrg1(); + const mspId = this.getDefaultMspId(organization); + const connectionProfile = await this.getConnectionProfileOrgX( + organization, + ); // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); const discovery = { enabled: true, asLocalhost: true }; @@ -205,12 +211,12 @@ export class FabricTestLedgerV1 implements ITestLedger { // Get the CA client object from the gateway for interacting with the CA. // const ca = gateway.getClient().getCertificateAuthority(); - const ca = await this.createCaClient(); + const ca = await this.createCaClient(organization); const adminIdentity = gateway.getIdentity(); // Register the user, enroll the user, and import the new identity into the wallet. const registrationRequest = { - affiliation: "org1.department1", + affiliation: organization + ".department1", enrollmentID, role: "client", }; @@ -255,10 +261,12 @@ export class FabricTestLedgerV1 implements ITestLedger { return ["admin", "adminpw"]; } - public async enrollAdmin(): Promise<[X509Identity, Wallet]> { + public async enrollAdmin( + organization: string, + ): Promise<[X509Identity, Wallet]> { const fnTag = `${this.className}#enrollAdmin()`; try { - const ca = await this.createCaClient(); + const ca = await this.createCaClient(organization); const wallet = await Wallets.newInMemoryWallet(); // Enroll the admin user, and import the new identity into the wallet. @@ -268,7 +276,7 @@ export class FabricTestLedgerV1 implements ITestLedger { }; const enrollment = await ca.enroll(request); - const mspId = this.getDefaultMspId(); + const mspId = this.getDefaultMspId(organization); const { certificate, key } = enrollment; const keyBytes = key.toBytes(); diff --git a/tsconfig.json b/tsconfig.json index 0cc496a24a..172ea66d8f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -127,6 +127,9 @@ { "path": "./examples/cactus-example-discounted-asset-trade/tsconfig.json" }, + { + "path": "./examples/cactus-example-cbdc-bridging-backend/tsconfig.json" + }, { "path": "./extensions/cactus-plugin-object-store-ipfs/tsconfig.json" },