diff --git a/contracts/.env.example b/contracts/.env.example new file mode 100644 index 0000000..332d935 --- /dev/null +++ b/contracts/.env.example @@ -0,0 +1,3 @@ +DEPLOYER_ADDRESS= +DEPLOYER_PRIVATE_KEY= +RPC_ENDPOINT= \ No newline at end of file diff --git a/contracts/.gitignore b/contracts/.gitignore index eb5a316..a33c2fa 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -1 +1,3 @@ target +node_modules +.env \ No newline at end of file diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock index e6cb110..f5e345f 100644 --- a/contracts/Scarb.lock +++ b/contracts/Scarb.lock @@ -1,6 +1,137 @@ # Code generated by scarb DO NOT EDIT. version = 1 +[[package]] +name = "openzeppelin" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:7e77855aaba0825a2a12cad72d52d85380a9fab732007754b3c5d98908918ce7" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:541bb8fdf1ad17fe0d275b00acb9f0d7f56ea5534741e21535ac3fda2c600281" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:c4e11609fdd1f4c3d3004cd1468711bd2ea664739c9e59a4b270567fe4c23ee3" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:9adcbec76ee8ed08be8d87c6af6014aa7080d67578816f5ba77f4376b25bc165" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:b7e0142d88d69a8c367aea8c9dc7f659f27372551efc23f39a0cf71a189c1302" +dependencies = [ + "openzeppelin_access", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_introspection" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:892433a4a1ea0fc9cf7cdb01e06ddc2782182abcc188e4ea5dd480906d006cf8" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:3c338fa07cbaba8034051a42967816800abe535ef7d709a929175616603dccf9" + +[[package]] +name = "openzeppelin_presets" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:0a39e0effff133ab7fb003961ee2986438ee09b53608ce0d71aca24459879597" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", +] + +[[package]] +name = "openzeppelin_security" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:6e2dee39d87f9ddec2ad37e33e80cf0d8b6c6927fd7950f220dbc2baea658d43" + +[[package]] +name = "openzeppelin_token" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:77997a7e217b69674c34b402dc0c7b2210540db66a56087572679c31896eaabb" +dependencies = [ + "openzeppelin_account", + "openzeppelin_governance", + "openzeppelin_introspection", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a0fa5934f2924e1e85ec8f8c5b7dcd95c25295c029d3a745ba87b3191146004d" + +[[package]] +name = "openzeppelin_utils" +version = "0.17.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:36d93e353f42fd6b824abcd8b4b51c3f5d02c893c5f886ae81403b0368aa5fde" + +[[package]] +name = "snforge_scarb_plugin" +version = "0.35.1" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.35.1#089de2c7a391372a7aaa54ec2706b117f955d06a" + +[[package]] +name = "snforge_std" +version = "0.35.1" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.35.1#089de2c7a391372a7aaa54ec2706b117f955d06a" +dependencies = [ + "snforge_scarb_plugin", +] + [[package]] name = "win_saved" version = "0.1.0" +dependencies = [ + "openzeppelin", + "snforge_std", +] diff --git a/contracts/Scarb.toml b/contracts/Scarb.toml index 27d49c1..606aa8c 100644 --- a/contracts/Scarb.toml +++ b/contracts/Scarb.toml @@ -6,7 +6,8 @@ edition = "2024_07" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] -starknet = "2.8.2" +starknet = "2.8.5" +openzeppelin = "0.17.0" snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.35.1" } [[target.starknet-contract]] diff --git a/contracts/package-lock.json b/contracts/package-lock.json new file mode 100644 index 0000000..30327a4 --- /dev/null +++ b/contracts/package-lock.json @@ -0,0 +1,504 @@ +{ + "name": "winsaved", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "winsaved", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.4.7", + "starknet": "^6.11.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/starknet": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@scure/starknet/-/starknet-1.0.0.tgz", + "integrity": "sha512-o5J57zY0f+2IL/mq8+AYJJ4Xpc1fOtDhr+mFQKbHnYFmm3WQrC+8zj2HEgxak1a+x86mhmBC1Kq305KUpVf0wg==", + "dependencies": { + "@noble/curves": "~1.3.0", + "@noble/hashes": "~1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/starknet/node_modules/@noble/curves": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "dependencies": { + "@noble/hashes": "1.3.3" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/starknet/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@starknet-io/types-js": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.10.tgz", + "integrity": "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==" + }, + "node_modules/abi-wan-kanabi": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/abi-wan-kanabi/-/abi-wan-kanabi-2.2.4.tgz", + "integrity": "sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==", + "dependencies": { + "ansicolors": "^0.3.2", + "cardinal": "^2.1.1", + "fs-extra": "^10.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "generate": "dist/generate.js" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==" + }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fetch-cookie": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.1.0.tgz", + "integrity": "sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^5.0.0" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-starknet-core": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/get-starknet-core/-/get-starknet-core-4.0.0.tgz", + "integrity": "sha512-6pLmidQZkC3wZsrHY99grQHoGpuuXqkbSP65F8ov1/JsEI8DDLkhsAuLCKFzNOK56cJp+f1bWWfTJ57e9r5eqQ==", + "dependencies": { + "@starknet-io/types-js": "^0.7.7" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lossless-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lossless-json/-/lossless-json-4.0.2.tgz", + "integrity": "sha512-+z0EaLi2UcWi8MZRxA5iTb6m4Ys4E80uftGY+yG5KNFJb5EceQXOhdW/pWJZ8m97s26u7yZZAYMcKWNztSZssA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + }, + "node_modules/starknet": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/starknet/-/starknet-6.11.0.tgz", + "integrity": "sha512-u50KrGDi9fbu1Ogu7ynwF/tSeFlp3mzOg1/Y5x50tYFICImo3OfY4lOz9OtYDk404HK4eUujKkhov9tG7GAKlg==", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "^1.4.0", + "@scure/base": "~1.1.3", + "@scure/starknet": "~1.0.0", + "abi-wan-kanabi": "^2.2.2", + "fetch-cookie": "^3.0.0", + "get-starknet-core": "^4.0.0-next.3", + "isomorphic-fetch": "^3.0.0", + "lossless-json": "^4.0.1", + "pako": "^2.0.4", + "starknet-types-07": "npm:@starknet-io/types-js@^0.7.7", + "ts-mixer": "^6.0.3", + "url-join": "^4.0.1" + } + }, + "node_modules/starknet-types-07": { + "name": "@starknet-io/types-js", + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.10.tgz", + "integrity": "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tldts": { + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.71.tgz", + "integrity": "sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==", + "dependencies": { + "tldts-core": "^6.1.71" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.71.tgz", + "integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==" + }, + "node_modules/tough-cookie": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", + "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/contracts/package.json b/contracts/package.json new file mode 100644 index 0000000..335f89d --- /dev/null +++ b/contracts/package.json @@ -0,0 +1,17 @@ +{ + "name": "winsaved", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "deploy": "node scripts/deploy.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^16.4.7", + "starknet": "^6.11.0" + } +} diff --git a/contracts/scripts/deploy.js b/contracts/scripts/deploy.js new file mode 100644 index 0000000..37d2b17 --- /dev/null +++ b/contracts/scripts/deploy.js @@ -0,0 +1,57 @@ +import { Account, CallData, Contract, RpcProvider, stark } from "starknet"; +import * as dotenv from "dotenv"; +import { getCompiledCode } from "./reader.js"; +dotenv.config(); + +async function main() { + const provider = new RpcProvider({ + nodeUrl: process.env.RPC_ENDPOINT, + }); + + // initialize existing predeployed account 0 + console.log("ACCOUNT_ADDRESS=", process.env.DEPLOYER_ADDRESS); + const privateKey0 = process.env.DEPLOYER_PRIVATE_KEY ?? ""; + const accountAddress0 = process.env.DEPLOYER_ADDRESS ?? ""; + const account0 = new Account(provider, accountAddress0, privateKey0); + console.log("Account connected.\n"); + + // Declare & deploy contract + let sierraCode, casmCode; + + try { + ({ sierraCode, casmCode } = await getCompiledCode( + "win_saved.WinSaved" + )); + } catch (error) { + console.log("Failed to read contract files"); + console.log(error); + process.exit(1); + } + + const myCallData = new CallData(sierraCode.abi); + + const constructor = myCallData.compile("constructor", {}); + + const deployResponse = await account0.declareAndDeploy({ + contract: sierraCode, + casm: casmCode, + constructorCalldata: constructor, + salt: stark.randomAddress(), + }); + + // Connect the new contract instance : + const votingContract = new Contract( + sierraCode.abi, + deployResponse.deploy.contract_address, + provider + ); + console.log( + `✅ Contract has been deploy with the address: ${votingContract.address}` + ); +} +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/contracts/scripts/reader.js b/contracts/scripts/reader.js new file mode 100644 index 0000000..19210f1 --- /dev/null +++ b/contracts/scripts/reader.js @@ -0,0 +1,33 @@ +import { promises as fs } from "fs"; +import path from "path"; +import { fileURLToPath } from 'url'; // To convert URL to file path +import { dirname } from 'path'; // To extract the directory name + +// Get the current module's URL and convert it to a file path +const __filename = fileURLToPath(import.meta.url); + +// Get the directory name from the file path +const __dirname = dirname(__filename); + +export async function getCompiledCode(filename) { + const sierraFilePath = path.join( + path.dirname(__filename), + `../target/dev/${filename}.contract_class.json` + ); + const casmFilePath = path.join( + path.dirname(__filename), + `../target/dev/${filename}.compiled_contract_class.json` + ); + + const code = [sierraFilePath, casmFilePath].map(async (filePath) => { + const file = await fs.readFile(filePath); + return JSON.parse(file.toString("ascii")); + }); + + const [sierraCode, casmCode] = await Promise.all(code); + + return { + sierraCode, + casmCode, + }; +} \ No newline at end of file diff --git a/contracts/src/connectors/zklend.cairo b/contracts/src/connectors/zklend.cairo new file mode 100644 index 0000000..8af90a3 --- /dev/null +++ b/contracts/src/connectors/zklend.cairo @@ -0,0 +1,101 @@ +use starknet::ContractAddress; + +#[starknet::interface] +trait IZKLend { + fn deposit(ref self: TContractState, token: ContractAddress, amount: felt252); + fn withdraw(ref self: TContractState, token: ContractAddress, amount: felt252); + fn get_lending_accumulator(self: @TContractState, token: ContractAddress) -> felt252; + fn get_reserve_data(self: @TContractState, token: ContractAddress) -> MarketReserveData; +} +#[derive(Drop, Serde)] +struct MarketReserveData { + enabled: bool, + decimals: felt252, + z_token_address: ContractAddress, + interest_rate_model: ContractAddress, + collateral_factor: felt252, + borrow_factor: felt252, + reserve_factor: felt252, + last_update_timestamp: felt252, + lending_accumulator: felt252, + debt_accumulator: felt252, + current_lending_rate: felt252, + current_borrowing_rate: felt252, + raw_total_debt: felt252, + flash_loan_fee: felt252, + liquidation_bonus: felt252, + debt_limit: felt252, + deposit_limit: felt252, +} + +#[starknet::contract] +pub mod ZKLendConnector { + use starknet::{ContractAddress, ClassHash}; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::syscalls::library_call_syscall; + use starknet::SyscallResult; + use win_saved::interfaces::iyieldsource::{IYieldSource, YieldSourceData}; + use super::{IZKLendDispatcher, IZKLendDispatcherTrait}; + + #[storage] + struct Storage { + contract_address: ContractAddress, + contract_class_hash: ClassHash + } + + #[constructor] + fn constructor( + ref self: ContractState, contract_address: ContractAddress, class_hash: ClassHash + ) { + self.contract_address.write(contract_address); + self.contract_class_hash.write(class_hash); + } + + #[abi(embed_v0)] + impl ZKLendConnectorImpl of IYieldSource { + fn get_source_class_hash(self: @ContractState) -> ClassHash { + self.contract_class_hash.read() + } + fn get_source_contract_address(self: @ContractState) -> ContractAddress { + self.contract_address.read() + } + fn deposit(ref self: ContractState, token: ContractAddress, amount: felt252) { + let lib_address = self.contract_class_hash.read(); + // Make the library call + library_call_syscall( + lib_address, + selector!("deposit"), // Function selector + array![token.into(), amount].span() + ) + .unwrap(); + } + fn withdraw(ref self: ContractState, token: ContractAddress, amount: felt252) { + let lib_address = self.contract_class_hash.read(); + library_call_syscall( + lib_address, + selector!("withdraw"), // Function selector + array![token.into(), amount].span() + ) + .unwrap(); + } + fn withdraw_yield(ref self: ContractState, token: ContractAddress) {} + fn get_supply_pool_data(self: @ContractState, token: ContractAddress) -> YieldSourceData { + let izklend_dispatcher = IZKLendDispatcher { + contract_address: self.contract_address.read() + }; + let accumulator = izklend_dispatcher.get_lending_accumulator(token); + let data = izklend_dispatcher.get_reserve_data(token); + YieldSourceData { APY: accumulator, deposit_limit: data.deposit_limit } + } + fn get_yield_generated(self: @ContractState, token: ContractAddress) -> u128 { + 1000000 + } + fn get_total_value_locked(self: @ContractState, token: ContractAddress) -> u128 { + 100000 + } + fn get_supported_assets(self: @ContractState) -> Array { + let asset_array: Array = array![]; + asset_array + } + } +} diff --git a/contracts/src/interfaces/ierc20.cairo b/contracts/src/interfaces/ierc20.cairo new file mode 100644 index 0000000..64c9d9d --- /dev/null +++ b/contracts/src/interfaces/ierc20.cairo @@ -0,0 +1,19 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IERC20 { + fn name(self: @TContractState) -> ByteArray; + fn symbol(self: @TContractState) -> ByteArray; + fn decimals(self: @TContractState) -> u8; + + fn total_supply(self: @TContractState) -> u256; + fn balance_of(self: @TContractState, account: ContractAddress) -> u256; + fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; + fn approve(ref self: TContractState, spender: ContractAddress, amount: u256) -> bool; + fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + + fn mint(ref self: TContractState, recipient: ContractAddress, amount: u256) -> bool; +} diff --git a/contracts/src/interfaces/ivault.cairo b/contracts/src/interfaces/ivault.cairo new file mode 100644 index 0000000..0d6552f --- /dev/null +++ b/contracts/src/interfaces/ivault.cairo @@ -0,0 +1,24 @@ +use core::starknet::ContractAddress; +use core::starknet::ClassHash; +use win_saved::types::{YieldSourceData, VaultDetails}; + +#[derive(Drop, Serde)] +pub struct WinnerStruct { + pub address: ContractAddress, + pub date: u64, + pub amount: u256 +} + +#[starknet::interface] +pub trait IVault { + fn get_vault_details(self: @TContractState) -> VaultDetails; + fn deposit(ref self: TContractState, amount: u256); + fn withdraw(ref self: TContractState, amount: u256); + fn draw(ref self: TContractState, random_value: u32); + fn get_total_assets(self: @TContractState) -> u256; + fn get_yield_source_data(self: @TContractState) -> YieldSourceData; + fn get_recent_winners(self: @TContractState) -> Array; + fn pause(ref self: TContractState); + fn unpause(ref self: TContractState); + fn upgrade(ref self: TContractState, new_class_hash: ClassHash); +} diff --git a/contracts/src/interfaces/iwinsaved.cairo b/contracts/src/interfaces/iwinsaved.cairo new file mode 100644 index 0000000..6fbefac --- /dev/null +++ b/contracts/src/interfaces/iwinsaved.cairo @@ -0,0 +1,19 @@ +use starknet::{ContractAddress, ClassHash}; +#[starknet::interface] +pub trait IWinSaved { + fn create_vault( + ref self: TContractState, + yield_token: ContractAddress, + yield_source: ContractAddress, + draw_duration: u64 + ); + fn make_vault_draw(ref self: TContractState, vault: ContractAddress); + fn add_yield_token(ref self: TContractState, token: ContractAddress); + fn add_yield_source(ref self: TContractState, yield_source: ContractAddress, name: ByteArray); + fn get_vaults(self: @TContractState) -> Array; + fn get_yield_sources(self: @TContractState) -> Array; + fn get_yield_tokens(self: @TContractState) -> Array; + fn pause_vault(ref self: TContractState, vault: ContractAddress); + fn unpause_vault(ref self: TContractState, vault: ContractAddress); + fn upgrade_vault(ref self: TContractState, vault: ContractAddress, class_hash: ClassHash); +} diff --git a/contracts/src/interfaces/iyieldsource.cairo b/contracts/src/interfaces/iyieldsource.cairo new file mode 100644 index 0000000..ad72f04 --- /dev/null +++ b/contracts/src/interfaces/iyieldsource.cairo @@ -0,0 +1,20 @@ +use starknet::{ContractAddress, ClassHash}; + +#[derive(Copy, Drop, Serde)] +pub struct YieldSourceData { + pub APY: felt252, + pub deposit_limit: felt252, +} + +#[starknet::interface] +pub trait IYieldSource { + fn deposit(ref self: TContractState, token: ContractAddress, amount: felt252); + fn withdraw(ref self: TContractState, token: ContractAddress, amount: felt252); + fn withdraw_yield(ref self: TContractState, token: ContractAddress); + fn get_supply_pool_data(self: @TContractState, token: ContractAddress) -> YieldSourceData; + fn get_yield_generated(self: @TContractState, token: ContractAddress) -> u256; + fn get_total_value_locked(self: @TContractState, token: ContractAddress) -> u256; + fn get_supported_assets(self: @TContractState) -> Array; + fn get_source_class_hash(self: @TContractState) -> ClassHash; + fn get_source_contract_address(self: @TContractState) -> ContractAddress; +} diff --git a/contracts/src/lib.cairo b/contracts/src/lib.cairo index c55f19b..4a441a8 100644 --- a/contracts/src/lib.cairo +++ b/contracts/src/lib.cairo @@ -1,25 +1,12 @@ -fn main() -> u32 { - fib(16) -} +pub mod types; -fn fib(mut n: u32) -> u32 { - let mut a: u32 = 0; - let mut b: u32 = 1; - while n != 0 { - n = n - 1; - let temp = b; - b = a + b; - a = temp; - }; - a +pub mod interfaces { + pub mod ivault; + pub mod ierc20; + pub mod iyieldsource; + pub mod iwinsaved; } -#[cfg(test)] -mod tests { - use super::fib; +pub mod vault; +pub mod main; - #[test] - fn it_works() { - assert(fib(16) == 987, 'it works!'); - } -} diff --git a/contracts/src/main.cairo b/contracts/src/main.cairo new file mode 100644 index 0000000..bca97d2 --- /dev/null +++ b/contracts/src/main.cairo @@ -0,0 +1,232 @@ +#[starknet::contract] +pub mod WinSaved { + use starknet::{ContractAddress, ClassHash, get_caller_address, get_block_timestamp}; + use starknet::storage::{ + StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, MutableVecTrait + }; + use starknet::syscalls::{deploy_syscall}; + use win_saved::interfaces::{ + iwinsaved::IWinSaved, ivault::{IVaultDispatcher, IVaultDispatcherTrait} + }; + + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin_security::{PausableComponent, ReentrancyGuardComponent}; + use openzeppelin_upgrades::upgradeable::UpgradeableComponent; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: PausableComponent, storage: pausable, event: PausableEvent); + component!( + path: ReentrancyGuardComponent, storage: reentrancyguard, event: ReentrancyGuardEvent + ); + component!(path: UpgradeableComponent, storage: upgradable, event: UpgradableEvent); + + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl PausableImpl = PausableComponent::PausableImpl; + impl PausableInternalImpl = PausableComponent::InternalImpl; + + impl ReentrancyGuardInternalImpl = ReentrancyGuardComponent::InternalImpl; + + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + vaults: Vec, + tokens: Vec, + yield_sources: Vec, + vault_class_hash: ClassHash, + duration_range: (u64, u64), + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + pausable: PausableComponent::Storage, + #[substorage(v0)] + reentrancyguard: ReentrancyGuardComponent::Storage, + #[substorage(v0)] + upgradable: UpgradeableComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + VaultCreatedEvent: VaultCreatedEvent, + TokenAddedEvent: TokenAddedEvent, + YieldSourceEvent: YieldSourceEvent, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + PausableEvent: PausableComponent::Event, + #[flat] + ReentrancyGuardEvent: ReentrancyGuardComponent::Event, + #[flat] + UpgradableEvent: UpgradeableComponent::Event, + } + + #[derive(Drop, starknet::Event)] + struct VaultCreatedEvent { + address: ContractAddress + } + #[derive(Drop, starknet::Event)] + struct TokenAddedEvent { + address: ContractAddress + } + + #[derive(Drop, starknet::Event)] + struct YieldSourceEvent { + address: ContractAddress + } + + #[constructor] + fn constructor( + ref self: ContractState, + vault_class_hash: ClassHash, + min_draw_duration: u64, + max_draw_duration: u64 + ) { + let caller = get_caller_address(); + self.ownable.initializer(caller); + self.duration_range.write((min_draw_duration, max_draw_duration)); + } + + #[abi(embed_v0)] + impl WinSavedImpl of IWinSaved { + fn create_vault( + ref self: ContractState, + yield_token: ContractAddress, + yield_source: ContractAddress, + draw_duration: u64 + ) { + self.ownable.assert_only_owner(); + self.validate_yield_token(yield_token); + self.validate_yield_source(yield_source); + let constructor_calldata: Array = array![ + yield_token.into(), yield_source.into(), draw_duration.into() + ]; + let salt: felt252 = get_block_timestamp().into(); + let (contract_address, _) = deploy_syscall( + self.vault_class_hash.read(), salt, constructor_calldata.span(), false + ) + .unwrap(); + self.vaults.append().write(contract_address); + self.emit(VaultCreatedEvent { address: contract_address }); + } + fn make_vault_draw( + ref self: ContractState, vault: ContractAddress + ) { // generate random number with prima lib + } + fn add_yield_token(ref self: ContractState, token: ContractAddress) { + self.ownable.assert_only_owner(); + self.tokens.append().write(token); + self.emit(TokenAddedEvent { address: token }); + } + fn add_yield_source( + ref self: ContractState, yield_source: ContractAddress, name: ByteArray + ) { + self.ownable.assert_only_owner(); + self.yield_sources.append().write(yield_source); + self.emit(YieldSourceEvent { address: yield_source }); + } + fn get_yield_tokens(self: @ContractState) -> Array { + let mut tokens: Array = array![]; + for index in 0..self.tokens.len() { + tokens.append(self.tokens.at(index).read()); + }; + tokens + } + fn get_yield_sources(self: @ContractState) -> Array { + let mut sources: Array = array![]; + for index in 0 + ..self.yield_sources.len() { + sources.append(self.yield_sources.at(index).read()); + }; + sources + } + fn get_vaults(self: @ContractState) -> Array { + let mut vaults: Array = array![]; + for index in 0..self.vaults.len() { + vaults.append(self.vaults.at(index).read()); + }; + vaults + } + fn pause_vault(ref self: ContractState, vault: ContractAddress) { + self.ownable.assert_only_owner(); + self.validate_vault(vault); + let dispatcher = IVaultDispatcher { contract_address: vault }; + dispatcher.pause(); + } + fn unpause_vault(ref self: ContractState, vault: ContractAddress) { + self.ownable.assert_only_owner(); + self.validate_vault(vault); + let dispatcher = IVaultDispatcher { contract_address: vault }; + dispatcher.unpause(); + } + fn upgrade_vault(ref self: ContractState, vault: ContractAddress, class_hash: ClassHash) { + self.ownable.assert_only_owner(); + self.validate_vault(vault); + let dispatcher = IVaultDispatcher { contract_address: vault }; + dispatcher.upgrade(class_hash); + } + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn validate_yield_source(self: @ContractState, yield_source: ContractAddress) { + let mut checker = false; + for index in 0 + ..self + .yield_sources + .len() { + if yield_source == self.yield_sources.at(index).read() { + checker = true; + } + }; + + assert!(checker, "Invalid yield source"); + } + fn validate_yield_token(self: @ContractState, yield_token: ContractAddress) { + let mut checker = false; + for index in 0 + ..self + .tokens + .len() { + if yield_token == self.tokens.at(index).read() { + checker = true; + } + }; + + assert!(checker, "Invalid yield token"); + } + fn validate_vault(self: @ContractState, vault: ContractAddress) { + let mut checker = false; + for index in 0 + ..self + .vaults + .len() { + if vault == self.vaults.at(index).read() { + checker = true; + } + }; + + assert!(checker, "Invalid vault address"); + } + fn validate_draw_time(self: @ContractState, duration: u64) { + let (min_duration, max_duration) = self.duration_range.read(); + if duration < min_duration || duration > max_duration { + panic!("Duration out of bound"); + } + } + } +} diff --git a/contracts/src/types.cairo b/contracts/src/types.cairo new file mode 100644 index 0000000..75054e9 --- /dev/null +++ b/contracts/src/types.cairo @@ -0,0 +1,35 @@ +use starknet::ContractAddress; + +#[derive(Copy, Drop, Serde)] +pub struct YieldSourceData { + pub lending_accumulator: felt252, + pub deposit_limit: felt252, +} + +#[derive(Copy, Drop, Serde)] +pub struct UserBalanceStruct { + pub address: ContractAddress, + pub amount: u256, +} + +#[derive(Drop, Serde)] +pub struct TokenData { + pub symbol: ByteArray, + pub address: ContractAddress +} + +#[derive(Drop, Serde)] +pub struct VaultDetails { + pub yield_token: TokenData, + pub vault_token: TokenData, + pub APY: felt252, + pub owner: ContractAddress, + pub total_deposit: u256, + pub total_yield: u256 +} + +#[derive(Drop, Serde)] +pub struct YieldSourceDetails { + pub name: ByteArray, + pub contract_address: ContractAddress +} diff --git a/contracts/src/vault.cairo b/contracts/src/vault.cairo new file mode 100644 index 0000000..69f3253 --- /dev/null +++ b/contracts/src/vault.cairo @@ -0,0 +1,321 @@ +#[starknet::contract] +pub mod Vault { + use core::num::traits::Zero; + use core::starknet::{ + ContractAddress, get_caller_address, get_contract_address, get_block_timestamp, ClassHash + }; + use core::starknet::storage::{ + StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, MutableVecTrait + }; + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin_security::{PausableComponent, ReentrancyGuardComponent}; + use openzeppelin_upgrades::upgradeable::UpgradeableComponent; + use win_saved::interfaces::{ + ivault::{IVault, WinnerStruct}, ierc20::{IERC20Dispatcher, IERC20DispatcherTrait}, + iyieldsource::{IYieldSourceDispatcher, IYieldSourceDispatcherTrait} + }; + use win_saved::types::{YieldSourceData, UserBalanceStruct, VaultDetails, TokenData}; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: PausableComponent, storage: pausable, event: PausableEvent); + component!( + path: ReentrancyGuardComponent, storage: reentrancyguard, event: ReentrancyGuardEvent + ); + component!(path: UpgradeableComponent, storage: upgradable, event: UpgradableEvent); + + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl PausableImpl = PausableComponent::PausableImpl; + impl PausableInternalImpl = PausableComponent::InternalImpl; + + impl ReentrancyGuardInternalImpl = ReentrancyGuardComponent::InternalImpl; + + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + yield_token: ContractAddress, + yield_source: ContractAddress, + draw_duration: u64, + last_draw_time: u64, + winners: Vec, + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + pausable: PausableComponent::Storage, + #[substorage(v0)] + reentrancyguard: ReentrancyGuardComponent::Storage, + #[substorage(v0)] + upgradable: UpgradeableComponent::Storage, + } + + #[starknet::storage_node] + struct Winner { + address: ContractAddress, + amount: u256, + date: u64, + claimed: bool, + } + + #[event] + #[derive(Drop, starknet::Event)] + pub enum Event { + UserDepositedEvent: UserDepositedEvent, + UserWithdrawalEvent: UserWithdrawalEvent, + PrizeDrawEvent: PrizeDrawEvent, + UserWinEvent: UserWinEvent, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + PausableEvent: PausableComponent::Event, + #[flat] + ReentrancyGuardEvent: ReentrancyGuardComponent::Event, + #[flat] + UpgradableEvent: UpgradeableComponent::Event, + } + + #[derive(Drop, starknet::Event)] + struct UserDepositedEvent { + #[key] + user: ContractAddress, + amount: u256, + date: u64, + } + #[derive(Drop, starknet::Event)] + struct UserWithdrawalEvent { + #[key] + user: ContractAddress, + amount: u256, + date: u64, + } + #[derive(Drop, starknet::Event)] + struct PrizeDrawEvent { + date: u64, + } + #[derive(Drop, starknet::Event)] + struct UserWinEvent { + #[key] + user: ContractAddress, + amount: u256, + date: u64, + } + + #[constructor] + fn constructor( + ref self: ContractState, + yield_token: ContractAddress, + yield_source: ContractAddress, + draw_duration: u64 + ) { + let owner = get_caller_address(); + self.ownable.initializer(owner); + // get yield token name for creater vault token (share) name + let erc20_dispatcher = IERC20Dispatcher { contract_address: yield_token }; + let token_name = format!("WinSaved_{}", erc20_dispatcher.name()); + let token_symbol = format!("ws{}", erc20_dispatcher.symbol()); + self.yield_token.write(yield_token); + self.yield_source.write(yield_source); + self.draw_duration.write(draw_duration); + self.erc20.initializer(token_name, token_symbol); + } + + #[abi(embed_v0)] + impl VaultImpl of IVault { + fn get_vault_details(self: @ContractState) -> VaultDetails { + let yield_token_address = self.yield_token.read(); + let yield_erc20_dispatcher = IERC20Dispatcher { contract_address: yield_token_address }; + let yield_source_dispatcher = IYieldSourceDispatcher { + contract_address: self.yield_source.read() + }; + let yield_source_data = yield_source_dispatcher + .get_supply_pool_data(yield_token_address); + let TVL = yield_source_dispatcher.get_total_value_locked(yield_token_address); + let total_yield = yield_source_dispatcher.get_yield_generated(yield_token_address); + VaultDetails { + yield_token: TokenData { + symbol: yield_erc20_dispatcher.symbol(), address: yield_token_address + }, + vault_token: TokenData { + symbol: self.erc20.symbol(), address: get_contract_address() + }, + APY: yield_source_data.APY, + owner: self.ownable.owner(), + total_deposit: TVL, + total_yield: total_yield + } + } + fn deposit(ref self: ContractState, amount: u256) { + // check paused + self.pausable.assert_not_paused(); + self.reentrancyguard.start(); + let caller = get_caller_address(); + let contract_address = get_contract_address(); + //transfer token from user + let erc20_address = self.yield_token.read(); + let erc20_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + erc20_dispatcher.transfer_from(caller, contract_address, amount); + // make call to yield source to deposit + let yield_source_dispatcher = IYieldSourceDispatcher { + contract_address: self.yield_source.read() + }; + yield_source_dispatcher.withdraw(erc20_address, amount.try_into().unwrap()); + // mint token + self.erc20.mint(caller, amount); + let current_date_time = get_block_timestamp(); + self.emit(UserDepositedEvent { user: caller, amount: amount, date: current_date_time }); + self.reentrancyguard.end(); + } + fn pause(ref self: ContractState) { + self.ownable.assert_only_owner(); + self.pausable.pause(); + } + fn unpause(ref self: ContractState) { + self.ownable.assert_only_owner(); + self.pausable.unpause(); + } + fn withdraw(ref self: ContractState, amount: u256) { + self.pausable.assert_not_paused(); + self.reentrancyguard.start(); + let caller = get_caller_address(); + // get user vault token balance and assert if balance is equal to amount + let user_balance = self.erc20.balance_of(caller); + assert!(user_balance >= amount, "Insufficient balance"); + // make call to yield source to withdraw amount + let erc20_address = self.yield_token.read(); + let yield_source_dispatcher = IYieldSourceDispatcher { + contract_address: self.yield_source.read() + }; + yield_source_dispatcher.withdraw(erc20_address, amount.try_into().unwrap()); + //transfer token to user + let erc20_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // burn token + self.erc20.burn(caller, amount); + erc20_dispatcher.transfer(caller, amount); + + let current_date_time = get_block_timestamp(); + self + .emit( + UserWithdrawalEvent { user: caller, amount: amount, date: current_date_time } + ); + self.reentrancyguard.end(); + } + fn draw(ref self: ContractState, random_value: u32) { + self.ownable.assert_only_owner(); + // check whether last draw time is greater than draw duration + self.assert_vault_draw_availabiliity(); + + // check whether yield is available + + // get_all users that deposited to this vault + let users = self.get_all_users(); + let sorted_holders = self.sort_users_by_balance(users); + // get the top half for randomization + let top_average: u32 = sorted_holders.len() / 2; + let mut top_average_holders: Array = array![]; + for idx in 0..top_average { + top_average_holders.append(*sorted_holders.at(idx)); + }; + + let rand_num = random_value % top_average_holders.len(); + let winner = *top_average_holders.at(rand_num.into()); + let win_amount = self.disperse_prize_to_winner(winner); + self.emit(PrizeDrawEvent { date: get_block_timestamp() }); + self + .emit( + UserWinEvent { user: winner, amount: win_amount, date: get_block_timestamp() } + ); + } + fn get_total_assets(self: @ContractState) -> u256 { + // access total assets form yield source + let yield_source_dispatcher = IYieldSourceDispatcher { + contract_address: self.yield_source.read() + }; + yield_source_dispatcher.get_total_value_locked(get_contract_address()) + } + fn get_yield_source_data(self: @ContractState) -> YieldSourceData { + // access yield source connector interface + // change later when connector is created + YieldSourceData { lending_accumulator: 10, deposit_limit: 10, } + } + fn get_recent_winners(self: @ContractState) -> Array { + let mut winners_array: Array = array![]; + for index in 0 + ..self + .winners + .len() { + let current = self.winners.at(index); + winners_array + .append( + WinnerStruct { + address: current.address.read(), + amount: current.amount.read(), + date: current.date.read() + } + ); + }; + winners_array + } + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.ownable.assert_only_owner(); + assert!(new_class_hash.is_non_zero(), "Zero ClassHash detected.. aborting"); + self.upgradable.upgrade(new_class_hash); + } + } + + #[generate_trait] + impl InternalImpl of InternalImplTrait { + fn get_all_users(self: @ContractState) -> Array { + // query event for all users that has deposited and check balance of vault token + // return users with balance greater than zero + let all_holders: Array = array![]; + all_holders + } + /// Sort users by vault token balance + fn sort_users_by_balance( + self: @ContractState, users: Array + ) -> Array { + let sorted_holders: Array = array![]; + sorted_holders + } + fn disperse_prize_to_winner(ref self: ContractState, user: ContractAddress) -> u256 { + // functionality to disperse yield to winner + // liquidate yield from yield source to contract and transfer to winner + let yield_amount: u256 = + 1000; // access yield source after liquidation for the exact amount + // transfer yield amount to winner via dispatcher + + // add winner to winner struct + let index = self.winners.len(); + let position = self.winners.at(index); + position.address.write(user); + position.amount.write(yield_amount); + position.date.write(get_block_timestamp()); + position.claimed.write(true); + + let erc20_address = self.yield_token.read(); + let erc20_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + erc20_dispatcher.transfer(user, yield_amount); + + yield_amount + } + fn assert_vault_draw_availabiliity(self: @ContractState) { + let current_time = get_block_timestamp(); + let draw_diff = current_time - self.last_draw_time.read(); + assert!(draw_diff > self.draw_duration.read(), "Cannot make prize draw"); + } + } +} + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 78720a2..f6ce77d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.20", "eslint": "^9.18.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -28,11 +29,26 @@ "eslint-plugin-react-refresh": "^0.4.16", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^15.14.0", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -886,6 +902,24 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -966,6 +1000,17 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1563,6 +1608,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1577,6 +1635,34 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1746,6 +1832,44 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1784,6 +1908,19 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1891,6 +2028,16 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001692", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", @@ -1926,6 +2073,44 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1942,6 +2127,16 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1966,6 +2161,19 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -2084,6 +2292,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2110,6 +2332,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.80", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", @@ -2830,6 +3059,37 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2945,6 +3205,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2956,6 +3237,32 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "15.14.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", @@ -3182,6 +3489,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", @@ -3281,6 +3601,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", @@ -3510,6 +3840,32 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3618,6 +3974,26 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3706,11 +4082,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -3740,6 +4138,26 @@ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3749,6 +4167,16 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -3915,6 +4343,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3948,6 +4383,30 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3965,19 +4424,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -3993,8 +4472,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4002,6 +4482,148 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4112,6 +4734,29 @@ "node": ">=0.10.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4454,6 +5099,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4463,6 +5121,70 @@ "node": ">=0.10.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -4570,6 +5292,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -4590,6 +5352,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4629,6 +5414,88 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4651,6 +5518,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -4856,6 +5730,13 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", @@ -5033,12 +5914,120 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0e9858e..2fe235f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.20", "eslint": "^9.18.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.2", @@ -31,6 +32,8 @@ "eslint-plugin-react-refresh": "^0.4.16", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^15.14.0", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5" diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/public/Images/arb.png b/frontend/public/Images/arb.png new file mode 100644 index 0000000..8589f20 Binary files /dev/null and b/frontend/public/Images/arb.png differ diff --git a/frontend/public/Images/base.png b/frontend/public/Images/base.png new file mode 100644 index 0000000..c3bba0d Binary files /dev/null and b/frontend/public/Images/base.png differ diff --git a/frontend/public/Images/coinsilver.png b/frontend/public/Images/coinsilver.png new file mode 100644 index 0000000..cbe00a8 Binary files /dev/null and b/frontend/public/Images/coinsilver.png differ diff --git a/frontend/public/Images/diamondblue.png b/frontend/public/Images/diamondblue.png new file mode 100644 index 0000000..a50fdd4 Binary files /dev/null and b/frontend/public/Images/diamondblue.png differ diff --git a/frontend/public/Images/eth.png b/frontend/public/Images/eth.png new file mode 100644 index 0000000..8a4d701 Binary files /dev/null and b/frontend/public/Images/eth.png differ diff --git a/frontend/public/Images/gemstoneblue.png b/frontend/public/Images/gemstoneblue.png new file mode 100644 index 0000000..54bf87a Binary files /dev/null and b/frontend/public/Images/gemstoneblue.png differ diff --git a/frontend/public/Images/goldcoin.png b/frontend/public/Images/goldcoin.png new file mode 100644 index 0000000..357b49a Binary files /dev/null and b/frontend/public/Images/goldcoin.png differ diff --git a/frontend/public/Images/image.png b/frontend/public/Images/image.png new file mode 100644 index 0000000..3f81063 Binary files /dev/null and b/frontend/public/Images/image.png differ diff --git a/frontend/public/Images/mail.png b/frontend/public/Images/mail.png new file mode 100644 index 0000000..958b931 Binary files /dev/null and b/frontend/public/Images/mail.png differ diff --git a/frontend/public/Images/percentage.png b/frontend/public/Images/percentage.png new file mode 100644 index 0000000..42d52d8 Binary files /dev/null and b/frontend/public/Images/percentage.png differ diff --git a/frontend/public/Images/safe.png b/frontend/public/Images/safe.png new file mode 100644 index 0000000..a59f4de Binary files /dev/null and b/frontend/public/Images/safe.png differ diff --git a/frontend/public/Images/shield.png b/frontend/public/Images/shield.png new file mode 100644 index 0000000..ce7c184 Binary files /dev/null and b/frontend/public/Images/shield.png differ diff --git a/frontend/public/Images/starknet.png b/frontend/public/Images/starknet.png new file mode 100644 index 0000000..cf32580 Binary files /dev/null and b/frontend/public/Images/starknet.png differ diff --git a/frontend/public/Images/usdc.png b/frontend/public/Images/usdc.png new file mode 100644 index 0000000..9f7c32b Binary files /dev/null and b/frontend/public/Images/usdc.png differ diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8b5b3a7..396d1e7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,12 +1,33 @@ -import './App.css' +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom' +import Navbar from './components/Nav' +import Heropage from './components/Hero' +import Metrics from './components/Metrics' +import Content from './components/Content' +import Footer from './components/Footer' +import Dapp from './DApp/page' function App() { return ( - <> -
-

WinSaved

-
- + + + + + + +
+ +
+